我正在学习使用Golang驱动程序go.mongodb.org/mongo-driver/mongo进行MongoDB事务处理.我正沿着this SO answer号和this example on github号路行驶.

示例代码由@ siphix给出:

if session, err = client.StartSession(); err != nil {
    t.Fatal(err)
}
if err = session.StartTransaction(); err != nil {
    t.Fatal(err)
}
if err = mongo.WithSession(ctx, session, func(sc mongo.SessionContext) error {
    if result, err = collection.UpdateOne(sc, bson.M{"_id": id}, update); err != nil {
        t.Fatal(err)
    }
    if result.MatchedCount != 1 || result.ModifiedCount != 1 {
        t.Fatal("replace failed, expected 1 but got", result.MatchedCount)
    }

    // more interrelated operations ...

    if err = session.CommitTransaction(sc); err != nil {
        t.Fatal(err)
    }
    return nil
}); err != nil {
    t.Fatal(err)
}
session.EndSession(ctx)

在这两个示例中,它们都不会在出错的情况下回滚.我知道这是一个用于演示的样本.但当我在我的代码中执行同样的操作时,它工作得很好.

在出错的情况下省略回滚可以吗(驱动程序能处理吗)?还是我错过了什么?

推荐答案

mongo.WithSession()不假定有任何活动事务,它"仅"帮助在给定会话下运行回调.因此,如果您希望它作为已启动事务的一部分执行,您应该自己处理提交和中止.这允许更精细的控制.

但是,如果您打算将回调作为事务运行,则使用Session.WithTransaction()要简单得多,因为它以一种透明的方式处理事务及其生命周期:它创建事务并根据回调返回的错误提交或中止事务.作为额外功能,它还可以处理重试.正如其文件还指出的那样:

如果回调失败,驱动程序将调用AbortTransaction.

下面是一个如何在事务中正确执行回调的简单示例:

var docToInsert, idToUpdate, updateDoc any

func doInTransactionExample(ctx context.Context, client *mongo.Client) error {
    sess, err := client.StartSession(options.Session().SetDefaultReadConcern(readconcern.Majority()))
    if err != nil {
        return fmt.Errorf("client.StartSession() error: %w", err)
    }
    defer sess.EndSession(ctx)

    result, err := sess.WithTransaction(
        ctx,
        func(sessCtx mongo.SessionContext) (any, error) {
            // sessCtx must be used as context.Context for all operations to be run in the transaction.
            var ctx context.Context = sessCtx // Shadow ctx on purpose!

            c := client.Database("foo").Collection("bar")

            // Insert example
            if _, err := c.InsertOne(ctx, docToInsert); err != nil {
                return nil, fmt.Errorf("InsertOne() failed: %w", err)
            }

            // Update example
            if ur, err := c.UpdateByID(ctx, idToUpdate, updateDoc); err != nil {
                return nil, fmt.Errorf("UpdateByID() failed: %w", err)
            } else {
                if ur.MatchedCount == 0 {
                    return nil, fmt.Errorf("UpdateByID() failed: %w", mongo.ErrNoDocuments)
                }
            }

            return "arbitrary-result-to-return", nil
        },
        options.Transaction().SetReadPreference(readpref.PrimaryPreferred()),
    )

    if err != nil {
        return fmt.Errorf("sess.WithTransaction() error: %w", err)
    }

    _ = result // Not using result

    return nil
}

Mongodb相关问答推荐

在一个视图中连接两个集合MongoDB;展开有什么作用?

如何从MongoDB集合中获取第一个和最后一个元素?

MongoDB合并按键更新值的对象数组

数组字段包含其他字段的数组值

如何在MongoSH中的现有文档中插入字段

在mongdob中按子文档筛选不起作用

从 Amazon S3(Next.js、Mongodb、Mongoose)删除图像

管道聚合mongodb同一$project阶段的计算字段?

System.FormatException occurred in MongoDB.Bson.dll - XXX is not a valid 24 digit hex string

MongoDB聚合多条件

更新 MongoDB 中嵌套实体数组中的属性

MongoDB映射/减少多个集合?

MongoDB 1.6.5:如何重命名集合中的字段

Clojure 和 NoSQL 数据库

不能在模块外使用 import 语句

Mongoose 是否真的验证了对象 ID 的存在?

MongoDB 日志(log)文件和 oplog 有何不同?

mongodb启动错误

初创公司应该考虑哪些数据库系统?

如何在第一个文档中恢复 MongoDB ChangeStream 而不仅仅是在我开始收听后更改