bulkwrite does not support multi-document transaction by using mongo-go-driver

自作多情 提交于 2019-12-25 01:09:11

问题


I's using mongo-go-driver 0.0.18 to build a bulk write which is consisted of a "NewUpdateManyModel" and several "NewInsertOneModel". My mongo server is atlas M10 with replica sets. I built some goroutines to test if the transactions are atomic, the result shows that each bulk write is not atomic, they would interfered with each other. I am wondering if mongo-go-driver supports for multi-document transaction?

func insertUpdateQuery(counter int, col *mongo.Collection, group *sync.WaitGroup){
var operations []mongo.WriteModel
var items = []item{}
items=append(items,item{"Name":strconv.Itoa(counter),"Description":"latest one"})

for _,v := range items{
    operations = append(operations, mongo.NewInsertOneModel().Document(v))
}

updateOperation := mongo.NewUpdateManyModel()
updateOperation.Filter(bson.D{
    {"Name", bson.D{
        {"$ne", strconv.Itoa(counter)},
    }},
})
updateOperation.Update(bson.D{
    {"$set", bson.D{
        {"Description", strconv.Itoa(counter)},
    }},
},)
operations = append(operations,updateOperation)

bulkOps:=options.BulkWrite()
result, err := col.BulkWrite(
    context.Background(),
    operations,
    bulkOps,
)
if err != nil{
    fmt.Println("err:",err)
}else{
    fmt.Printf("IU: %+v \n",result)
}

group.Done()

}


func retrieveQuery(group *sync.WaitGroup, col *mongo.Collection){
var results []item
qctx:=context.Background()
qctx, c := context.WithTimeout(qctx, 10*time.Second)
defer c()
cur, err := col.Find(qctx, nil)
if err != nil {
    log.Fatal(err)
}
defer cur.Close(context.Background())
res := item{}
for cur.Next(context.Background()) {
    err := cur.Decode(&res)
    if err != nil {
        log.Println(err)
    }else {
        results=append(results,res)
    }
}
if err := cur.Err(); err != nil {
    log.Println(err)
}

fmt.Println("res:",results)

group.Done()
}



func main()  {
ctx := context.Background()
ctx, cancel := context.WithTimeout(ctx,10*time.Second)
defer cancel()

uri := "..."
client, err := mongo.NewClient(uri)
if err != nil {
    fmt.Printf("todo: couldn't connect to mongo: %v", err)
}
defer cancel()
err = client.Connect(ctx)
if err != nil {
    fmt.Printf("todo: mongo client couldn't connect with background context: %v", err)
}
col:=client.Database("jistest").Collection("Rules")

wg :=&sync.WaitGroup{}

for i:=0; i<100; i++{
    wg.Add(2)
    go insertUpdateQuery(i,col,wg)
    go retrieveQuery(wg,col)

}
wg.Wait()
fmt.Println("All Done!")
}

回答1:


I am wondering if mongo-go-driver supports for multi-document transaction?

mongo-go-driver does support multi-document transaction since v0.0.12 (currently in beta version 0.1.0).

MongoDB multi-document transactions are associated with a session. That is, you start a transaction for a session. When using any of MongoDB officially supported drivers, you must pass the session to each operation in the transaction.

Your example does not seem to utilise session nor transactions. An example of multi-document transaction in mongo-go-driver (v0.1.0) is as below:

client, err := mongo.NewClient("<MONGODB URI>")
if err != nil { return err }
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
defer cancel()
err = client.Connect(ctx)
if err != nil { return err }

session, err := client.StartSession()
database := client.Database("databaseName")
collection := database.Collection("collectionName")

err = mongo.WithSession(ctx, session, func(sctx mongo.SessionContext) error {
        // Start a transaction in the session 
        sctx.StartTransaction()

        var operations []mongo.WriteModel
        // Create an insert one operation
        operations = append(operations,
                            mongo.NewInsertOneModel().Document(
                            bson.D{{"Name", counter}, 
                                   {"Description", "latest"}}))

        // Create an update many operation
        updateOperation := mongo.NewUpdateManyModel()
        updateOperation.Filter(bson.D{{"Name", bson.D{
            {"$ne", counter},
        }}})
        updateOperation.Update(bson.D{
            {"$set", bson.D{
                {"Description", counter},
            }},
        })
        operations = append(operations, updateOperation)

        // Execute bulkWrite operation in a transactional session.
        _, err := collection.BulkWrite(sctx, operations)
        if err != nil {
            fmt.Println(err)
            return err
        }
        // Committing transaction
        session.CommitTransaction(sctx)
        return nil
})
session.EndSession(ctx)

See also Transactions and Retryable Writes for examples to retry transactions.

I built some goroutines to test if the transactions are atomic

Just be mindful about how the processes are executed. For example, depending on the racing condition, you may get the latest performance overwriting the result. i.e.

transaction 1 finished 
transaction 2 finished 
transaction 3 and transaction 4 conflict 
transaction 5 finished
... 


来源:https://stackoverflow.com/questions/53621036/bulkwrite-does-not-support-multi-document-transaction-by-using-mongo-go-driver

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!