Always use database transactions when performing operations that require atomicity or could create race conditions. This includes scenarios where you check for existence before creating/updating records, or when multiple related database operations must succeed or fail together.
Always use database transactions when performing operations that require atomicity or could create race conditions. This includes scenarios where you check for existence before creating/updating records, or when multiple related database operations must succeed or fail together.
Follow the standard transaction pattern:
BeginTx()
Example:
func (store *store) Update(ctx context.Context, funnel *traceFunnels.StorableFunnel) error {
tx, err := store.sqlstore.BunDB().BeginTx(ctx, nil)
if err != nil {
return err
}
defer tx.Rollback()
// Check if funnel exists
exists, err := tx.NewSelect().
Model((*traceFunnels.StorableFunnel)(nil)).
Where("name = ? AND org_id = ? AND id != ?", funnel.Name, funnel.OrgID, funnel.ID).
Exists(ctx)
if err != nil {
return err
}
if exists {
return errors.NewAlreadyExistsf("funnel with name already exists")
}
// Update the funnel
_, err = tx.NewUpdate().
Model(funnel).
Where("id = ?", funnel.ID).
Exec(ctx)
if err != nil {
return err
}
return tx.Commit()
}
This prevents race conditions where another process could create a conflicting record between the existence check and the update operation.
Enter the URL of a public GitHub repository