Skip to main content

Retries

When two transactions collide, the database cancels one, and running it again usually just works. ant can do that for you.

One transaction​

ant.Attempts reruns a transaction that lost a serialization conflict or a deadlock. The number is the total number of runs. An ordinary error is never retried.

err := db.Tx(
ctx,
func(tx *AppDB) error {
user, err := tx.Users.NoTracking().Find(ctx, id)
if err != nil {
return err
}

_, err = tx.Users.Where(ant.Eq(&U.ID, id)).Update(ctx, ant.Set(&U.Age, user.Age+1))

return err
},
ant.Isolation(sql.LevelSerializable),
ant.Attempts(5),
)

Every transaction​

ant.WithRetry applies to every Tx and SaveChanges of a context, for an application that wants this handled in one place. ant.Attempts overrides it for one call.

db, err := ant.Open[AppDB](ctx, postgres.Dialect, dsn, ant.WithRetry(ant.Retry{Attempts: 5}))

Checking by hand​

ant.IsRetryable reports whether an error is one of these.

err := db.SaveChanges(ctx)

if ant.IsRetryable(err) {
log.Println("worth another try")
}
The function may run twice

A retried function runs again from the top, so it shouldn't change anything outside the database. Send the email after the commit, or from an observer.

Commit failures aren't retried: the transaction may already have committed. See when a commit fails.