spanner Read-Write Transactionの冪等性について

spannerのRead-Write Transactionは失敗した時にライブラリレベルで自動的にリトライされる。(golangの場合)

リトライが走るともう一度BeginTransactionからやり直して、一からトランザクションを始めることになるため、トランザクションで実行している部分が冪等性を担保できているかどうかを確認する必要がある。

bad

実際にはありえないケースだが、例えばiを0で宣言して1を加算してinsertしたいケースがあるとする。
下記のようなパターンでは、ReadWriteTransactionがabort等によって再実行された際にiが再加算される。 冪等になっていないので、iが1にならない(リトライされるごとに2,3,4と増えていく)ことがある

func write(ctx context.Context, w io.Writer, client *spanner.Client, id uint64) error {  
        i := 0
        _, err := client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanneadWriteTransaction) error {
                i++
                m := []*spanner.Mutation{
                        spanner.InsertOrUpdate("test", []string{"id", "i"}, []interface{}{id, i}),
                }
                return client.Apply(ctx, m)
        })
        return err
}

good

下記のように何度実行されても大丈夫なように冪等性を担保する必要がある。

func write(ctx context.Context, w io.Writer, client *spanner.Client, id uint64) error {  
        _, err := client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanneadWriteTransaction) error {
                // txn内で初期化
                i := 0
                i++
                m := []*spanner.Mutation{
                        spanner.InsertOrUpdate("test", []string{"id", "i"}, []interface{}{id, i}),
                }
                return client.Apply(ctx, m)
        })
        return err
}

link