diff --git a/model/t_uid/t_uid.go b/model/t_uid/t_uid.go new file mode 100644 index 0000000..48a196e --- /dev/null +++ b/model/t_uid/t_uid.go @@ -0,0 +1,139 @@ +package t_uid + +import ( + "fmt" + "time" + + "gorm.io/gorm" + "gorm.io/gorm/clause" + "qoobing.com/gomod/log" +) + +type t_uid struct { + F_id int64 `gorm:"column:F_id;primaryKey"` // ID范围id + F_pid int64 `gorm:"column:F_pid"` // ID范围的前一个范围id + F_type string `gorm:"column:F_type"` // ID类型 + F_prefix string `gorm:"column:F_prefix"` // ID前缀(type+prefix+range_start唯一) + F_range_start int64 `gorm:"column:F_range_start"` // ID范围的起始id + F_range_length int `gorm:"column:F_range_length"` // ID范围的长度 + F_range_owner string `gorm:"column:F_range_owner"` // ID范围的拥有者,一般是机器id + F_create_time time.Time `gorm:"column:F_create_time"` // 记录创建时间 +} + +type typeInfo struct { + Name string //type name + RangeLength int //Range length +} + +var table string = "t_uid" // id range table name +var typeSupported = map[string]typeInfo{ + "TXID": typeInfo{ + Name: "TXID", + RangeLength: 3, + }, + "USERID": typeInfo{ + Name: "USERID", + RangeLength: 3, + }, +} + +func Init(tablename string) { + table = tablename +} + +func (id *t_uid) TableName() string { + return table +} + +func (id *t_uid) BeforeCreate(db *gorm.DB) error { + id.F_create_time = time.Now() + return nil +} + +func (id *t_uid) BeforeUpdate(db *gorm.DB) (err error) { + return nil +} + +func CreateIdRange(db *gorm.DB, typ, prefix string) (start int64, length int, err error) { + // Step 1. init variables + if t, ok := typeSupported[typ]; !ok { + panic(fmt.Sprintf("id type [%s] not supported yet", typ)) + } else { + length = t.RangeLength + } + var ( + lock = clause.Locking{Strength: "UPDATE"} + root = t_uid{} + rootcond = map[string]interface{}{ + "F_type": fmt.Sprintf("%s#ROOT", typ), + "F_prefix": prefix, + "F_pid": 0, + } + rootattr = map[string]interface{}{ + "F_range_start": -length, + "F_range_length": length, + "F_range_owner": "0", + } + selfid = "TODO" + ) + + // Step 2. get lastid && lock root(type+prefix) + var tx = db.Begin() + var txsuccess = false + defer func() { + if !txsuccess { + tx.Rollback() + } + }() + txr := tx.Clauses(lock). + Where(rootcond). + Attrs(rootattr). + FirstOrCreate(&root) + if txr.Error != nil { + return 0, 0, txr.Error + } + if root.F_range_owner == "0" { + // for new created root we set owner to itself. + root.F_range_owner = fmt.Sprintf("%d", root.F_id) + } + log.PrintPretty("root:", root) + + // Step 3. insert new range. [can DELETE this for perfermance] + var newrange = t_uid{ + F_type: typ, + F_prefix: prefix, + F_range_start: root.F_range_start + int64(length), + F_range_length: length, + F_range_owner: selfid, + } + if n, e := fmt.Sscanf(root.F_range_owner, "%d", &newrange.F_pid); e != nil { + log.Debugf("sscanf F_range_owner(%s) failed: '%s'", root.F_range_owner, e) + return 0, 0, fmt.Errorf("range_owner error") + } else if n != 1 { + log.Debugf("sscanf F_range_owner(%s) failed: n=%d", root.F_range_owner, n) + return 0, 0, fmt.Errorf("range_owner error") + } + if txr := tx.Create(&newrange); txr.Error != nil { + log.PrintPretty("newrange:", newrange) + return 0, 0, txr.Error + } + + // Step 4. update root + root.F_range_start = newrange.F_range_start + root.F_range_length = newrange.F_range_length + root.F_range_owner = fmt.Sprintf("%d", newrange.F_id) + if txr := tx.Save(&root); txr.Error != nil { + return 0, 0, txr.Error + } + + // Step 5. commit and return + if txr := tx.Commit(); txr.Error != nil { + return 0, 0, txr.Error + } + + txsuccess = true + start = newrange.F_range_start + length = newrange.F_range_length + log.Infof("create range success:(%s, %s, %d, %d)", typ, prefix, start, length) + return start, length, nil +}