Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle the case where RELEASE SAVEPOINT fails with a retry #227

Merged
merged 1 commit into from
Oct 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 5 additions & 9 deletions e2e/newenemy/newenemy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ func TestNoNewEnemy(t *testing.T) {

t.Log("filling with data to span multiple ranges")
rand.Seed(time.Now().UnixNano())
fill(vulnerableSpiceDb[0].Client().V0().ACL(), 4000, 100)
fill(require, vulnerableSpiceDb[0].Client().V0().ACL(), 4000, 100)

const sampleSize = 5
samples := make([]int, sampleSize)
Expand Down Expand Up @@ -249,29 +249,25 @@ func BenchmarkConflictingTupleWrites(b *testing.B) {
require.NoError(b, spicedb.Connect(ctx, os.Stdout))

// fill with tuples to ensure we span multiple ranges
fill(spicedb[0].Client().V0().ACL(), 2000, 100)
fill(require.New(b), spicedb[0].Client().V0().ACL(), 2000, 100)

b.ResetTimer()

checkNoNewEnemy(ctx, b, spicedb, b.N)
}

func fill(client v0.ACLServiceClient, fillerCount, batchSize int) {
func fill(require *require.Assertions, client v0.ACLServiceClient, fillerCount, batchSize int) {
directs, excludes := generateTuples(fillerCount)
for i := 0; i < fillerCount/batchSize; i++ {
fmt.Println("filling ", i*batchSize, "to", (i+1)*batchSize)
_, err := client.Write(testCtx, &v0.WriteRequest{
Updates: excludes[i*batchSize : (i+1)*batchSize],
})
if err != nil {
fmt.Println(err)
}
require.NoError(err)
_, err = client.Write(testCtx, &v0.WriteRequest{
Updates: directs[i*batchSize : (i+1)*batchSize],
})
if err != nil {
fmt.Println(err)
}
require.NoError(err)
}
}

Expand Down
26 changes: 19 additions & 7 deletions internal/datastore/crdb/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,25 @@ func execute(ctx context.Context, conn conn, txOptions pgx.TxOptions, fn transac
defer func() {
retryHistogram.Observe(float64(i))
}()

releasedFn := func(tx pgx.Tx) error {
if err := fn(tx); err != nil {
return err
}

// RELEASE acts like COMMIT in CockroachDB. We use it since it gives us an
// opportunity to react to retryable errors, whereas tx.Commit() doesn't.

// RELEASE SAVEPOINT itself can fail, in which case the entire
// transaction needs to be retried
if _, err := tx.Exec(ctx, "RELEASE SAVEPOINT cockroach_restart"); err != nil {
return err
}
return nil
}

for i = 0; i < maxRetries; i++ {
if err = fn(tx); err != nil {
if err = releasedFn(tx); err != nil {
if !retriable(err) {
return err
}
Expand All @@ -76,12 +93,7 @@ func execute(ctx context.Context, conn conn, txOptions pgx.TxOptions, fn transac
}
continue
}

// RELEASE acts like COMMIT in CockroachDB. We use it since it gives us an
// opportunity to react to retryable errors, whereas tx.Commit() doesn't.
if _, err = tx.Exec(ctx, "RELEASE SAVEPOINT cockroach_restart"); err == nil {
return nil
}
return nil
}
return errors.New(errReachedMaxRetry)
}
Expand Down