incubator-seata-go
incubator-seata-go copied to clipboard
XA 相同的DB执行两条sql时报错
What happened: XA 一个事务里跑多个sql不能正常执行 What you expected to happen: XA 一个事务里跑多个sql可以正常执行
How to reproduce it (as minimally and precisely as possible):
使用xa basic sample里代码, 重复执行 db.ExecContext两次, 报错
func insertData(ctx context.Context) error {
fmt.Printf("insert start \n\n\n")
sql := "INSERT INTO `order_tbl` ( `user_id`, `commodity_code`, `count`, `money`, `descs`) VALUES (?, ?, ?, ?, ?);"
ret, err := db.ExecContext(ctx, sql, "NO-100001", "C100000", 100, nil, "init desc")
if err != nil {
fmt.Printf("insert failed, err:%v\n", err)
return err
}
rows, err := ret.RowsAffected()
if err != nil {
fmt.Printf("insert failed, err:%v\n", err)
return err
}
fmt.Printf("insert success 1: %d.\n\n\n", rows)
sql = "INSERT INTO `order_tbl` ( `user_id`, `commodity_code`, `count`, `money`, `descs`) VALUES (?, ?, ?, ?, ?);"
ret, err = db.ExecContext(ctx, sql, "NO-100001", "C100000", 100, nil, "init desc")
if err != nil {
fmt.Printf("insert failed, err:%v\n", err)
return err
}
rows, err = ret.RowsAffected()
if err != nil {
fmt.Printf("insert failed, err:%v\n", err)
return err
}
fmt.Printf("insert success 2: %d.\n\n\n", rows)
return nil
}
func sampleInsert(ctx context.Context) {
tm.WithGlobalTx(ctx, &tm.GtxConfig{
Name: "XASampleLocalGlobalTx_Insert",
Timeout: time.Second * 30,
}, insertData)
}
报错, panic
should NEVER happen: setAutoCommit from true to false while xa branch is active
Anything else we need to know?:
// BeginTx like common transaction. but it just exec XA START
func (c *XAConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
if !tm.IsGlobalTx(ctx) {
tx, err := c.Conn.BeginTx(ctx, opts)
return tx, err
}
c.autoCommit = false
c.txCtx = types.NewTxCtx()
c.txCtx.DBType = c.res.dbType
c.txCtx.TxOpt = opts
c.txCtx.ResourceID = c.res.resourceID
c.txCtx.XID = tm.GetXID(ctx)
c.txCtx.TransactionMode = types.XAMode
tx, err := c.Conn.BeginTx(ctx, opts)
if err != nil {
return nil, err
}
c.tx = tx
if !c.autoCommit {
if c.xaActive {
return nil, errors.New("should NEVER happen: setAutoCommit from true to false while xa branch is active")
}
baseTx, ok := tx.(*Tx)
if !ok {
return nil, fmt.Errorf("start xa %s transaction failure for the tx is a wrong type", c.txCtx.XID)
}
c.branchRegisterTime = time.Now()
if err := baseTx.register(c.txCtx); err != nil {
c.cleanXABranchContext()
return nil, fmt.Errorf("failed to register xa branch %s, err:%w", c.txCtx.XID, err)
}
c.xaBranchXid = XaIdBuild(c.txCtx.XID, c.txCtx.BranchID)
c.keepIfNecessary()
if err = c.start(ctx); err != nil {
c.cleanXABranchContext()
return nil, fmt.Errorf("failed to start xa branch xid:%s err:%w", c.txCtx.XID, err)
}
c.xaActive = true
}
return &XATx{tx: tx.(*Tx)}, nil
}
这里的逻辑好像定死了一个XAConn 不能区分两次不同的sql tx. c.autoCommit = false 默认为false,c.xaActive = true 执行一次,这个(c *XAConn) BeginTx 就无法再次执行。