Files
MealQuest/internal/store/tx.go
T
dev_agentandClaude Opus 5.5 59c3cf259d
CI / ci (push) Successful in 50s
CI / ci (pull_request) Successful in 49s
Independent review Codex gpt-6-sol high: CHANGES_REQUIRED (1 medium)
Add store helpers: transactions, ULIDs and time conventions
- Store.WithTx runs fn on the writer, committing on nil and rolling back
  on error, panic or goroutine exit; panics propagate unchanged.
- NewID returns a monotonic ULID string (oklog/ulid/v2, crypto/rand),
  strictly increasing within the process.
- Timestamp stores UTC RFC 3339 TEXT at fixed millisecond precision.
- LocalDate stores YYYY-MM-DD TEXT and rejects invalid or non-canonical
  dates.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:38:54 -04:00

38 lines
951 B
Go

package store
import (
"context"
"database/sql"
"errors"
"fmt"
)
// WithTx runs fn in a transaction on the writer. It commits when fn returns
// nil and rolls back when fn returns an error, panics or exits the goroutine.
// A panic is not recovered: it propagates unchanged after the rollback.
func (s *Store) WithTx(ctx context.Context, fn func(tx *sql.Tx) error) error {
tx, err := s.Writer.BeginTx(ctx, nil)
if err != nil {
return fmt.Errorf("store: begin transaction: %w", err)
}
finished := false
defer func() {
if !finished {
_ = tx.Rollback() // fn panicked; the panic carries on regardless
}
}()
if err := fn(tx); err != nil {
finished = true
if rbErr := tx.Rollback(); rbErr != nil {
return errors.Join(err, fmt.Errorf("store: roll back transaction: %w", rbErr))
}
return err
}
finished = true
if err := tx.Commit(); err != nil {
return fmt.Errorf("store: commit transaction: %w", err)
}
return nil
}