我确信我遗漏了一些东西,但我无法使以下简单事务按预期工作。这种行为与我能找到的所有其他SO问题都不同。
下面的函数MultipleInsertsTransaction()
受到了官方示例的启发
它成功地写入了一个文档,然后尝试写入第二个文档,结果返回错误,因为(故意(再次使用了相同的ID。
我的理解是,这些文档都没有持久化在数据库中,因为我们从未到达sc.CommitTransaction(sc)
,所以StartTransaction()
和AbortTransaction()
中的所有操作都应该回滚,或者甚至对其他会话都不可见。
然而,事实并非如此。第一个文档被写入,第二个文档如预期那样抛出错误,但在函数返回后,第一个文档将被保留在数据库中。
此交易有什么问题?我错过了什么?或者这是意料之中的事
package main
import (
"context"
"fmt"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/mongo/readconcern"
"go.mongodb.org/mongo-driver/mongo/writeconcern"
)
const (
db = "test"
coll = "test"
)
func main() {
client, _ := mongo.Connect(context.Background(), options.Client().ApplyURI("<put replica set connection string here>"))
want, _ := client.Database(db).Collection(coll).CountDocuments(context.Background(), bson.M{})
if err := MultipleInsertsTransaction(context.Background(), client); err != nil {
fmt.Println("expected error occured...")
}
got, _ := client.Database(db).Collection(coll).CountDocuments(context.Background(), bson.M{})
if got != want {
fmt.Printf("expected %d entries in database, but got %d", want, got)
return
}
fmt.Println("it worked!!")
}
func MultipleInsertsTransaction(ctx context.Context, client *mongo.Client) (err error) {
return client.UseSession(ctx, func(sc mongo.SessionContext) error {
err := sc.StartTransaction(options.Transaction().
SetReadConcern(readconcern.Snapshot()).
SetWriteConcern(writeconcern.New(writeconcern.WMajority())),
)
if err != nil {
return err
}
id := primitive.NewObjectID()
if _, err := client.Database(db).Collection(coll).InsertOne(sc, bson.M{"_id": id}); err != nil {
sc.AbortTransaction(sc)
return err
}
if _, err := client.Database(db).Collection(coll).InsertOne(sc, bson.M{"_id": id}); err != nil {
sc.AbortTransaction(sc)
return err
}
return sc.CommitTransaction(sc)
})
}
非常感谢!
我也尝试过不同的实现,但(正如预期的那样(也没有运气:
package main
import (
"context"
"fmt"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
const (
db = "test"
coll = "test"
)
func main() {
client, _ := mongo.Connect(context.Background(), options.Client().ApplyURI("<put replica set connection string here>"))
want, _ := client.Database(db).Collection(coll).CountDocuments(context.Background(), bson.M{})
if err := MultipleInsertsTransaction(context.Background(), client); err != nil {
fmt.Println("expected error occured...")
}
got, _ := client.Database(db).Collection(coll).CountDocuments(context.Background(), bson.M{})
if got != want {
fmt.Printf("expected %d entries in database, but got %d", want, got)
return
}
fmt.Println("it worked!!")
}
func MultipleInsertsTransaction(ctx context.Context, client *mongo.Client) (err error) {
var session mongo.Session
if session, err = client.StartSession(); err != nil {
return err
}
defer session.EndSession(context.Background())
id := primitive.NewObjectID()
if _, err := session.WithTransaction(ctx, func(sc mongo.SessionContext) (interface{}, error) {
if _, err := client.Database(db).Collection(coll).InsertOne(sc, bson.M{"_id": id}); err != nil {
sc.AbortTransaction(sc)
return nil, err
}
if _, err := client.Database(db).Collection(coll).InsertOne(sc, bson.M{"_id": id}); err != nil {
sc.AbortTransaction(sc)
return nil, err
}
return nil, nil
}); err != nil {
return err
}
return
}
Azure CosmosDB针对MongoDB的API仅兼容MongoDB有线协议3.6版。它在模拟与数据库的通信,核心数据库本身不是MongoDB。
MongoDB多文档事务是在4.0版(目前是4.2版(上引入的。如果你使用的是支持事务和发送事务操作的MongoDB驱动程序,那么目前CosmosDB将不具备它的兼容性。根据你的用例,你可能会发现MongoDB Atlas很有用。