Неожиданное поведение для транзакций, использующих драйвер mon go - go с Azure Cosmos DB - PullRequest
0 голосов
/ 05 марта 2020

Я уверен, что что-то упустил, но я не смог заставить следующую простую транзакцию работать так, как ожидалось. Это поведение отличается от всех других вопросов 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
}

1 Ответ

1 голос
/ 07 марта 2020

Azure API CosmosDB для MongoDB совместим только для Проводного протокола MongoDB версия 3.6. Он эмулирует связь с базой данных, сама базовая база данных не MongoDB.

Многодокументные транзакции MongoDB введена в версии 4.0 (в настоящее время v4.2). Если вы используете драйверы MongoDB, которые поддерживают транзакции и отправляют транзакционные операции, в настоящее время CosmosDB не будет иметь совместимости для него. В зависимости от вашего варианта использования, вы можете найти MongoDB Atlas полезным вместо этого.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...