Documentation
¶
Overview ¶
Persistence for the saga Store - same reasoning as internal/approval's: a proxy restart mid-run must not silently lose the ability to compensate steps that already executed. Best-effort writes; a storage hiccup is logged, never blocks or fails the in-memory operation.
Postgres-backed persistence for multi-instance/HA deployments - same reasoning as internal/approval/postgres.go: the in-memory map + mutex is correct for one process, but a saga step's Seq assignment (len(s.runs[runID]) today) would collide across two instances appending to the same run concurrently. Postgres mode locks a run's existing rows before assigning the next Seq, serializing that assignment across instances the same way the in-process mutex does within one. MarkCompensated/MarkCompensationFailed need no such locking - they're idempotent single-row UPDATEs, and two instances marking the same step compensated concurrently land on the identical end state.
Package saga records each run's execution trace so it can be walked back. It is simpler than a "dynamic DAG" once one fact is taken seriously: an agent's tool-calling loop is sequential from this proxy's vantage point - one call, one result, one next decision. There is no concurrent branching to reconcile here. So the "graph" a run's steps need is just an ordered list, appended to as calls actually execute, with no workflow pre-declared anywhere. That's the literal meaning of "built from the execution trace, not a static declaration" - branching or retrying earlier in an agent's reasoning never has to be modeled, because only what actually executed ever lands in the trace.
Only Class A steps are ever compensated - that's what Class A means. Class B/C steps still get recorded for a human's visibility into the full run, they're just never walked back.
Index ¶
- type Step
- type Store
- func (s *Store) Close() error
- func (s *Store) HasCompensableSteps(runID string) bool
- func (s *Store) MarkCompensated(runID string, seq int)
- func (s *Store) MarkCompensationFailed(runID string, seq int, note string)
- func (s *Store) RecordStep(step *Step)
- func (s *Store) StepsFor(runID string) []*Step
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store holds each tracked run's ordered step trace. Same shape as internal/approval.Store: in-memory by default (tests), optionally SQLite-backed (NewPersistentStore) so a proxy restart mid-run doesn't lose the ability to compensate it, or Postgres-backed (NewPostgresStore, postgres.go) for multi-instance/HA deployments - see that file's doc comment for why Postgres mode is a genuinely different concurrency model, not just a different file format.
func NewPersistentStore ¶
func NewPostgresStore ¶
NewPostgresStore opens (creating the schema if needed) a Postgres-backed Store safe for concurrent use by multiple proxy processes against the same database. Reuses the same "pgx" database/sql driver as internal/approval's Postgres backend and cmd/rinwall's DB dynamic-secrets path - one driver, blank-imported wherever it's used directly.
func (*Store) Close ¶
Close releases the underlying DB handle, if any (NewStore's in-memory Store has none, and Close is a no-op for it).
func (*Store) HasCompensableSteps ¶
HasCompensableSteps reports whether runID has any executed, not-yet- compensated Class A step - the gate before bothering to trigger a compensation walk at all.
func (*Store) MarkCompensated ¶
func (*Store) MarkCompensationFailed ¶
func (*Store) RecordStep ¶
RecordStep appends a step to its run's trace - always append-only; the sequence number is assigned here so callers never have to coordinate it.