问题
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