在 Azure Cosmos DB 中使用 mongo-go-driver 的事务的意外行为
Unexpected behavior for transactions using mongo-go-driver with Azure Cosmos DB
我确定我遗漏了什么,但我无法让以下简单事务按预期工作。此行为不同于我能找到的所有其他 SO 问题。
下面的函数MultipleInsertsTransaction()
是受official examples的启发。
它成功写入了一个文档,然后尝试写入第二个文档,这 returns 一个错误,因为再次(有意)使用了相同的 ID。
我的理解是,none 这些文件都保存在数据库中,因为我们从未达到 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 的 API for MongoDB 仅兼容 MongoDB Wire Protocol 版本 3.6。它模拟与数据库的通信,核心数据库本身不是 MongoDB.
MongoDB multi-document transactions is introduced on version 4.0 (currently v4.2). If you're using MongoDB drivers that supports transactions and sending transactional operations, currently CosmosDB would not have the compatibility for it. Depending on your use case, you may find MongoDB Atlas 反而有用。
我确定我遗漏了什么,但我无法让以下简单事务按预期工作。此行为不同于我能找到的所有其他 SO 问题。
下面的函数MultipleInsertsTransaction()
是受official examples的启发。
它成功写入了一个文档,然后尝试写入第二个文档,这 returns 一个错误,因为再次(有意)使用了相同的 ID。
我的理解是,none 这些文件都保存在数据库中,因为我们从未达到 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 的 API for MongoDB 仅兼容 MongoDB Wire Protocol 版本 3.6。它模拟与数据库的通信,核心数据库本身不是 MongoDB.
MongoDB multi-document transactions is introduced on version 4.0 (currently v4.2). If you're using MongoDB drivers that supports transactions and sending transactional operations, currently CosmosDB would not have the compatibility for it. Depending on your use case, you may find MongoDB Atlas 反而有用。