Giter Club home page Giter Club logo

sharding's People

Contributors

a631807682 avatar dependabot[bot] avatar huacnlee avatar hyperphoton avatar jinzhu avatar liangjunmo avatar ricleal avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

sharding's Issues

The sharding does not work when call gorm.Create method!

GORM Playground Link

go-gorm/playground#1

Description

Recently, I test sharding plugin and try to intergate it in my project. However, it fail the tests when I run the example in https://github.com/go-gorm/sharding/tree/main/examples/order.go.
The only difference is the database driver. I replace postgres to mysql.

enviroment:

OS: WSL1 for Win10
Go: go version go1.17.6 linux/amd64
Package:
	gorm.io/driver/mysql v1.2.3
	gorm.io/gorm v1.22.5
	gorm.io/sharding v0.4.0

The following is the error message:

2022/02/21 09:19:34 ~/main.go:67 Error 1146: Table 'gorm.orders' doesn't exist
[19.048ms] [rows:0] INSERT INTO `orders` (`user_id`,`product_id`) VALUES (2,0)
Error 1146: Table 'gorm.orders' doesn't exist

and the code:

// this record will insert to orders_02
	err = db.Create(&Order{UserID: 2}).Error
	if err != nil {
		fmt.Println(err)
	}
```go

Apparently, the sharding policy does not work. It try to insert new record in `orders` not `orders_2`.

MySQL Nested transaction failed due to incorrect interface impl.

Description

in gorm.io/gorm/finisher_api.go:547 Transaction, nested transaction need to check if connPool fit TxCommitter:

  • can fit: in nested transaction now, BEGIN has been called, so use SAVEPOINT instead
  • can't fit: not in nested transaction now, call BEGIN.

However, sharding plugin impl BeginTx / Commit / Rollback in same struct. So it can fit TxCommitter interface without BEGIN call. The nested transaction won't work.

ConnPool struct CANNOT impl TxBeginner and TxCommitter interface in the same time. When BeginTx is called, different struct need to be returned.

Does not work in MySQL

                type Order struct {
			ID        int64 `gorm:"primarykey"`
			UserID    int64
			ProductID int64
		}

		for i := 0; i < 64; i += 1 {
			table := fmt.Sprintf("orders_%02d", i)
			db.Exec(`DROP TABLE IF EXISTS ` + table)
			db.Exec(`CREATE TABLE ` + table + ` (id int PRIMARY KEY,user_id int,product_id int)`)
		}

		middleware := sharding.Register(sharding.Config{
			ShardingKey:         "user_id",
			NumberOfShards:      64,
			PrimaryKeyGenerator: sharding.PKSnowflake,
		}, "orders")
		err = db.Use(middleware)
		if err != nil {
			panic(err)
		}

		err = db.Create(&Order{UserID: 2}).Error
		if err != nil {
			fmt.Println(err)
		}

		err = db.Create(&Order{UserID: 3}).Error
		if err != nil {
			fmt.Println(err)
		}

		err = db.Create(&Order{UserID: 4}).Error
		if err != nil {
			fmt.Println(err)
		}

Create table successfully

[13.420ms] [rows:0] CREATE TABLE orders_00 (id int PRIMARY KEY,user_id int,product_id int)
...
[13.420ms] [rows:0] CREATE TABLE orders_63 (id int PRIMARY KEY,user_id int,product_id int)

However, when Gorm is used normally, the corresponding table cannot be indexed

Index to "orders"

2022/01/27 23:41:07 ./gorm.go:144 Error 1146: Table 'orders' doesn't exist
[0.567ms] [rows:0] INSERT INTO `orders` (`user_id`,`product_id`) VALUES (2,0)
Error 1146: Table 'orders' doesn't exist

db.Statement change after use plugin

stmt.Parse will change its own value, it will cause some bugs.
https://github.com/go-gorm/gorm/blob/master/migrator/migrator.go#L48
https://github.com/go-gorm/sharding/blob/main/sharding.go#L113

func (s *Sharding) compile() error {
...
			// stmt := &gorm.Statement{DB: s.DB}
			stmt := s.DB.Statement
			if err := stmt.Parse(table); err == nil {
				s.configs[stmt.Table] = s._config
			} else {
				return err
			}
...
}

I think the code stmt := &gorm.Statement{DB: s.DB} should be used instead, but it is commented, is this necessary?

Use sharding with AutoMigrate failed

Description

  1. when a dataobject use sharding, and automigrate when connect to Mysql server, it will just appear a tabel, and sharding aljust like this
ShardingAlgorithm: func(columnValue interface{}) (suffix string, err error) {
	if id, ok := columnValue.(uint); ok {
		return fmt.Sprintf("_%02d", id%shardingNum), nil
	}
	return "", fmt.Errorf("invalid column value type")
}

and just migrate table {table_name}_00

  1. when override TableName(), and just like this
func (d *dataobject) TableName() string {
	return fmt.Sprintf("table_name_%02d", d.ID%shardingNum)
}

and just migrate table {table_name}_00 and didnot creat any new table

sharding.Register((sharding.Config literal), (model.XXXX literal)).Register undefined (type *sharding.Sharding has no field or method

code:
db.Use(sharding.Register(sharding.Config{ ShardingKey: "user_id", NumberOfShards: 64, PrimaryKeyGenerator: sharding.PKSnowflake, }, "orders").Register(sharding.Config{ ShardingKey: "user_id", NumberOfShards: 256, PrimaryKeyGenerator: sharding.PKSnowflake, // This case for show up give notifications, audit_logs table use same sharding rule. }, Notification{}, AuditLog{}))
the second Register function show error below:
*sharding.Register((sharding.Config literal), (model.XXXX literal)).Register undefined (type sharding.Sharding has no field or method
why?
thanks.

Error 1054 (42S22): Unknown column 'user_follow.deleted_at' in 'where clause'

GORM Playground Link

568

Description

I also met this question sharding Error 1054: Unknown column 'ny_order.deleted_at' in 'where clause'

detailed error as follows

2023/02/19 20:57:18 testing mysql...
=== RUN   TestGORM

2023/02/19 20:57:18 /Users/zhengjb/go/src/github.com/zishiguo/playground/main_test.go:13 Error 1054 (42S22): Unknown column 'user_follow.deleted_at' in 'where clause'
[2.526ms] [rows:0] SELECT * FROM `user_follow` WHERE user_id = 79535114761158382 AND `user_follow`.`deleted_at` IS NULL
    main_test.go:14: Failed, got error: Error 1054 (42S22): Unknown column 'user_follow.deleted_at' in 'where clause'
--- FAIL: TestGORM (0.00s)

FAIL

Process finished with the exit code 1

Why restrict the primary key of sharding to int64 and must be called "ID"

Why restrict the primary key of sharding to int64 and must be called "ID"

I am using the latest version 0.5.3 of gorm sharding and have encountered two issues with primary keys.
Problem 1: When inserting, a primary key will be generated based on the suffix. My suffix is not a number, which causes the insert to fail (such as line 363 of the sharding.go source code image). The solution comes from rows 357 and 358, but my table primary key is not called "ID" but "order_id". Is there a way to skip the error of row 363.
image

Question 2: When querying, if there is an "id" field, it must be of type int64, otherwise it will cause an error in line 486. Is there a way to skip this judgment and successfully query
image

Expected answer

I want to know if there is a way to bypass the limitation of gorm sharing for primary keys that are "ID" and of type int64

Question about the requirement of go version

Your Question

In the latest release v0.6.0, the go version requirement in go.mod was upgraded from 1.20 to 1.21.
According to the release note of go 1.21, the go version in go.mod is now a strict minimum requirement, which means that in order to use this module, users need to upgrade their go version to 1.21. This may cause some concerns, since go 1.21 was just released a month ago.
My question is that could we downgrade the go version of go.mod?

The document you expected this should be explained

Expected answer

property does not exist

Cannot find NumberOfShards in sharding.Config, PrimaryKeyGenerator in field, and sharding.PKSnowflake property does not exist.
Can you provide the latest examples

example doesn't cover soft delete cases

Your Question

     I met soft delete question.

    order.go doesn't cover soft delete cases.

The document you expected this should be explained

    example can cover soft delete cases.

Expected answer

    example can cover soft delete cases.

MySQL transaction not work due to conn pool management.

Description

// BeginTx Implement ConnPoolBeginner.BeginTx
func (pool *ConnPool) BeginTx(ctx context.Context, opt *sql.TxOptions) (gorm.ConnPool, error) {
	if basePool, ok := pool.ConnPool.(gorm.ConnPoolBeginner); ok {
		return basePool.BeginTx(ctx, opt)
	}
	return pool, nil
}

For this method, when init with default setting, pool.ConnPool is actually an sql.DB struct, which return pool, nil directly. Transaction is not opened.

When DoubleWrite is on, is the query also run twice?

Your Question

func (pool ConnPool) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) {
	ftQuery, stQuery, table, err := pool.sharding.resolve(query, args...)
	if err != nil {
		return nil, err
	}

	pool.sharding.querys.Store("last_query", stQuery)

	if table != "" {
		if r, ok := pool.sharding.configs[table]; ok {
			if r.DoubleWrite {
				pool.ConnPool.ExecContext(ctx, ftQuery, args...)
			}
		}
	}

	return pool.ConnPool.QueryContext(ctx, stQuery, args...)
}

The document you expected this should be explained

Expected answer

Dose it support mysql well now?

Your Question

I'm using mysql and I‘m not sure if i should try using this library.

image
image

The document you expected this should be explained

Expected answer

支持配置多个数据库吗

Your Question

支持分库吗,即将分表分布在多个数据库上

The document you expected this should be explained

Expected answer

sharding key or id required, and use operator =

Your Question

sharding key or id required, and use operator =

sharding rules as follows

  • user_id use Snowflake id,such as 79535114761158382

  • md5(user_id)

  • get first one letter for schema name, then join wkread_users_, such as wkread_users_a

  • every database host has two schema, such as wkread_users_2,wkread_users_3in the db06 host

  • first two letters for table name, then join user_, such as tables user_30,user_31``user_32``user_33``user_34``user_35``user_36``user_37``user_38``user_39``user_3a``user_3b``user_3c``user_3d``user_3e``user_3f in the schema wkread_users_3

Sharding is as follows in my project

db05
  wkread_users_0
       user_00
       user_01
         ...
       user_0f
  wkread_users_1
       user_10
       user_11
         ...
       user_1f
db06
  wkread_users_2
       user_20
       user_21
         ...
       user_2f
  wkread_users_3
       user_30
       user_31
         ...
       user_3f
...
db12
  wkread_users_e
       user_e0
       user_e1
         ...
       user_ef
  wkread_users_f
       user_f0
       user_f1
         ...
       user_ff

database conf

default:
  master:
    Name: express
    Addr: 10.131.32.124:30001
    UserName: root
    Password: hhel0315
db05-wkread_users_0:
  master:
    Name: wkread_users_0
    Addr: 10.131.32.124:30005
    UserName: root
    Password: hhel0315
db05-wkread_users_1:
  master:
    Name: wkread_users_1
    Addr: 10.131.32.124:30005
    UserName: root
    Password: hhel0315
db06-wkread_users_2:
  master:
    Name: wkread_users_2
    Addr: 10.131.32.124:30006
    UserName: root
    Password: hhel0315
db06-wkread_users_3:
  master:
    Name: wkread_users_3
    Addr: 10.131.32.124:30006
    UserName: root
    Password: hhel0315
db07-wkread_users_4:
  master:
    Name: wkread_users_4
    Addr: 10.131.32.124:30007
    UserName: root
    Password: hhel0315
db07-wkread_users_5:
  master:
    Name: wkread_users_5
    Addr: 10.131.32.124:30007
db08-wkread_users_6:
  master:
    Name: wkread_users_6
    Addr: 10.131.32.124:30008
db08-wkread_users_7:
  master:
    Name: wkread_users_7
    Addr: 10.131.32.124:30008
db09-wkread_users_8:
  master:
    Name: wkread_users_8
    Addr: 10.131.32.124:30009
db09-wkread_users_9:
  master:
    Name: wkread_users_9
    Addr: 10.131.32.124:30009
db10-wkread_users_a:
  master:
    Name: wkread_users_a
    Addr: 10.131.32.124:30010
db10-wkread_users_b:
  master:
    Name: wkread_users_b
    Addr: 10.131.32.124:30010
db11-wkread_users_c:
  master:
    Name: wkread_users_c
    Addr: 10.131.32.124:30011
db11-wkread_users_d:
  master:
    Name: wkread_users_d
    Addr: 10.131.32.124:30011
db12-wkread_users_e:
  master:
    Name: wkread_users_e
    Addr: 10.131.32.124:30012
    UserName: root
    Password: hhel0315
db12-wkread_users_f:
  master:
    Name: wkread_users_f
    Addr: 10.131.32.124:30012

database dialector is mysql

func NewReadWriteSplits(cm map[string]ReadWriteSplitConfig) (db *gorm.DB) {
	defaultConfig := cm["default"]
	defaultMasterConfig := defaultConfig.Master

	dialector := NewDialectorWithPool(&defaultMasterConfig)

	db, err := gorm.Open(dialector, gormConfig(&defaultMasterConfig))
	if err != nil {
		log.Panicf("database connection failed. database name: %s, err: %+v", defaultMasterConfig.Name, err)
	}
	db.Set("gorm:table_options", "CHARSET=utf8mb4")
	// default
	resolver := dbresolver.Register(dbresolver.Config{
		Replicas: func(cs []Config) []gorm.Dialector {
			var ds []gorm.Dialector
			for _, c := range cs {
				ds = append(ds, NewDialectorWithPool(&c))
			}
			return ds
		}(defaultConfig.Slaves),
		TraceResolverMode: true,
	})
	// other
	for name, c := range cm {
		if name == "default" {
			break
		}
		resolver = resolver.Register(dbresolver.Config{
			Sources: func(c Config) []gorm.Dialector {
				ds := []gorm.Dialector{
					NewDialector(&c),
				}
				return ds
			}(c.Master),
			Replicas: func(cs []Config) []gorm.Dialector {
				var ds []gorm.Dialector
				for _, c := range cs {
					ds = append(ds, NewDialector(&c))
				}
				return ds
			}(c.Slaves),
			TraceResolverMode: true,
		}, name).SetMaxOpenConns(c.Master.MaxOpenConn).
			SetMaxIdleConns(c.Master.MaxIdleConn).
			SetConnMaxLifetime(c.Master.ConnMaxLifeTime).
			SetConnMaxIdleTime(c.Master.ConnMaxIdleTime)
	}

	db.Use(resolver)

	return db
}

query a user by uid code as follows

func (d *repository) GetUser(ctx context.Context, uid int64) (*model.User, error) {
	var data model.User
	resolverName := data.GetDBResolverName(uid)
	db := d.orm.WithContext(ctx).Clauses(dbresolver.Use(string(resolverName)))
	err := db.Use(sharding.Register(sharding.Config{
		ShardingKey:         "id",
		NumberOfShards:      16,
		PrimaryKeyGenerator: sharding.PKSnowflake,
		ShardingAlgorithmByPrimaryKey: func(id int64) (suffix string) {
			hashed, _ := utils.Md5(fmt.Sprintf("%d", id))
			fmt.Printf("id:%d, hashed:%s\n", id, hashed)
			return fmt.Sprintf("_%s", hashed[:2])
		},
	}, model.User{}))
	if err != nil {
		return nil, errors.Wrapf(err, "[repo.user] sharding.Register err")
	}
	err = db.First(&data, uid).Error
	if errors.Is(err, ErrNotFound) {
		return nil, ErrNotFound
	} else if err != nil {
		return nil, errors.Wrapf(err, "[repo.user] query db err")
	}
	return &data, nil
}

get an error when run

2023/02/13 20:13:11 /Users/zhengjb/go/src/gitlab.y5ops.com/jin/hertz_demo/biz/repository/user_repo.go:30 sharding key or id required, and use operator =
[0.141ms] [rows:0] [replica] SELECT * FROM `user` WHERE `user`.`id` = 79535114761158382 ORDER BY `user`.`id` LIMIT 1

The document you expected this should be explained

Can not db.Use used when db.Clauses is used ?

cannot use Clauses

Expected answer

get a user as expected. or how to do as described ?

does gorm sharding support JOIN ?

Your Question

in project when i use gorm sharding and my SQL contains JOIN, like A.col1, B.col2
the query will fail. the query trys to find the main table instead of sub table

The document you expected this should be explained

Expected answer

any help ? thanks

怎么自定义路由策略

比如我想根据主键id 自定义一个路由策略 根据一定的算法把数据路由到对应的数据库或者表 要怎么实现

panic: interface conversion: sharding.ShardingMigrator is not migrator.BuildIndexOptionsInterface: missing method BuildIndexOptions

GORM Playground Link

569

Description

detailed error as follows

2023/02/19 21:14:12 testing mysql...
panic: interface conversion: sharding.ShardingMigrator is not migrator.BuildIndexOptionsInterface: missing method BuildIndexOptions

goroutine 1 [running]:
gorm.io/gorm/migrator.Migrator.CreateTable.func1(0x14000484380)
	/Users/zhengjb/go/src/github.com/zishiguo/playground/gorm/migrator/migrator.go:247 +0x568
gorm.io/gorm/migrator.Migrator.RunWithValue({{0x0?, 0x14000271500?, {0x105414100?, 0x1400023c000?}}}, {0x105330f80?, 0x1400023c230}, 0x140000e3540)
	/Users/zhengjb/go/src/github.com/zishiguo/playground/gorm/migrator/migrator.go:62 +0x13c
gorm.io/gorm/migrator.Migrator.CreateTable({{0x70?, 0x14000271500?, {0x105414100?, 0x1400023c000?}}}, {0x140004803c0?, 0x140004803c0?, 0x0?})
	/Users/zhengjb/go/src/github.com/zishiguo/playground/gorm/migrator/migrator.go:198 +0x114
gorm.io/gorm/migrator.Migrator.AutoMigrate({{0x0?, 0x14000270720?, {0x105414100?, 0x1400023c000?}}}, {0x1400021bad0?, 0x105311aa0?, 0x1400023c001?})
	/Users/zhengjb/go/src/github.com/zishiguo/playground/gorm/migrator/migrator.go:112 +0x244
gorm.io/sharding.ShardingMigrator.AutoMigrate({{0x105417ca8, 0x14000270690}, 0x140002368f0, {0x105413e40, 0x14000010bd0}}, {0x1400021b770?, 0x0?, 0x0?})
	/Users/zhengjb/go/pkg/mod/gorm.io/sharding@v0.5.2/dialector.go:45 +0x308
gorm.io/gorm.(*DB).AutoMigrate(0x140000017a0?, {0x1400021b770, 0x1, 0x1})
	/Users/zhengjb/go/src/github.com/zishiguo/playground/gorm/migrator.go:28 +0x44
gorm.io/playground.RunMigrations()
	/Users/zhengjb/go/src/github.com/zishiguo/playground/db.go:113 +0x194
gorm.io/playground.init.0()
	/Users/zhengjb/go/src/github.com/zishiguo/playground/db.go:37 +0x108

Process finished with the exit code 1

替换sql, 让日志中记录真实执行的sql

Describe the feature

替换原sql, gorm 追踪记录新的sql内容

Motivation

希望执行分表后生成新的sql记录到日志中,而不是原来的sql。

Related Issues

conn_pool.go

type ConnPool struct {
	// db, This is global db instance
	sharding *Sharding
	gorm.ConnPool
	currentDB *gorm.DB // 记录当前的db实例
}


func (pool ConnPool) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) {
// ....
        pool.sharding.querys.Store("last_query", stQuery)
        // 重置sql
	oldSqlCap := pool.currentDB.Statement.SQL.Cap()
	pool.currentDB.Statement.SQL.Reset()
	pool.currentDB.Statement.SQL.Grow(oldSqlCap + 5)
	pool.currentDB.Statement.SQL.WriteString(stQuery)
// ...
}



func (pool ConnPool) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) {
// ...
        pool.sharding.querys.Store("last_query", stQuery)
        // 重置sql
	oldSqlCap := pool.currentDB.Statement.SQL.Cap()
	pool.currentDB.Statement.SQL.Reset()
	pool.currentDB.Statement.SQL.Grow(oldSqlCap + 5)
	pool.currentDB.Statement.SQL.WriteString(stQuery)
// ...
}

sharding.go

func (s *Sharding) switchConn(db *gorm.DB) {
	// Support ignore sharding in some case, like:
	// When DoubleWrite is enabled, we need to query database schema
	// information by table name during the migration.
	if _, ok := db.Get(ShardingIgnoreStoreKey); !ok {
		s.ConnPool = &ConnPool{ConnPool: db.Statement.ConnPool, sharding: s, currentDB: db} // 记录当前的db实例
		db.Statement.ConnPool = s.ConnPool
	}
}

full text query macth not work

_ = db.Use(sharding.Register(sharding.Config{
		ShardingKey:         "ymd",
		NumberOfShards:      100,
		PrimaryKeyGenerator: sharding.PKCustom,
		ShardingAlgorithm: func(columnValue any) (suffix string, err error) {
			parentId := cast.ToInt(columnValue)
			suffix = fmt.Sprintf("_%v", parentId)
			return
		},
		PrimaryKeyGeneratorFn: func(tableIdx int64) int64 {
			return 0
		},
		// This case for show up give notifications, audit_logs table use same sharding rule.
	}, ChatMsg{}))

qb.Where(fmt.Sprintf("(MATCH(body) AGAINST('%s'))", req.Key))

can not fond table

是否可以支持注册不同的shardingkey 分表策略

Describe the feature

sharding.Register func will be get the same plugin name for sharding,so different sharding-key column could not register successfully when invoking db.USE()

Motivation

The same database has multiple tables with different shardingkey rules

Related Issues

#62

Is it possible to support setting plugins name to solve the problem of different shardingKey rules for multiple tables in the same database?

Example in README won't work: Registering multiple Config

Description

db.Use(sharding.Register(sharding.Config{
    ShardingKey:         "user_id",
    NumberOfShards:      64,
    PrimaryKeyGenerator: sharding.PKSnowflake,
}, "orders").Register(sharding.Config{
    ShardingKey:         "user_id",
    NumberOfShards:      256,
    PrimaryKeyGenerator: sharding.PKSnowflake,
    // This case for show up give notifications, audit_logs table use same sharding rule.
}, Notification{}, AuditLog{}))

This example won't work since Register return a struct which hasn't implemented the Register method.

How to backfill primary key when use this plugin?

Your Question

type User struct{
  ID int64 `gorm:"column:id;primaryKey"`
  UserId int64 `gorm:"column:user_id"`
}

shard, err := sharding.Register(sharding.Config{
  ShardingKey:         "user_id",
  NumberOfShards:      1,
  PrimaryKeyGenerator: sharding.PKSnowflake,
  ShardingAlgorithm:   getShardFn(1),
}, "users"))
if err != nil {
  return nil, err
}

if err := db.Use(shard); err != nil {
  return nil, err
}

result := db.Create(&user) // pass pointer of data to Create

user.ID             // 0

The document you expected this should be explained

Expected answer

Support for custom schema name in Postgres

Right now sharding is creating tables under public schema while we use custom name so it fail when there is no public already created. Please consider adding support for custom schema names in Postgres.

Refactor Config API same as dbresolver

db.Use(sharding.Register(sharding.Config{
		ShardingKey: "user_id",
		ShardingAlgorithm: func(value interface{}) (suffix string, err error) {
				if uid, ok := value.(int64); ok {
						return fmt.Sprintf("_%02d", uid%4), nil
				}
				return "", errors.New("invalid user_id")
		},
		PrimaryKeyGenerate: func(tableIdx int64) interface{} {
				// use Built in keygen for generate a sequence primary key with table index
				return keygen.Next(tableIdx)
		}
}, "orders").Register(sharding.Config{
		ShardingKey: "user_id",
		ShardingAlgorithm: func(value interface{}) (suffix string, err error) {
				if uid, ok := value.(int64); ok {
						return fmt.Sprintf("_%02d", uid%4), nil
				}
				return "", errors.New("invalid user_id")
		},
		PrimaryKeyGenerate: func(tableIdx int64) interface{} {
				return uuid.UUID()
		}
		// This case for show up give notifications, activities table use same sharding rule.
}, Notification{}, Acitivity{}))

如何注册不同的shardingkey 分表策略

Your Question

The document you expected this should be explained

Expected answer

Hi,
目前有需求是注册不同类型的shardingkey 切片表, gorm (db.Use()) 如果多次使用相同name的插件会被返回已注册.
请问该如何实现, 谢谢

Rename EnableFullTable for a better name.

EnableFullTable is means double write in to main table for keep all records have a duplication version in main table.

This feature helpful for:

  • Admin console to list all records, without sharding key query.
  • Index page to get recent created records.

Occur DATA RACE when use sharding in concurrency

GORM Playground Link

go-gorm/playground#567

Description

Use go-gorm/sharding in concurrency, occur DATA RACE sometimes on production environment.

The code below may occur DATA RACE:

func (s *Sharding) switchConn(db *gorm.DB) {
	// Support ignore sharding in some case, like:
	// When DoubleWrite is enabled, we need to query database schema
	// information by table name during the migration.
	if _, ok := db.Get(ShardingIgnoreStoreKey); !ok {
		s.ConnPool = &ConnPool{ConnPool: db.Statement.ConnPool, sharding: s}
		db.Statement.ConnPool = s.ConnPool
	}
}

Update:

func (s *Sharding) switchConn(db *gorm.DB) {
	// Support ignore sharding in some case, like:
	// When DoubleWrite is enabled, we need to query database schema
	// information by table name during the migration.
	if _, ok := db.Get(ShardingIgnoreStoreKey); !ok {
		s.mutex.Lock()
		if db.Statement.ConnPool != nil {
			if _, ok := db.Statement.ConnPool.(ConnPool); !ok {
				db.Statement.ConnPool = &ConnPool{ConnPool: db.Statement.ConnPool, sharding: s}
			}
		}
		s.mutex.Unlock()
	}
}

mysql 驱动没有回填snowflake id

GORM Playground Link

go-gorm/playground#1

Description

#37

Env:
go 1.16
mysql: 5.7.28
sharding: 0.5.2
gorm: 1.24.3

package main

import (
	"fmt"

	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/sharding"
)

type User struct {
	ID     int64 `gorm:"column:id;primaryKey"`
	UserId int64 `gorm:"column:user_id"`
}

func main() {
	dsn := "username:pwd@tcp(host:3306)/database_name?charset=utf8mb4&parseTime=true&loc=Local"
	db, _ := gorm.Open(mysql.New(mysql.Config{DSN: dsn}))

	shard := sharding.Register(sharding.Config{
		ShardingKey:         "user_id",
		NumberOfShards:      2,
		PrimaryKeyGenerator: sharding.PKSnowflake,
	}, "users")

	db.Use(shard)

	user := User{UserId: 42}
	err := db.Create(&user).Error // nil
	if err != nil {
		panic(err)
	}

	fmt.Println(user.ID) // 0
}

debug code

gorm: callbacks/create.go
Create() {
     insertID, err := result.LastInsertId() // 0
     insertOk := err == nil && insertID > 0
}

// return 0
func (res *mysqlResult) LastInsertId() (int64, error) {
	return res.insertId, nil
}

i test example with Mysql, i use Model to use CURD Interface, it return error

Your Question

I try to modify the example code to test MySQL sharding

But when I use model interface to access, there will be an error

I change it to Table, is OK

I think this issue like #14 ?

my env:

  • go1.17.1 darwin/amd64
    • dependency
      • gorm.io/driver/mysql v1.3.4
      • gorm.io/gorm v1.23.6
      • github.com/bwmarrin/snowflake v0.3.0 // indirect
      • github.com/longbridgeapp/sqlparser v0.3.1 // indirect
      • github.com/technoweenie/multipartstreamer v1.0.1 // indirect
      • gorm.io/sharding v0.5.1 // indirect
  • mysql:8.0.23

examples/order.go

package main

import (
	"fmt"

	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"gorm.io/gorm/schema"
	"gorm.io/sharding"
)

type Order struct {
	ID        int64 `gorm:"primarykey"`
	UserID    int64
	ProductID int64
}

func main() {
	connection := fmt.Sprintf("test:test@tcp(127.0.0.1:3306)/?parseTime=true")
	db, err := gorm.Open(mysql.New(mysql.Config{
		DSN: connection,
	}), &gorm.Config{
		NamingStrategy: schema.NamingStrategy{
			SingularTable: true,
		},
		Logger: logger.Default.LogMode(logger.Silent),
	})
	if err != nil {
		fmt.Println("err", err)
	}

	db.Exec("USE " + "MyTable")

	for i := 0; i < 4; i += 1 {
		table := fmt.Sprintf("orders_%d", i)
		db.Exec(`DROP TABLE IF EXISTS ` + table)
		db.Table(table).AutoMigrate(&Order{})
	}
	// db.AutoMigrate(&Order{})

	middleware := sharding.Register(sharding.Config{
		ShardingKey:         "user_id",
		NumberOfShards:      4,
		PrimaryKeyGenerator: sharding.PKSnowflake,
	}, "orders")
	db.Use(middleware)

	// this record will insert to orders_02
        // !!!!!!!!!!!!!!!!!!  it returm err: Error 1146: Table 'MyTable.order' doesn't exist
	// err = db.Create(&Order{UserID: 2}).Error
	err = db.Table("orders").Create(&Order{UserID: 2}).Error
	if err != nil {
		fmt.Println("err1", err)
	}

	// this record will insert to orders_03
	err = db.Exec("INSERT INTO orders(user_id) VALUES(?)", int64(3)).Error
	if err != nil {
		fmt.Println("err2", err)
	}

	// this will throw ErrMissingShardingKey error
	err = db.Exec("INSERT INTO orders(product_id) VALUES(1)").Error
	fmt.Println("err3", err)

	// this will redirect query to orders_02
	var orders []Order
        // !!!!!!!!!!!!!!!!!!  it returm err: Error 1146: Table 'MyTable.order' doesn't exist
	// err = db.Model(&Order{}).Where("user_id", int64(3)).Find(&orders).Error
	err = db.Table("orders").Where("user_id", int64(2)).Find(&orders).Error
	if err != nil {
		fmt.Println("err4", err)
	}
	fmt.Printf("%#v\n", orders)

	// Raw SQL also supported
	db.Raw("SELECT * FROM orders WHERE user_id = ?", int64(3)).Scan(&orders)
	fmt.Printf("%#v\n", orders)

	// this will throw ErrMissingShardingKey error
        // !!!!!!!!!!!!!!!!!!  it returm err: Error 1146: Table 'MyTable.order' doesn't exist
	// err = db.Model(&Order{}).Where("product_id", "1").Find(&orders).Error
	err = db.Table("orders").Where("product_id", "1").Find(&orders).Error
	fmt.Println("err5", err)

	// Update and Delete are similar to create and query
	err = db.Exec("UPDATE orders SET product_id = ? WHERE user_id = ?", 2, int64(3)).Error
	fmt.Println("err6", err) // nil
	err = db.Exec("DELETE FROM orders WHERE product_id = 3").Error
	fmt.Println("err7", err) // ErrMissingShardingKey
}

see // !!!!!!!!!!!!!!!!!! it returm err: Error 1146: Table 'MyTable.order' doesn't exist

The document you expected this should be explained

Expected answer

i want use Model to access, not Table

sharding plugin seems not compatable with gorm.Model well.

my code works well befor using sharding middleware:

func (r *MySQLRepo) PlanExists(id uint) (bool, error) {
    var exists bool
    err := r.db.Model(&entity.StockPlan{}).
        Select("count(*) > 0").
        Where("id = ?", id).
        Find(&exists).
        Error
    return exists, err
}

but the error occurs after registering sharding like this:

db.Use(sharding.Register(sharding.Config{
    ShardingKey:         "title",
    NumberOfShards:      4,
    PrimaryKeyGenerator: sharding.PKSnowflake,
}, "stock_plans"))

and the error is:

Error 1054: Unknown column 'stock_plans.deleted_at' in 'where clause'

sharding key or id required, and use operator =

GORM Playground Link

go-gorm/playground#1

Description

When i use preload on sharding table, the return "msg sharding key or id required, and use operator ="

type A struct {
ID string
BS []B
}

type B struct {
ID string
AID string
}

var aModel A

db.Model(&A{}).Preload("B").Find(&aModel)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.