MongoDB独立模式下事务未回滚的原因及解决方法求助
独立模式下MongoDB事务未回滚的原因及解决方法
问题场景
需要在MongoDB中实现多语句事务,达成「要么全部成功,要么全部失败」的效果。当前trxTest数据库的users集合已有如下文档:
trxTest> db.users.find({}) [ { _id: ObjectId("64ad882f451d9a5fc930eeb7"), name: 'werain' } ]
name字段已设置唯一索引,尝试在事务中插入多个文档,最后一个文档的name为werain会触发唯一键冲突。预期最后一个插入失败时,事务回滚前面插入的vikas和sriram文档,但实际执行后这两个文档仍存在于集合中。
问题代码(Go语言)
client, err := mongo.Connect(context.Background(), options.Client().ApplyURI("mongodb://localhost:27017")) if err != nil { panic(err) } DB := client.Database("trxTest") coll1 := DB.Collection("users") session, err := client.StartSession() if err != nil { panic(err) } defer session.EndSession(context.Background()) _, err = session.WithTransaction(context.Background(), func(ctx mongo.SessionContext) (interface{}, error) { _, err = coll1.InsertOne(context.Background(), bson.M{"name": "vikas"}) if err != nil { return nil, err } fmt.Println("vikas is inserted") _, err = coll1.InsertOne(context.Background(), bson.M{"name": "sriram"}) if err != nil { return nil, err } fmt.Println("sriram is inserted") fmt.Println("inserting is werain again") _, err = coll1.InsertOne(context.Background(), bson.M{"name": "werain"}) // 此处应触发事务失败 if err != nil { return nil, err } fmt.Println("werain is inserted") return nil, nil }, nil) if err != nil { fmt.Println("got trx error", err) }
实际执行结果
trxTest> db.users.find({}) [ { _id: ObjectId("64ad882f451d9a5fc930eeb7"), name: 'werain' }, { _id: ObjectId("64ad8c65f63f3f2b48f66169"), name: 'vikas' }, { _id: ObjectId("64ad8c66f63f3f2b48f6616a"), name: 'sriram' } ]
报错信息
vikas is inserted sriram is inserted inserting is werain again got error write exception: write errors: [E11000 duplicate key error collection: trxTest.users index: name_1 dup key: { name: "werain" }]
MongoDB版本信息
db version v6.0.1 Build Info: { "version": "6.0.1", "gitVersion": "32f0f9c88dc44a2c8073a5bd47cf779d4bfdee6b", "modules": [], "allocator": "system", "environment": { "distarch": "aarch64", "target_arch": "aarch64" } }
原因分析
核心问题是事务回调内的所有InsertOne操作,传入的上下文是context.Background()而非事务提供的SessionContext(即回调参数ctx)。MongoDB的事务操作必须绑定到事务上下文,否则每个插入都会作为独立的非事务操作执行,不受事务管理,自然不会触发回滚。
解决方法
将事务回调中所有InsertOne调用的上下文参数替换为回调的ctx,修正后的代码如下:
_, err = session.WithTransaction(context.Background(), func(ctx mongo.SessionContext) (interface{}, error) { _, err = coll1.InsertOne(ctx, bson.M{"name": "vikas"}) if err != nil { return nil, err } fmt.Println("vikas is inserted") _, err = coll1.InsertOne(ctx, bson.M{"name": "sriram"}) if err != nil { return nil, err } fmt.Println("sriram is inserted") fmt.Println("inserting is werain again") _, err = coll1.InsertOne(ctx, bson.M{"name": "werain"}) // 此处触发事务失败 if err != nil { return nil, err } fmt.Println("werain is inserted") return nil, nil }, nil)
验证结果
修改后执行,当最后一个插入触发唯一键冲突时,事务会自动回滚,users集合中仅保留初始的werain文档,不会出现vikas和sriram。
内容的提问来源于stack exchange,提问作者Vicky
相关产品推荐
相关产品推荐

