func (db *DB) SetConnMaxLifetime(d time.Duration) {
	db.maxLifetime = d
	walk(db.cpool, func(conn *sql.DB) error {
		conn.SetConnMaxLifetime(d)
		return nil
	})
}
SetConnMaxLifetime sets the maximum amount of time a connection may be reused
for all connections. This is concurrency safe.
package rwdb
import (
	"context"
	"database/sql"
	"database/sql/driver"
	"errors"
	"time"
)
type DB struct {
	cpool           *CPool
	sticky          bool  
	modified        bool
	maxIdle         int
	maxOpen         int
	maxLifetime     time.Duration
	maxIdleModified bool
}
func walk(cpool *CPool, fn func(conn *sql.DB) error) error {
	errors := make(chan error, 1)
	cpool.lock.Lock()
	defer cpool.lock.Unlock()
	for _, conn := range cpool.pool {
		if conn == nil {
			continue
		}
		go func(conn *sql.DB) {
			errors <- fn(conn)
		}(conn)
	}
	if err := <-errors; err != nil {
		return err
	}
	return nil
}
func Open(driver string, dataSourceNames ...string) (*DB, error) {
	var db = &DB{cpool: &CPool{}}
	db.SetSticky(true)
	if len(dataSourceNames) == 0 {
		return nil, errors.New("no data source name available")
	}
	d, err := sql.Open(driver, dataSourceNames[0])
	if err != nil {
		return nil, err
	}
	db.cpool.AddWriter(d)
	for _, conn := range dataSourceNames[1:] {
		go func(conn string) {
			d, _ := sql.Open(driver, conn)
			if db.maxIdleModified {
				d.SetMaxIdleConns(db.maxIdle)
			}
			d.SetConnMaxLifetime(db.maxLifetime)
			d.SetMaxOpenConns(db.maxOpen)
			db.cpool.AddReader(d)
		}(conn)
	}
	return db, nil
}
func (db *DB) next() (*sql.DB, error) {
	if db.sticky && db.modified {
		return db.cpool.Writer()
	}
	return db.cpool.Reader()
}
func (db *DB) SetSticky(stick bool) {
	db.sticky = stick
}
func (db *DB) New() *DB {
	return &DB{
		cpool:  db.cpool,
		sticky: db.sticky,
	}
}
func (db *DB) Driver() driver.Driver {
	writer, _ := db.cpool.Writer()
	return writer.Driver()
}
func (db *DB) Begin() (*sql.Tx, error) {
	writer, err := db.cpool.Writer()
	if err != nil {
		return nil, err
	}
	return writer.Begin()
}
func (db *DB) Exec(query string, args ...interface{}) (sql.Result, error) {
	return db.ExecContext(context.Background(), query, args...)
}
func (db *DB) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) {
	writer, err := db.cpool.Writer()
	if err != nil {
		return nil, err
	}
	result, err := writer.ExecContext(ctx, query, args...)
	if err == nil {
		if rowAffected, _ := result.RowsAffected(); rowAffected > 0 {
			db.modified = true
		}
	}
	return result, err
}
func (db *DB) Ping() error {
	return db.PingContext(context.Background())
}
func (db *DB) PingContext(ctx context.Context) error {
	return walk(db.cpool, func(conn *sql.DB) error {
		return conn.PingContext(ctx)
	})
}
func (db *DB) Prepare(query string) (Stmt, error) {
	return db.PrepareContext(context.Background(), query)
}
func (db *DB) PrepareContext(ctx context.Context, query string) (Stmt, error) {
	stmt := &stmt{}
	writer, err := db.cpool.Writer()
	if err != nil {
		return nil, err
	}
	write, err := writer.Prepare(query)
	if err != nil {
		return nil, err
	}
	stmt.stmts = []*sql.Stmt{write}
	if db.cpool.poolSize() > 1 {
		reader, err := db.cpool.Reader()
		if err == nil {
			go func(reader *sql.DB) {
				read, _ := reader.PrepareContext(ctx, query)
				stmt.lock.Lock()
				defer stmt.lock.Unlock()
				stmt.stmts = append(stmt.stmts, read)
			}(reader)
		}
	}
	return stmt, nil
}
func (db *DB) Close() error {
	return walk(db.cpool, func(conn *sql.DB) error {
		return conn.Close()
	})
}
func (db *DB) Query(query string, args ...interface{}) (*sql.Rows, error) {
	return db.QueryContext(context.Background(), query, args...)
}
func (db *DB) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) {
	reader, err := db.next()
	if err != nil {
		return nil, err
	}
	return reader.QueryContext(ctx, query, args...)
}
func (db *DB) QueryRow(query string, args ...interface{}) Row {
	return db.QueryRowContext(context.Background(), query, args)
}
func (db *DB) QueryRowContext(ctx context.Context, query string, args ...interface{}) Row {
	reader, err := db.next()
	if err != nil {
		return &row{err: err}
	}
	return reader.QueryRowContext(ctx, query, args...)
}
func (db *DB) SetMaxIdleConns(n int) {
	db.maxIdle = n
	db.maxIdleModified = true
	walk(db.cpool, func(conn *sql.DB) error {
		conn.SetMaxIdleConns(n)
		return nil
	})
}
func (db *DB) SetMaxOpenConns(n int) {
	db.maxOpen = n
	walk(db.cpool, func(conn *sql.DB) error {
		conn.SetMaxOpenConns(n)
		return nil
	})
}