From 760b4749bc993e466fbfa69bda0f773fe441c78a Mon Sep 17 00:00:00 2001 From: marvin Date: Tue, 15 Mar 2022 17:49:54 +0800 Subject: [PATCH 01/14] Readwrite-splitting --- README.md | 21 +++++++++++ conn_pool.go | 40 +++++++++++++++++---- sharding.go | 48 +++++++++++++++++++++---- sharding_test.go | 93 +++++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 181 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index fabe0b5..0806987 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ Gorm Sharding 是一个高性能的数据库分表中间件。 - Lighting-fast. No network based middlewares, as fast as Go. - Multiple database (PostgreSQL, MySQL) support. - Integrated primary key generator (Snowflake, PostgreSQL Sequence, Custom, ...). +- Readwrite-splitting. ## Install @@ -94,6 +95,26 @@ Recommend options: - [Snowflake](https://github.com/bwmarrin/snowflake) - [Database sequence by manully](https://www.postgresql.org/docs/current/sql-createsequence.html) +## Readwrite-splitting + +```go +dsnRead := "host=localhost user=gorm password=gorm dbname=gorm-slave port=5432 sslmode=disable" +dsnWrite := "host=localhost user=gorm password=gorm dbname=gorm port=5432 sslmode=disable" + +connRead := postgres.Open(dsnRead) +connWrite := postgres.Open(dsnWrite) + +db, err := gorm.Open(connWrite, &gorm.Config{}) + +db.Use(sharding.Register(sharding.Config{ + ShardingKey: "user_id", + NumberOfShards: 64, + PrimaryKeyGenerator: sharding.PKSnowflake, + ReadConnections: []gorm.Dialector{connRead}, + WriteConnections: []gorm.Dialector{connWrite}, +} +``` + ## Sharding process This graph show up how Gorm Sharding works. diff --git a/conn_pool.go b/conn_pool.go index f97086f..70179cf 100644 --- a/conn_pool.go +++ b/conn_pool.go @@ -3,6 +3,7 @@ package sharding import ( "context" "database/sql" + "math/rand" "gorm.io/gorm" ) @@ -36,7 +37,7 @@ func (pool ConnPool) PrepareContext(ctx context.Context, query string) (*sql.Stm } func (pool ConnPool) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) { - ftQuery, stQuery, table, err := pool.sharding.resolve(query, args...) + ftQuery, stQuery, table, stmtType, err := pool.sharding.resolve(query, args...) if err != nil { return nil, err } @@ -51,12 +52,14 @@ func (pool ConnPool) ExecContext(ctx context.Context, query string, args ...inte } } - return pool.ConnPool.ExecContext(ctx, stQuery, args...) + cp := pool.GetReadWriteConn(table, stmtType) + + return cp.ExecContext(ctx, stQuery, args...) } // https://github.com/go-gorm/gorm/blob/v1.21.11/callbacks/query.go#L18 func (pool ConnPool) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) { - ftQuery, stQuery, table, err := pool.sharding.resolve(query, args...) + ftQuery, stQuery, table, stmtType, err := pool.sharding.resolve(query, args...) if err != nil { return nil, err } @@ -71,14 +74,18 @@ func (pool ConnPool) QueryContext(ctx context.Context, query string, args ...int } } - return pool.ConnPool.QueryContext(ctx, stQuery, args...) + cp := pool.GetReadWriteConn(table, stmtType) + + return cp.QueryContext(ctx, stQuery, args...) } func (pool ConnPool) QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row { - _, query, _, _ = pool.sharding.resolve(query, args...) + _, query, table, stmtType, _ := pool.sharding.resolve(query, args...) pool.sharding.querys.Store("last_query", query) - return pool.ConnPool.QueryRowContext(ctx, query, args...) + cp := pool.GetReadWriteConn(table, stmtType) + + return cp.QueryRowContext(ctx, query, args...) } // BeginTx Implement ConnPoolBeginner.BeginTx @@ -111,3 +118,24 @@ func (pool *ConnPool) Rollback() error { func (pool *ConnPool) Ping() error { return nil } + +func (pool *ConnPool) GetReadWriteConn(table, stmtType string) gorm.ConnPool { + cp := pool.ConnPool + if table != "" { + switch stmtType { + case "SELECT": + if conns, ok := pool.sharding.readConns[table]; ok { + if len(conns) > 0 { + cp = conns[rand.Intn(len(conns))] + } + } + case "INSERT", "UPDATE", "DELETE": + if conns, ok := pool.sharding.writeConns[table]; ok { + if len(conns) > 0 { + cp = conns[rand.Intn(len(conns))] + } + } + } + } + return cp +} diff --git a/sharding.go b/sharding.go index 252ee7f..a7d40aa 100644 --- a/sharding.go +++ b/sharding.go @@ -25,6 +25,9 @@ type Sharding struct { querys sync.Map snowflakeNodes []*snowflake.Node + readConns map[string][]gorm.ConnPool + writeConns map[string][]gorm.ConnPool + _config Config _tables []interface{} } @@ -79,6 +82,12 @@ type Config struct { // return nodes[tableIdx].Generate().Int64() // } PrimaryKeyGeneratorFn func(tableIdx int64) int64 + + // ReadConnections specifies the connections for read, like SELECT. + ReadConnections []gorm.Dialector + + // WriteConnections specifies the connections for wite, like CREATE, UPDATE, DELETE. + WriteConnections []gorm.Dialector } func Register(config Config, tables ...interface{}) *Sharding { @@ -195,6 +204,30 @@ func (s *Sharding) LastQuery() string { // Initialize implement for Gorm plugin interface func (s *Sharding) Initialize(db *gorm.DB) error { s.DB = db + err := s.compile() + if err != nil { + return err + } + + s.readConns = make(map[string][]gorm.ConnPool) + s.writeConns = make(map[string][]gorm.ConnPool) + for t, c := range s.configs { + for _, dialector := range c.ReadConnections { + db, err := gorm.Open(dialector, s.DB.Config) + if err != nil { + return err + } + s.readConns[t] = append(s.readConns[t], db.Config.ConnPool) + } + for _, dialector := range c.WriteConnections { + db, err := gorm.Open(dialector, s.DB.Config) + if err != nil { + return err + } + s.writeConns[t] = append(s.writeConns[t], db.Config.ConnPool) + } + } + s.registerConnPool(db) for t, c := range s.configs { @@ -215,11 +248,11 @@ func (s *Sharding) Initialize(db *gorm.DB) error { s.snowflakeNodes[i] = n } - return s.compile() + return nil } // resolve split the old query to full table query and sharding table query -func (s *Sharding) resolve(query string, args ...interface{}) (ftQuery, stQuery, tableName string, err error) { +func (s *Sharding) resolve(query string, args ...interface{}) (ftQuery, stQuery, tableName, stmtType string, err error) { ftQuery = query stQuery = query if len(s.configs) == 0 { @@ -228,7 +261,7 @@ func (s *Sharding) resolve(query string, args ...interface{}) (ftQuery, stQuery, expr, err := sqlparser.NewParser(strings.NewReader(query)).ParseStatement() if err != nil { - return ftQuery, stQuery, tableName, nil + return ftQuery, stQuery, tableName, stmtType, nil } var table *sqlparser.TableName @@ -248,20 +281,23 @@ func (s *Sharding) resolve(query string, args ...interface{}) (ftQuery, stQuery, } table = tbl condition = stmt.Condition - + stmtType = "SELECT" case *sqlparser.InsertStatement: table = stmt.TableName isInsert = true insertNames = stmt.ColumnNames insertValues = stmt.Expressions[0].Exprs + stmtType = "INSERT" case *sqlparser.UpdateStatement: condition = stmt.Condition table = stmt.TableName + stmtType = "UPDATE" case *sqlparser.DeleteStatement: condition = stmt.Condition table = stmt.TableName + stmtType = "DELETE" default: - return ftQuery, stQuery, "", sqlparser.ErrNotImplemented + return ftQuery, stQuery, "", "", sqlparser.ErrNotImplemented } tableName = table.Name.Name @@ -313,7 +349,7 @@ func (s *Sharding) resolve(query string, args ...interface{}) (ftQuery, stQuery, if fillID { tblIdx, err := strconv.Atoi(strings.Replace(suffix, "_", "", 1)) if err != nil { - return ftQuery, stQuery, tableName, err + return ftQuery, stQuery, tableName, "", err } id := r.PrimaryKeyGeneratorFn(int64(tblIdx)) insertNames = append(insertNames, &sqlparser.Ident{Name: "id"}) diff --git a/sharding_test.go b/sharding_test.go index 3608949..c3a5914 100644 --- a/sharding_test.go +++ b/sharding_test.go @@ -37,22 +37,46 @@ func databaseURL() string { return databaseURL } +func databaseReadURL() string { + databaseURL := os.Getenv("DATABASE_READ_URL") + if len(databaseURL) == 0 { + databaseURL = "postgres://localhost:5432/sharding-read-test?sslmode=disable" + if os.Getenv("DIALECTOR") == "mysql" { + databaseURL = "root@tcp(127.0.0.1:3306)/sharding-read-test?charset=utf8mb4" + } + } + return databaseURL +} + +func databaseWriteURL() string { + databaseURL := os.Getenv("DATABASE_WRITE_URL") + if len(databaseURL) == 0 { + databaseURL = "postgres://localhost:5432/sharding-write-test?sslmode=disable" + if os.Getenv("DIALECTOR") == "mysql" { + databaseURL = "root@tcp(127.0.0.1:3306)/sharding-write-test?charset=utf8mb4" + } + } + return databaseURL +} + var ( dbConfig = postgres.Config{ DSN: databaseURL(), PreferSimpleProtocol: true, } - db *gorm.DB - - shardingConfig = Config{ - DoubleWrite: true, - ShardingKey: "user_id", - NumberOfShards: 4, - PrimaryKeyGenerator: PKSnowflake, + dbReadConfig = postgres.Config{ + DSN: databaseReadURL(), + PreferSimpleProtocol: true, } + dbWriteConfig = postgres.Config{ + DSN: databaseWriteURL(), + PreferSimpleProtocol: true, + } + db, dbRead, dbWrite *gorm.DB - middleware = Register(shardingConfig, &Order{}) - node, _ = snowflake.NewNode(1) + shardingConfig Config + middleware *Sharding + node, _ = snowflake.NewNode(1) ) func init() { @@ -60,12 +84,35 @@ func init() { db, _ = gorm.Open(mysql.Open(databaseURL()), &gorm.Config{ DisableForeignKeyConstraintWhenMigrating: true, }) + dbRead, _ = gorm.Open(mysql.Open(databaseReadURL()), &gorm.Config{ + DisableForeignKeyConstraintWhenMigrating: true, + }) + dbWrite, _ = gorm.Open(mysql.Open(databaseWriteURL()), &gorm.Config{ + DisableForeignKeyConstraintWhenMigrating: true, + }) } else { db, _ = gorm.Open(postgres.New(dbConfig), &gorm.Config{ DisableForeignKeyConstraintWhenMigrating: true, }) + dbRead, _ = gorm.Open(postgres.New(dbReadConfig), &gorm.Config{ + DisableForeignKeyConstraintWhenMigrating: true, + }) + dbWrite, _ = gorm.Open(postgres.New(dbWriteConfig), &gorm.Config{ + DisableForeignKeyConstraintWhenMigrating: true, + }) + } + + shardingConfig = Config{ + DoubleWrite: true, + ShardingKey: "user_id", + NumberOfShards: 4, + PrimaryKeyGenerator: PKSnowflake, + ReadConnections: []gorm.Dialector{dbRead.Dialector}, + WriteConnections: []gorm.Dialector{dbWrite.Dialector}, } + middleware = Register(shardingConfig, &Order{}) + fmt.Println("Clean only tables ...") dropTables() fmt.Println("AutoMigrate tables ...") @@ -80,6 +127,16 @@ func init() { user_id bigint, product text )`) + dbRead.Exec(`CREATE TABLE ` + table + ` ( + id bigint PRIMARY KEY, + user_id bigint, + product text + )`) + dbWrite.Exec(`CREATE TABLE ` + table + ` ( + id bigint PRIMARY KEY, + user_id bigint, + product text + )`) } db.Use(middleware) @@ -89,6 +146,8 @@ func dropTables() { tables := []string{"orders", "orders_0", "orders_1", "orders_2", "orders_3", "categories"} for _, table := range tables { db.Exec("DROP TABLE IF EXISTS " + table) + dbRead.Exec("DROP TABLE IF EXISTS " + table) + dbWrite.Exec("DROP TABLE IF EXISTS " + table) db.Exec(("DROP SEQUENCE IF EXISTS gorm_sharding_" + table + "_id_seq")) } } @@ -264,6 +323,22 @@ func TestPKPGSequence(t *testing.T) { assert.Equal(t, expected, middleware.LastQuery()) } +func TestReadWriteConnections(t *testing.T) { + dbRead.Exec("INSERT INTO orders_0 (id, product, user_id) VALUES(1, 'iPad', 100)") + dbWrite.Exec("INSERT INTO orders_0 (id, product, user_id) VALUES(1, 'iPad', 100)") + + var order Order + db.Model(&Order{}).Where("user_id", 100).Find(&order) + assert.Equal(t, "iPad", order.Product) + + db.Model(&Order{}).Where("user_id", 100).Update("product", "iPhone") + db.Table("orders_0").Where("user_id", 100).Find(&order) + assert.Equal(t, "iPad", order.Product) + + dbWrite.Table("orders_0").Where("user_id", 100).Find(&order) + assert.Equal(t, "iPhone", order.Product) +} + func assertQueryResult(t *testing.T, expected string, tx *gorm.DB) { t.Helper() assert.Equal(t, toDialect(expected), middleware.LastQuery()) From ab59a25aaa1324bf0f7dfb17971ec40d3567feea Mon Sep 17 00:00:00 2001 From: marvin Date: Tue, 15 Mar 2022 17:53:36 +0800 Subject: [PATCH 02/14] Format readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0806987..c77b4fd 100644 --- a/README.md +++ b/README.md @@ -110,8 +110,8 @@ db.Use(sharding.Register(sharding.Config{ ShardingKey: "user_id", NumberOfShards: 64, PrimaryKeyGenerator: sharding.PKSnowflake, - ReadConnections: []gorm.Dialector{connRead}, - WriteConnections: []gorm.Dialector{connWrite}, + ReadConnections: []gorm.Dialector{connRead}, + WriteConnections: []gorm.Dialector{connWrite}, } ``` From e671ddd97574914a9bcf05a48be40cee361a2845 Mon Sep 17 00:00:00 2001 From: marvin Date: Thu, 17 Mar 2022 17:05:53 +0800 Subject: [PATCH 03/14] Combining with dbresolver --- README.md | 26 +++++++++++--------- conn_pool.go | 53 +++++----------------------------------- go.mod | 3 ++- go.sum | 10 +++++++- sharding.go | 63 +++++++++++++++--------------------------------- sharding_test.go | 14 ++++++++--- 6 files changed, 63 insertions(+), 106 deletions(-) diff --git a/README.md b/README.md index c77b4fd..f0644ef 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,6 @@ Gorm Sharding 是一个高性能的数据库分表中间件。 - Lighting-fast. No network based middlewares, as fast as Go. - Multiple database (PostgreSQL, MySQL) support. - Integrated primary key generator (Snowflake, PostgreSQL Sequence, Custom, ...). -- Readwrite-splitting. ## Install @@ -95,24 +94,29 @@ Recommend options: - [Snowflake](https://github.com/bwmarrin/snowflake) - [Database sequence by manully](https://www.postgresql.org/docs/current/sql-createsequence.html) -## Readwrite-splitting +## Combining with dbresolver + +> 🚨 NOTE: Use dbresolver first. ```go +dsn := "host=localhost user=gorm password=gorm dbname=gorm port=5432 sslmode=disable" dsnRead := "host=localhost user=gorm password=gorm dbname=gorm-slave port=5432 sslmode=disable" -dsnWrite := "host=localhost user=gorm password=gorm dbname=gorm port=5432 sslmode=disable" +conn := postgres.Open(dsn) connRead := postgres.Open(dsnRead) -connWrite := postgres.Open(dsnWrite) -db, err := gorm.Open(connWrite, &gorm.Config{}) +db, err := gorm.Open(conn, &gorm.Config{}) +dbRead, err := gorm.Open(conn, &gorm.Config{}) + +db.Use(dbresolver.Register(dbresolver.Config{ + Replicas: []gorm.Dialector{dbRead.Dialector}, +})) db.Use(sharding.Register(sharding.Config{ - ShardingKey: "user_id", - NumberOfShards: 64, - PrimaryKeyGenerator: sharding.PKSnowflake, - ReadConnections: []gorm.Dialector{connRead}, - WriteConnections: []gorm.Dialector{connWrite}, -} + ShardingKey: "user_id", + NumberOfShards: 64, + PrimaryKeyGenerator: sharding.PKSnowflake, +})) ``` ## Sharding process diff --git a/conn_pool.go b/conn_pool.go index 70179cf..3beaa7e 100644 --- a/conn_pool.go +++ b/conn_pool.go @@ -3,7 +3,6 @@ package sharding import ( "context" "database/sql" - "math/rand" "gorm.io/gorm" ) @@ -15,19 +14,6 @@ type ConnPool struct { gorm.ConnPool } -// registerConnPool replace Gorm db.ConnPool as custom -func (s *Sharding) registerConnPool(db *gorm.DB) { - // Avoid assign loop - basePool := db.ConnPool - if _, ok := basePool.(ConnPool); ok { - return - } - - s.ConnPool = &ConnPool{ConnPool: basePool, sharding: s} - db.ConnPool = s.ConnPool - db.Statement.ConnPool = s.ConnPool -} - func (pool *ConnPool) String() string { return "gorm:sharding:conn_pool" } @@ -37,7 +23,7 @@ func (pool ConnPool) PrepareContext(ctx context.Context, query string) (*sql.Stm } func (pool ConnPool) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) { - ftQuery, stQuery, table, stmtType, err := pool.sharding.resolve(query, args...) + ftQuery, stQuery, table, err := pool.sharding.resolve(query, args...) if err != nil { return nil, err } @@ -52,14 +38,12 @@ func (pool ConnPool) ExecContext(ctx context.Context, query string, args ...inte } } - cp := pool.GetReadWriteConn(table, stmtType) - - return cp.ExecContext(ctx, stQuery, args...) + return pool.ConnPool.ExecContext(ctx, stQuery, args...) } // https://github.com/go-gorm/gorm/blob/v1.21.11/callbacks/query.go#L18 func (pool ConnPool) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) { - ftQuery, stQuery, table, stmtType, err := pool.sharding.resolve(query, args...) + ftQuery, stQuery, table, err := pool.sharding.resolve(query, args...) if err != nil { return nil, err } @@ -74,18 +58,14 @@ func (pool ConnPool) QueryContext(ctx context.Context, query string, args ...int } } - cp := pool.GetReadWriteConn(table, stmtType) - - return cp.QueryContext(ctx, stQuery, args...) + return pool.ConnPool.QueryContext(ctx, stQuery, args...) } func (pool ConnPool) QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row { - _, query, table, stmtType, _ := pool.sharding.resolve(query, args...) + _, query, _, _ = pool.sharding.resolve(query, args...) pool.sharding.querys.Store("last_query", query) - cp := pool.GetReadWriteConn(table, stmtType) - - return cp.QueryRowContext(ctx, query, args...) + return pool.ConnPool.QueryRowContext(ctx, query, args...) } // BeginTx Implement ConnPoolBeginner.BeginTx @@ -118,24 +98,3 @@ func (pool *ConnPool) Rollback() error { func (pool *ConnPool) Ping() error { return nil } - -func (pool *ConnPool) GetReadWriteConn(table, stmtType string) gorm.ConnPool { - cp := pool.ConnPool - if table != "" { - switch stmtType { - case "SELECT": - if conns, ok := pool.sharding.readConns[table]; ok { - if len(conns) > 0 { - cp = conns[rand.Intn(len(conns))] - } - } - case "INSERT", "UPDATE", "DELETE": - if conns, ok := pool.sharding.writeConns[table]; ok { - if len(conns) > 0 { - cp = conns[rand.Intn(len(conns))] - } - } - } - } - return cp -} diff --git a/go.mod b/go.mod index 5bb3fc4..8e3009f 100644 --- a/go.mod +++ b/go.mod @@ -8,8 +8,9 @@ require ( github.com/longbridgeapp/sqlparser v0.3.1 gorm.io/driver/mysql v1.3.2 gorm.io/driver/postgres v1.3.1 - gorm.io/gorm v1.23.1 + gorm.io/gorm v1.23.2 gorm.io/hints v1.1.0 + gorm.io/plugin/dbresolver v1.1.0 ) require ( diff --git a/go.sum b/go.sum index da148b8..4c9c076 100644 --- a/go.sum +++ b/go.sum @@ -13,6 +13,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -76,6 +77,7 @@ github.com/jackc/puddle v1.2.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dv github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas= github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= @@ -205,16 +207,22 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/mysql v1.0.3/go.mod h1:twGxftLBlFgNVNakL7F+P/x9oYqoymG3YYT8cAfI9oI= gorm.io/driver/mysql v1.3.2 h1:QJryWiqQ91EvZ0jZL48NOpdlPdMjdip1hQ8bTgo4H7I= gorm.io/driver/mysql v1.3.2/go.mod h1:ChK6AHbHgDCFZyJp0F+BmVGb06PSIoh9uVYKAlRbb2U= gorm.io/driver/postgres v1.3.1 h1:Pyv+gg1Gq1IgsLYytj/S2k7ebII3CzEdpqQkPOdH24g= gorm.io/driver/postgres v1.3.1/go.mod h1:WwvWOuR9unCLpGWCL6Y3JOeBWvbKi6JLhayiVclSZZU= gorm.io/driver/sqlite v1.1.6 h1:p3U8WXkVFTOLPED4JjrZExfndjOtya3db8w9/vEMNyI= gorm.io/driver/sqlite v1.1.6/go.mod h1:W8LmC/6UvVbHKah0+QOC7Ja66EaZXHwUTjgXY8YNWX8= +gorm.io/gorm v1.20.4/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= +gorm.io/gorm v1.20.11/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= gorm.io/gorm v1.21.15/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= gorm.io/gorm v1.22.2/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= -gorm.io/gorm v1.23.1 h1:aj5IlhDzEPsoIyOPtTRVI+SyaN1u6k613sbt4pwbxG0= gorm.io/gorm v1.23.1/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= +gorm.io/gorm v1.23.2 h1:xmq9QRMWL8HTJyhAUBXy8FqIIQCYESeKfJL4DoGKiWQ= +gorm.io/gorm v1.23.2/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= gorm.io/hints v1.1.0 h1:Lp4z3rxREufSdxn4qmkK3TLDltrM10FLTHiuqwDPvXw= gorm.io/hints v1.1.0/go.mod h1:lKQ0JjySsPBj3uslFzY3JhYDtqEwzm+G1hv8rWujB6Y= +gorm.io/plugin/dbresolver v1.1.0 h1:cegr4DeprR6SkLIQlKhJLYxH8muFbJ4SmnojXvoeb00= +gorm.io/plugin/dbresolver v1.1.0/go.mod h1:tpImigFAEejCALOttyhWqsy4vfa2Uh/vAUVnL5IRF7Y= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= diff --git a/sharding.go b/sharding.go index a7d40aa..df2ccf9 100644 --- a/sharding.go +++ b/sharding.go @@ -25,9 +25,6 @@ type Sharding struct { querys sync.Map snowflakeNodes []*snowflake.Node - readConns map[string][]gorm.ConnPool - writeConns map[string][]gorm.ConnPool - _config Config _tables []interface{} } @@ -82,12 +79,6 @@ type Config struct { // return nodes[tableIdx].Generate().Int64() // } PrimaryKeyGeneratorFn func(tableIdx int64) int64 - - // ReadConnections specifies the connections for read, like SELECT. - ReadConnections []gorm.Dialector - - // WriteConnections specifies the connections for wite, like CREATE, UPDATE, DELETE. - WriteConnections []gorm.Dialector } func Register(config Config, tables ...interface{}) *Sharding { @@ -204,31 +195,7 @@ func (s *Sharding) LastQuery() string { // Initialize implement for Gorm plugin interface func (s *Sharding) Initialize(db *gorm.DB) error { s.DB = db - err := s.compile() - if err != nil { - return err - } - - s.readConns = make(map[string][]gorm.ConnPool) - s.writeConns = make(map[string][]gorm.ConnPool) - for t, c := range s.configs { - for _, dialector := range c.ReadConnections { - db, err := gorm.Open(dialector, s.DB.Config) - if err != nil { - return err - } - s.readConns[t] = append(s.readConns[t], db.Config.ConnPool) - } - for _, dialector := range c.WriteConnections { - db, err := gorm.Open(dialector, s.DB.Config) - if err != nil { - return err - } - s.writeConns[t] = append(s.writeConns[t], db.Config.ConnPool) - } - } - - s.registerConnPool(db) + s.registerCallbacks(db) for t, c := range s.configs { if c.PrimaryKeyGenerator == PKPGSequence { @@ -248,11 +215,25 @@ func (s *Sharding) Initialize(db *gorm.DB) error { s.snowflakeNodes[i] = n } - return nil + return s.compile() +} + +func (s *Sharding) registerCallbacks(db *gorm.DB) { + s.Callback().Create().Before("*").Register("gorm:sharding", s.switchConn) + s.Callback().Query().Before("*").Register("gorm:sharding", s.switchConn) + s.Callback().Update().Before("*").Register("gorm:sharding", s.switchConn) + s.Callback().Delete().Before("*").Register("gorm:sharding", s.switchConn) + s.Callback().Row().Before("*").Register("gorm:sharding", s.switchConn) + s.Callback().Raw().Before("*").Register("gorm:sharding", s.switchConn) +} + +func (s *Sharding) switchConn(db *gorm.DB) { + s.ConnPool = &ConnPool{ConnPool: db.Statement.ConnPool, sharding: s} + db.Statement.ConnPool = s.ConnPool } // resolve split the old query to full table query and sharding table query -func (s *Sharding) resolve(query string, args ...interface{}) (ftQuery, stQuery, tableName, stmtType string, err error) { +func (s *Sharding) resolve(query string, args ...interface{}) (ftQuery, stQuery, tableName string, err error) { ftQuery = query stQuery = query if len(s.configs) == 0 { @@ -261,7 +242,7 @@ func (s *Sharding) resolve(query string, args ...interface{}) (ftQuery, stQuery, expr, err := sqlparser.NewParser(strings.NewReader(query)).ParseStatement() if err != nil { - return ftQuery, stQuery, tableName, stmtType, nil + return ftQuery, stQuery, tableName, nil } var table *sqlparser.TableName @@ -281,23 +262,19 @@ func (s *Sharding) resolve(query string, args ...interface{}) (ftQuery, stQuery, } table = tbl condition = stmt.Condition - stmtType = "SELECT" case *sqlparser.InsertStatement: table = stmt.TableName isInsert = true insertNames = stmt.ColumnNames insertValues = stmt.Expressions[0].Exprs - stmtType = "INSERT" case *sqlparser.UpdateStatement: condition = stmt.Condition table = stmt.TableName - stmtType = "UPDATE" case *sqlparser.DeleteStatement: condition = stmt.Condition table = stmt.TableName - stmtType = "DELETE" default: - return ftQuery, stQuery, "", "", sqlparser.ErrNotImplemented + return ftQuery, stQuery, "", sqlparser.ErrNotImplemented } tableName = table.Name.Name @@ -349,7 +326,7 @@ func (s *Sharding) resolve(query string, args ...interface{}) (ftQuery, stQuery, if fillID { tblIdx, err := strconv.Atoi(strings.Replace(suffix, "_", "", 1)) if err != nil { - return ftQuery, stQuery, tableName, "", err + return ftQuery, stQuery, tableName, err } id := r.PrimaryKeyGeneratorFn(int64(tblIdx)) insertNames = append(insertNames, &sqlparser.Ident{Name: "id"}) diff --git a/sharding_test.go b/sharding_test.go index c3a5914..1e35305 100644 --- a/sharding_test.go +++ b/sharding_test.go @@ -13,6 +13,7 @@ import ( "gorm.io/driver/postgres" "gorm.io/gorm" "gorm.io/hints" + "gorm.io/plugin/dbresolver" ) type Order struct { @@ -107,8 +108,6 @@ func init() { ShardingKey: "user_id", NumberOfShards: 4, PrimaryKeyGenerator: PKSnowflake, - ReadConnections: []gorm.Dialector{dbRead.Dialector}, - WriteConnections: []gorm.Dialector{dbWrite.Dialector}, } middleware = Register(shardingConfig, &Order{}) @@ -323,10 +322,19 @@ func TestPKPGSequence(t *testing.T) { assert.Equal(t, expected, middleware.LastQuery()) } -func TestReadWriteConnections(t *testing.T) { +func TestReadWriteSplitting(t *testing.T) { dbRead.Exec("INSERT INTO orders_0 (id, product, user_id) VALUES(1, 'iPad', 100)") dbWrite.Exec("INSERT INTO orders_0 (id, product, user_id) VALUES(1, 'iPad', 100)") + db, _ := gorm.Open(postgres.New(dbConfig), &gorm.Config{ + DisableForeignKeyConstraintWhenMigrating: true, + }) + db.Use(dbresolver.Register(dbresolver.Config{ + Sources: []gorm.Dialector{dbWrite.Dialector}, + Replicas: []gorm.Dialector{dbRead.Dialector}, + })) + db.Use(middleware) + var order Order db.Model(&Order{}).Where("user_id", 100).Find(&order) assert.Equal(t, "iPad", order.Product) From e7aade8cad8a282244a1852ef03dd9b5406a1a8d Mon Sep 17 00:00:00 2001 From: marvin Date: Fri, 18 Mar 2022 20:57:34 +0800 Subject: [PATCH 04/14] Create databases on ci --- .github/workflows/tests.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 15c1af5..cdc7e94 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -35,6 +35,8 @@ jobs: env: DIALECTOR: postgres DATABASE_URL: postgres://gorm:gorm@localhost:5432/sharding-test + DATABASE_READ_URL: postgres://gorm:gorm@localhost:5432/sharding-read-test + DATABASE_WRITE_URL: postgres://gorm:gorm@localhost:5432/sharding-write-test steps: - name: Set up Go uses: actions/setup-go@v1 @@ -42,6 +44,12 @@ jobs: go-version: 1.17 id: go + - name: Create Read Database + run: psql -U gorm -c 'CREATE DATABASE "sharding-read-test";' + + - name: Create Write Databases + run: psql -U gorm -c 'CREATE DATABASE "sharding-write-test";' + - name: Check out code into the Go module directory uses: actions/checkout@v1 @@ -80,6 +88,8 @@ jobs: env: DIALECTOR: mysql DATABASE_URL: gorm:gorm@tcp(127.0.0.1:3306)/sharding-test?charset=utf8mb4&parseTime=True&loc=Local + DATABASE_READ_URL: gorm:gorm@tcp(127.0.0.1:3306)/sharding-read-test?charset=utf8mb4&parseTime=True&loc=Local + DATABASE_WRITE_URL: gorm:gorm@tcp(127.0.0.1:3306)/sharding-write-test?charset=utf8mb4&parseTime=True&loc=Local steps: - name: Set up Go uses: actions/setup-go@v1 @@ -87,6 +97,12 @@ jobs: go-version: 1.17 id: go + - name: Create Read Database + run: mysqladmin -ugorm -pgorm create sharding-read-test + + - name: Create Write Database + run: mysqladmin -ugorm -pgorm create sharding-write-test + - name: Check out code into the Go module directory uses: actions/checkout@v1 From fbf2ad4ff23a750cd05bc54d942690883602d165 Mon Sep 17 00:00:00 2001 From: marvin Date: Fri, 18 Mar 2022 21:11:43 +0800 Subject: [PATCH 05/14] try localhost on ci --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index cdc7e94..b3a619b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -45,10 +45,10 @@ jobs: id: go - name: Create Read Database - run: psql -U gorm -c 'CREATE DATABASE "sharding-read-test";' + run: psql -h localhost -U gorm -c 'CREATE DATABASE "sharding-read-test";' - name: Create Write Databases - run: psql -U gorm -c 'CREATE DATABASE "sharding-write-test";' + run: psql -h localhost -U gorm -c 'CREATE DATABASE "sharding-write-test";' - name: Check out code into the Go module directory uses: actions/checkout@v1 From 29b9af1ed5a092b8b1302ef12eb0567a07f570be Mon Sep 17 00:00:00 2001 From: marvin Date: Fri, 18 Mar 2022 21:17:44 +0800 Subject: [PATCH 06/14] connect to pg on ci --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b3a619b..a733066 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -45,10 +45,10 @@ jobs: id: go - name: Create Read Database - run: psql -h localhost -U gorm -c 'CREATE DATABASE "sharding-read-test";' + run: PGPASSWORD=gorm psql -h localhost -U gorm -c 'CREATE DATABASE "sharding-read-test";' - name: Create Write Databases - run: psql -h localhost -U gorm -c 'CREATE DATABASE "sharding-write-test";' + run: PGPASSWORD=gorm psql -h localhost -U gorm -c 'CREATE DATABASE "sharding-write-test";' - name: Check out code into the Go module directory uses: actions/checkout@v1 From 6cd691850424a68f9e6d5bc9dade701c3a559013 Mon Sep 17 00:00:00 2001 From: marvin Date: Fri, 18 Mar 2022 21:20:56 +0800 Subject: [PATCH 07/14] connect to pg on ci --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a733066..118b9d3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -45,10 +45,10 @@ jobs: id: go - name: Create Read Database - run: PGPASSWORD=gorm psql -h localhost -U gorm -c 'CREATE DATABASE "sharding-read-test";' + run: PGPASSWORD=gorm psql -h localhost -U gorm -d sharding-test -c 'CREATE DATABASE "sharding-read-test";' - name: Create Write Databases - run: PGPASSWORD=gorm psql -h localhost -U gorm -c 'CREATE DATABASE "sharding-write-test";' + run: PGPASSWORD=gorm psql -h localhost -U gorm -d sharding-test -c 'CREATE DATABASE "sharding-write-test";' - name: Check out code into the Go module directory uses: actions/checkout@v1 From 71bf9999fd5ca97b03869c39b8303a6c6baefcbe Mon Sep 17 00:00:00 2001 From: marvin Date: Fri, 18 Mar 2022 21:24:25 +0800 Subject: [PATCH 08/14] connect to mysql on ci --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 118b9d3..a8de178 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -98,10 +98,10 @@ jobs: id: go - name: Create Read Database - run: mysqladmin -ugorm -pgorm create sharding-read-test + run: mysqladmin -hlocalhost -ugorm -pgorm create sharding-read-test - name: Create Write Database - run: mysqladmin -ugorm -pgorm create sharding-write-test + run: mysqladmin -hlocalhost -ugorm -pgorm create sharding-write-test - name: Check out code into the Go module directory uses: actions/checkout@v1 From 3c059b60216bfc7241c29d29177d8913725ba93f Mon Sep 17 00:00:00 2001 From: marvin Date: Fri, 18 Mar 2022 21:28:34 +0800 Subject: [PATCH 09/14] connect to mysql on ci --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a8de178..4ae66f0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -98,10 +98,10 @@ jobs: id: go - name: Create Read Database - run: mysqladmin -hlocalhost -ugorm -pgorm create sharding-read-test + run: mysqladmin -h localhost -ugorm -pgorm create sharding-read-test - name: Create Write Database - run: mysqladmin -hlocalhost -ugorm -pgorm create sharding-write-test + run: mysqladmin -h localhost -ugorm -pgorm create sharding-write-test - name: Check out code into the Go module directory uses: actions/checkout@v1 From 88444f96d3e856b46f118237a2772ac9075d6aed Mon Sep 17 00:00:00 2001 From: marvin Date: Fri, 18 Mar 2022 21:31:36 +0800 Subject: [PATCH 10/14] connect to mysql on ci --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4ae66f0..436e18e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -98,10 +98,10 @@ jobs: id: go - name: Create Read Database - run: mysqladmin -h localhost -ugorm -pgorm create sharding-read-test + run: mysqladmin --host=localhost -ugorm -pgorm create sharding-read-test - name: Create Write Database - run: mysqladmin -h localhost -ugorm -pgorm create sharding-write-test + run: mysqladmin --host=localhost -ugorm -pgorm create sharding-write-test - name: Check out code into the Go module directory uses: actions/checkout@v1 From 394563b22c191464e20c12bf1a5f96246d83e29d Mon Sep 17 00:00:00 2001 From: marvin Date: Fri, 18 Mar 2022 21:43:12 +0800 Subject: [PATCH 11/14] connect to mysql on ci --- .github/workflows/tests.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 436e18e..8746b0e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -76,6 +76,7 @@ jobs: MYSQL_USER: gorm MYSQL_PASSWORD: gorm MYSQL_RANDOM_ROOT_PASSWORD: "yes" + MYSQL_HOST: mysql ports: - 3306:3306 options: >- @@ -98,10 +99,10 @@ jobs: id: go - name: Create Read Database - run: mysqladmin --host=localhost -ugorm -pgorm create sharding-read-test + run: mysqladmin -h mysql -ugorm -pgorm create sharding-read-test - name: Create Write Database - run: mysqladmin --host=localhost -ugorm -pgorm create sharding-write-test + run: mysqladmin -h mysql -ugorm -pgorm create sharding-write-test - name: Check out code into the Go module directory uses: actions/checkout@v1 From 15db02eeb908f0e1fd93eacf46afa81ce99be2cc Mon Sep 17 00:00:00 2001 From: marvin Date: Fri, 18 Mar 2022 21:55:50 +0800 Subject: [PATCH 12/14] connect to mysql on ci --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8746b0e..a9b68a6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -99,10 +99,10 @@ jobs: id: go - name: Create Read Database - run: mysqladmin -h mysql -ugorm -pgorm create sharding-read-test + run: mysqladmin -h 127.0.0.1 -ugorm -pgorm create sharding-read-test - name: Create Write Database - run: mysqladmin -h mysql -ugorm -pgorm create sharding-write-test + run: mysqladmin -h 127.0.0.1 -ugorm -pgorm create sharding-write-test - name: Check out code into the Go module directory uses: actions/checkout@v1 From 0d1b8bc46d2f99ac4f729531c975bba1b67f0eff Mon Sep 17 00:00:00 2001 From: marvin Date: Fri, 18 Mar 2022 22:02:51 +0800 Subject: [PATCH 13/14] connect to mysql on ci --- .github/workflows/tests.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a9b68a6..d84c74f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -75,8 +75,7 @@ jobs: MYSQL_DATABASE: sharding-test MYSQL_USER: gorm MYSQL_PASSWORD: gorm - MYSQL_RANDOM_ROOT_PASSWORD: "yes" - MYSQL_HOST: mysql + MYSQL_ROOT_PASSWORD: gorm ports: - 3306:3306 options: >- @@ -89,8 +88,8 @@ jobs: env: DIALECTOR: mysql DATABASE_URL: gorm:gorm@tcp(127.0.0.1:3306)/sharding-test?charset=utf8mb4&parseTime=True&loc=Local - DATABASE_READ_URL: gorm:gorm@tcp(127.0.0.1:3306)/sharding-read-test?charset=utf8mb4&parseTime=True&loc=Local - DATABASE_WRITE_URL: gorm:gorm@tcp(127.0.0.1:3306)/sharding-write-test?charset=utf8mb4&parseTime=True&loc=Local + DATABASE_READ_URL: root:gorm@tcp(127.0.0.1:3306)/sharding-read-test?charset=utf8mb4&parseTime=True&loc=Local + DATABASE_WRITE_URL: root:gorm@tcp(127.0.0.1:3306)/sharding-write-test?charset=utf8mb4&parseTime=True&loc=Local steps: - name: Set up Go uses: actions/setup-go@v1 @@ -99,10 +98,12 @@ jobs: id: go - name: Create Read Database - run: mysqladmin -h 127.0.0.1 -ugorm -pgorm create sharding-read-test + run: mysqladmin -h 127.0.0.1 -uroot -pgorm create sharding-read-test + #run: mysql -e 'CREATE DATABASE sharding-read-test' -ugorm -pgorm - name: Create Write Database - run: mysqladmin -h 127.0.0.1 -ugorm -pgorm create sharding-write-test + run: mysqladmin -h 127.0.0.1 -uroot -pgorm create sharding-write-test + #run: mysql -e 'CREATE DATABASE sharding-write-test' -ugorm -pgorm - name: Check out code into the Go module directory uses: actions/checkout@v1 From 1ac1ef10ed5412418ed011d2a0011771605f85ad Mon Sep 17 00:00:00 2001 From: marvin Date: Fri, 18 Mar 2022 22:14:28 +0800 Subject: [PATCH 14/14] fix mysql tests --- sharding_test.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/sharding_test.go b/sharding_test.go index 1e35305..eaeb8d0 100644 --- a/sharding_test.go +++ b/sharding_test.go @@ -326,9 +326,17 @@ func TestReadWriteSplitting(t *testing.T) { dbRead.Exec("INSERT INTO orders_0 (id, product, user_id) VALUES(1, 'iPad', 100)") dbWrite.Exec("INSERT INTO orders_0 (id, product, user_id) VALUES(1, 'iPad', 100)") - db, _ := gorm.Open(postgres.New(dbConfig), &gorm.Config{ - DisableForeignKeyConstraintWhenMigrating: true, - }) + var db *gorm.DB + if os.Getenv("DIALECTOR") == "mysql" { + db, _ = gorm.Open(mysql.Open(databaseURL()), &gorm.Config{ + DisableForeignKeyConstraintWhenMigrating: true, + }) + } else { + db, _ = gorm.Open(postgres.New(dbConfig), &gorm.Config{ + DisableForeignKeyConstraintWhenMigrating: true, + }) + } + db.Use(dbresolver.Register(dbresolver.Config{ Sources: []gorm.Dialector{dbWrite.Dialector}, Replicas: []gorm.Dialector{dbRead.Dialector},