github.com/go-board/x-go@v0.1.2-0.20220610024734-db1323f6cb15/xdatabase/xsql/cluster.go (about) 1 package xsql 2 3 import ( 4 "database/sql" 5 "log" 6 ) 7 8 type Cluster struct { 9 name string 10 master *sql.DB 11 slaves []*sql.DB 12 selector Selector 13 } 14 15 func newDB(options ConnectionOptions) (*sql.DB, error) { 16 db, err := sql.Open(options.DriverName, options.Dsn) 17 if err != nil { 18 return nil, err 19 } 20 db.SetMaxIdleConns(options.MaxIdleConn) 21 db.SetMaxOpenConns(options.MaxOpenConn) 22 db.SetConnMaxLifetime(options.ConnMaxLifeTime) 23 return db, nil 24 } 25 26 // OpenCluster init new gorm client cluster with mysql database. 27 // name is instance name of current mysql instance. 28 // selector to select which slave node to use. 29 // masterOption is master node options 30 // slaveOptions are slave nodes options 31 func OpenCluster(name string, selector Selector, masterOption ConnectionOptions, slaveOptions []ConnectionOptions) (*Cluster, error) { 32 if selector == nil { 33 selector = RoundRobinSelector() 34 } 35 db, err := newDB(masterOption) 36 if err != nil { 37 return nil, err 38 } 39 slaves := make([]*sql.DB, 0, len(slaveOptions)) 40 for _, slaveCfg := range slaveOptions { 41 db, err := newDB(slaveCfg) 42 if err != nil { 43 // open connection to slave node failed, not affect the whole cluster state 44 log.Printf("[xdatabase/xsql] open slave node failed, driver_name(%s), dsn(%s), details(%+v\n)", slaveCfg.DriverName, slaveCfg.Dsn, err) 45 continue 46 } 47 slaves = append(slaves, db) 48 } 49 return &Cluster{ 50 name: name, 51 master: db, 52 slaves: slaves, 53 selector: selector, 54 }, nil 55 } 56 57 // Master return master node to do more operation. 58 // 返回主节点。 59 func (c *Cluster) Master() *sql.DB { 60 return c.master 61 } 62 63 // SlaveBySelector get slave db use user `Selector`. 64 // 使用用户指定的选择器选择从节点。 65 func (c *Cluster) SlaveBySelector(selector Selector) *sql.DB { 66 if len(c.slaves) == 0 { 67 return c.master 68 } 69 if len(c.slaves) == 1 { 70 return c.slaves[0] 71 } 72 return c.slaves[selector.SelectDB(len(c.slaves))] 73 } 74 75 // SlaveByKey get slave db use shard `Selector` with shard key. 76 // 使用key来选择从节点。 77 func (c *Cluster) SlaveByKey(key string) *sql.DB { 78 return c.SlaveBySelector(ShardSelector(key)) 79 } 80 81 // Slave get slave db use default `Selector`. 82 // 使用默认的选择器选择从节点。 83 func (c *Cluster) Slave() *sql.DB { 84 return c.SlaveBySelector(c.selector) 85 } 86 87 // // Query will retrieve data slice from database, prefer to use slave node than master node. 88 // // 从database 获取多条数据,优先从对应的从节点读取。 89 // func (c *Cluster) Query(dest interface{}, sql string, args ...interface{}) error { 90 // return c.Slave().Raw(sql, args...).Find(dest).Error 91 // } 92 93 // // QueryOne will retrieve one record from database, prefer to use slave node than master node. 94 // // 从database 获取单条数据,优先从对应的从节点读取。 95 // func (c *Cluster) QueryOne(dest interface{}, sql string, args ...interface{}) error { 96 // return c.Slave().Raw(sql, args...).First(dest).Error 97 // } 98 99 // Exec will do update/insert/delete operation with master node. 100 // 在主节点上执行 更新/插入/删除 操作。 101 func (c *Cluster) Exec(sql string, args ...interface{}) error { 102 _, err := c.Master().Exec(sql, args...) 103 return err 104 } 105 106 // Run will do operation on master or slave node with user choose. 107 // 用户可选的在何种节点上执行操作。 108 func (c *Cluster) Run(fn func(master *sql.DB, slave *sql.DB) error) error { 109 return fn(c.Master(), c.Slave()) 110 } 111 112 // Run will do operation on master or slave node with user choose, with key to select which slave node. 113 // 用户可选的在何种节点上执行操作,key 用来选择从节点。 114 func (c *Cluster) RunShard(key string, fn func(master *sql.DB, slave *sql.DB) error) error { 115 return fn(c.Master(), c.SlaveByKey(key)) 116 } 117 118 // Transaction will do transaction on master node, with commit/rollback automatically. 119 // 在主节点上执行事务,可以自动执行commit/rollback。 120 func (c *Cluster) Transaction(fn func(tx *sql.Tx) error) error { 121 master := c.master 122 db, err := master.Begin() 123 if err != nil { 124 return err 125 } 126 if err := fn(db); err != nil { 127 _ = db.Rollback() 128 return err 129 } 130 return db.Commit() 131 }