Я уверен, что что-то упустил, но я не смог заставить следующую простую транзакцию работать так, как ожидалось. Это поведение отличается от всех других вопросов SO, которые я мог найти.
Следующая функция MultipleInsertsTransaction()
основана на официальных примерах .
Она успешно записывает один документ, а затем пытается написать второй документ, который возвращает ошибку, потому что та же самая Идентификатор используется снова (намеренно).
Насколько я понимаю, ни один из этих документов не сохраняется в базе данных, поскольку мы никогда не достигли 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
}