func (p *Pool) put(pc *PoolConn) {
	p.addToFreeChan(pc)
	p.usedConnections--
}
put puts given PoolConn back to the pool
package clickhouse
import (
	"context"
	"errors"
	"fmt"
	"sync"
	"time"
	"github.com/hashicorp/go-multierror"
	"github.com/romberli/log"
	"github.com/romberli/go-util/constant"
	"github.com/romberli/go-util/middleware"
)
const (
	DefaultMaxConnections     = 20
	DefaultInitConnections    = 5
	DefaultMaxIdleConnections = 10
	DefaultMaxIdleTime        = 1800  
	DefaultKeepAliveInterval  = 300   
	DefaultKeepAliveChunkSize = 5
	DefaultSleepTime          = 1  
)
var _ middleware.PoolConn = (*PoolConn)(nil)
var _ middleware.Pool = (*Pool)(nil)
type PoolConfig struct {
	Config
	MaxConnections     int
	InitConnections    int
	MaxIdleConnections int
	MaxIdleTime        int
	KeepAliveInterval  int
}
func NewPoolConfig(addr, dbName, dbUser, dbPass string, debug bool, readTimeout, writeTimeout int,
	maxConnections, initConnections, maxIdleConnections, maxIdleTime, keepAliveInterval int, altHosts ...string) PoolConfig {
	config := NewConfig(addr, dbName, dbUser, dbPass, debug, readTimeout, writeTimeout, altHosts...)
	return PoolConfig{
		Config:             config,
		MaxConnections:     maxConnections,
		InitConnections:    initConnections,
		MaxIdleConnections: maxIdleConnections,
		MaxIdleTime:        maxIdleTime,
		KeepAliveInterval:  keepAliveInterval,
	}
}
func NewPoolConfigWithConfig(config Config, maxConnections, initConnections, maxIdleConnections,
	maxIdleTime, keepAliveInterval int) PoolConfig {
	return PoolConfig{
		Config:             config,
		MaxConnections:     maxConnections,
		InitConnections:    initConnections,
		MaxIdleConnections: maxIdleConnections,
		MaxIdleTime:        maxIdleTime,
		KeepAliveInterval:  keepAliveInterval,
	}
}
func (cfg *PoolConfig) Validate() (bool, error) {
	if cfg.MaxConnections <= constant.ZeroInt {
		return false, errors.New("maximum connection argument should larger than 0")
	}
	if cfg.InitConnections < constant.ZeroInt {
		return false, errors.New("init connection argument should not be smaller than 0")
	}
	if cfg.MaxIdleConnections < constant.ZeroInt {
		return false, errors.New("maximum idle connection argument should not be smaller than 0")
	}
	if cfg.MaxIdleConnections > cfg.MaxConnections {
		return false, errors.New("maximum idle connection argument should not be larger than maximum connection argument")
	}
	if cfg.MaxIdleTime <= constant.ZeroInt {
		return false, errors.New("maximum idle time argument should be larger than 0")
	}
	if cfg.KeepAliveInterval <= constant.ZeroInt {
		return false, errors.New("keep alive interval argument should be larger than 0")
	}
	return true, nil
}
type PoolConn struct {
	*Conn
	Pool *Pool
}
func NewPoolConn(addr, dbName, dbUser, dbPass string, debug bool, readTimeout, writeTimeout int, alterHosts ...string) (*PoolConn, error) {
	conn, err := NewConn(addr, dbName, dbUser, dbPass, debug, readTimeout, writeTimeout, alterHosts...)
	if err != nil {
		return nil, err
	}
	return &PoolConn{
		Conn: conn,
		Pool: nil,
	}, nil
}
func NewPoolConnWithPool(pool *Pool, addr, dbName, dbUser, dbPass string, debug bool, readTimeout, writeTimeout int, alterHosts ...string) (*PoolConn, error) {
	pc, err := NewPoolConn(addr, dbName, dbUser, dbPass, debug, readTimeout, writeTimeout, alterHosts...)
	if err != nil {
		return nil, err
	}
	if pc.IsValid() {
		pc.Pool = pool
		return pc, nil
	}
	_ = pc.Disconnect()
	return nil, errors.New("new created connection is not valid")
}
func (pc *PoolConn) Close() error {
	if pc.Pool.isClosed == true || pc.Pool == nil {
		return pc.Disconnect()
	}
	pc.Pool.Lock()
	defer pc.Pool.Unlock()
	pc.Pool.put(pc)
	return nil
}
func (pc *PoolConn) Disconnect() error {
	pc.Pool = nil
	return pc.Conn.Close()
}
func (pc *PoolConn) IsValid() bool {
	return pc.CheckInstanceStatus()
}
func (pc *PoolConn) Prepare(command string) (middleware.Statement, error) {
	return pc.Conn.prepareContext(context.Background(), command)
}
func (pc *PoolConn) PrepareContext(ctx context.Context, command string) (middleware.Statement, error) {
	return pc.Conn.prepareContext(ctx, command)
}
func (pc *PoolConn) Execute(command string, args ...interface{}) (middleware.Result, error) {
	return pc.executeContext(context.Background(), command, args...)
}
func (pc *PoolConn) ExecuteContext(ctx context.Context, command string, args ...interface{}) (middleware.Result, error) {
	return pc.executeContext(ctx, command, args...)
}
func (pc *PoolConn) executeContext(ctx context.Context, command string, args ...interface{}) (middleware.Result, error) {
	result, err := pc.Conn.executeContext(ctx, command, args...)
	if err != nil {
		return nil, err
	}
	return result, err
}
type Pool struct {
	sync.Mutex
	PoolConfig
	freeConnChan    chan *PoolConn
	usedConnections int
	expireTime      time.Time
	keepAliveTime   time.Time
	isClosed        bool
}
func NewPool(addr, dbName, dbUser, dbPass string, debug bool, readTimeout, writeTimeout int,
	maxConnections, initConnections, maxIdleConnections, maxIdleTime, keepAliveInterval int, altHosts ...string) (*Pool, error) {
	cfg := NewPoolConfig(addr, dbName, dbUser, dbPass, debug, readTimeout, writeTimeout,
		maxConnections, initConnections, maxIdleConnections, maxIdleTime, keepAliveInterval, altHosts...)
	return NewPoolWithPoolConfig(cfg)
}
func NewPoolWithDefault(addr, dbName, dbUser, dbPass string) (*Pool, error) {
	return NewPool(addr, dbName, dbUser, dbPass, false, DefaultReadTimeout, DefaultWriteTimeout,
		DefaultMaxConnections, DefaultInitConnections, DefaultMaxIdleConnections, DefaultMaxIdleTime, DefaultKeepAliveInterval)
}
func NewPoolWithConfig(config Config, maxConnections, initConnections, maxIdleConnections, maxIdleTime, keepAliveInterval int) (*Pool, error) {
	cfg := NewPoolConfigWithConfig(config, maxConnections, initConnections, maxIdleConnections, maxIdleTime, keepAliveInterval)
	return NewPoolWithPoolConfig(cfg)
}
func NewPoolWithPoolConfig(config PoolConfig) (*Pool, error) {
	p := &Pool{
		PoolConfig:      config,
		freeConnChan:    make(chan *PoolConn, config.MaxConnections),
		usedConnections: constant.ZeroInt,
		expireTime:      time.Now().Add(time.Duration(config.MaxIdleTime) * time.Second),
		keepAliveTime:   time.Now().Add(time.Duration(config.KeepAliveInterval) * time.Second),
		isClosed:        false,
	}
	err := p.init()
	if err != nil {
		return nil, err
	}
	return p, nil
}
func (p *Pool) init() error {
	err := p.supply(p.InitConnections)
	if err != nil {
		return err
	}
	go p.maintainFreeChan()
	return nil
}
func (p *Pool) UsedConnections() int {
	return p.usedConnections
}
func (p *Pool) IsClosed() bool {
	return p.isClosed
}
func (p *Pool) Supply(num int) error {
	p.Lock()
	defer p.Unlock()
	return p.supply(num)
}
func (p *Pool) supply(num int) error {
	if p.isClosed {
		return nil
	}
	merr := &multierror.Error{}
	for i := 0; i < num; i++ {
		if len(p.freeConnChan)+p.usedConnections < p.MaxConnections {
			pc, err := NewPoolConnWithPool(p, p.Addr, p.DBName, p.DBUser, p.DBPass, p.Debug, p.ReadTimeout, p.WriteTimeout, p.AltHosts...)
			if err != nil {
				merr = multierror.Append(merr, err)
				continue
			}
			p.addToFreeChan(pc)
		}
	}
	return merr.ErrorOrNil()
}
func (p *Pool) Close() error {
	p.Lock()
	defer p.Unlock()
	p.isClosed = true
	return p.release(len(p.freeConnChan))
}