Documentation
¶
Overview ¶
Package sqlite is the default on-disk agent.Store: a single-file, zero-cgo SQLite journal (via modernc.org/sqlite). An agent run journaled here survives a process crash and resumes without re-running completed steps. No cluster, no daemon: one binary and one file.
Store implements agent.Store, agent.Lister (so agent.Recover finds the runs to re-drive after a restart) and agent.Leaser (so processes sharing the file drive each run one at a time). Open opens three connection pools on the file: a writer of one connection for every Insert and Get, readers for Load and Runs, and a lease connection of its own, whose statements use a short busy timeout so a writer holding the file's lock never delays a lease renewal past its cutoff.
The tables are named with a prefix, "bide_" by default (see WithTablePrefix): bide_steps holds the journal, bide_leases the leases, bide_events the durable event trail (see EventStore), and bide_schema_version the schema version. Open refuses a file whose schema version is newer than this version knows. The file must be on a local disk: SQLite's locking does not work over NFS. A forward jump of the system clock expires leases early, which the journal's attempt claims keep safe.
Index ¶
- type EventStore
- type Option
- type Store
- func (s *Store) AcquireLease(ctx context.Context, runID, holder string, ttl time.Duration) (bool, error)
- func (s *Store) Close() error
- func (s *Store) EventStore() *EventStore
- func (s *Store) Get(ctx context.Context, runID, name string) (agent.Entry, bool, error)
- func (s *Store) Insert(ctx context.Context, runID, name string, data []byte) (agent.Entry, bool, error)
- func (s *Store) Load(ctx context.Context, runID string, after int64) iter.Seq2[agent.Entry, error]
- func (s *Store) ReapLeases(ctx context.Context, ended []string) (int, error)
- func (s *Store) ReleaseLease(ctx context.Context, runID, holder string) error
- func (s *Store) RenewLease(ctx context.Context, runID, holder string, ttl time.Duration) (bool, error)
- func (s *Store) Runs(ctx context.Context, f agent.RunFilter) iter.Seq2[string, error]
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type EventStore ¶ added in v0.16.0
type EventStore struct {
// contains filtered or unexported fields
}
EventStore is a SQLite-backed audit.EventStore: the durable copy of a run's event trail (see audit.PersistJournal and audit.LoadEventLog), in the file's bide_events table (see WithTablePrefix). It shares the Store's connection pools, so Close on the Store closes it, and it needs no setup beyond Open or New, which create the table.
A leaf is stored at primary key (run_id, seq), and Append is one statement, committed before it returns, that inserts the leaf only at the run's next position and does nothing when the position is taken. SQLite runs a write statement under the database's one write lock, so writers racing for a position, in this process or in others sharing the file, are serialized: one inserts, and the others find its leaf and report a no-op or audit.ErrSeqTaken. A crash leaves the trail as of the last committed Append: a leaf is never partly written, and a re-mirror (audit.PersistJournal) appends what is missing. Leaves are stored as the exact bytes given.
func (*EventStore) Append ¶ added in v0.16.0
Append implements audit.EventStore. A seq past the run's next position, or a negative one, is an error; a different leaf at a stored seq fails with an error that wraps audit.ErrSeqTaken.
type Option ¶ added in v0.9.0
type Option interface {
// contains filtered or unexported methods
}
Option configures Open and New.
func WithTablePrefix ¶ added in v0.9.0
WithTablePrefix names the tables with prefix instead of "bide_": prefix+"steps", prefix+"leases", prefix+"events" and prefix+"schema_version". It must be an SQL identifier (letters, digits and underscores, not starting with a digit), or Open and New return an ErrConfig error.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store is a SQLite-backed agent.Store.
func New ¶ added in v0.9.0
New returns a Store over db, which the caller opened and closes. db must be in WAL mode and allow at least two open connections (a Load's page and a write inside its loop may each need one); New returns an ErrConfig error otherwise. Its connections should set a busy timeout of several seconds (modernc.org/sqlite: _pragma=busy_timeout(30000) in the DSN), since a write that gives up on another process's lock fails a step whose side effect may have run. All three roles share db, so a lease statement can wait behind the caller's other work; Open avoids that.
func Open ¶
Open opens (creating if needed) a SQLite journal at path, in WAL mode, with its three connection pools. ":memory:" opens an ephemeral in-memory store for tests, on one connection shared by all three roles (an in-memory database lives on one connection).
Open reaches the file by its canonical path (absolute, symlinks resolved), so that every handle on it, whatever path it was given, makes SQLite derive the same -wal and -shm names; two names for one WAL-mode database corrupt it (https://sqlite.org/howtocorrupt.html). A hard link has no canonical name, so a file with more than one hard link is refused with an error wrapping agent.ErrConfig.
func OpenReadOnly ¶ added in v0.13.0
OpenReadOnly opens the SQLite journal at path for reading only. The file is opened read-only (mode=ro and query_only) on one pool of up to four connections; nothing is created, migrated or checkpointed. Get, Load and Runs (agent.Lister) read as they do on a Store from Open. Every write fails: Insert with an error wrapping agent.ErrStorage, and the agent.Leaser methods with an error. opts are Open's (WithTablePrefix names the tables to read).
It refuses ":memory:" (ErrConfig), a missing file (which it does not create), a file with no journal tables, and one whose schema version is newer than this version's (ErrConfig). A journal another process is writing in WAL mode can be read while it runs; each read sees the journal as of that read. Like Open, it uses the canonical path and refuses a file with more than one hard link (ErrConfig). Close closes the pool. It is for tools that inspect a journal, such as bide view, never for driving a run.
func (*Store) AcquireLease ¶ added in v0.9.0
func (s *Store) AcquireLease(ctx context.Context, runID, holder string, ttl time.Duration) (bool, error)
AcquireLease implements agent.Leaser with one upsert: it grants the lease when the run is unleased, already held by holder (a renewal), or the current lease has expired.
The TTL must exceed the disk's worst commit latency: a lease statement is a committed write, which runs ahead of this process's queued journal writes but cannot finish faster than the disk commits, so a commit that stalls for longer than the TTL lets the lease lapse (agent.ErrLeaseLost).
func (*Store) Close ¶
Close closes the connection pools Open opened. A Store made with New leaves its db open.
func (*Store) EventStore ¶ added in v0.16.0
func (s *Store) EventStore() *EventStore
EventStore returns the audit.EventStore over s's database. It is cheap; the handle holds no state of its own.
func (*Store) Get ¶ added in v0.9.0
Get implements agent.Store. It reads on the writer's connection: the journal reads a step just before it writes one, and a point read on another connection of the same file costs about as much again as the write, which the reader pool is for bulk reads (Load, Runs) to avoid.
func (*Store) Insert ¶ added in v0.9.0
func (s *Store) Insert(ctx context.Context, runID, name string, data []byte) (agent.Entry, bool, error)
Insert implements agent.Store. An entry's Seq is its row id, which SQLite assigns as one more than the table's highest when the row is inserted, under the database's single write lock: so it increases in commit order, within a run and across the table. The entry it returns for a new row holds data itself (SQLite copied it).
func (*Store) Load ¶ added in v0.9.0
Load implements agent.Store. It reads pageSize entries per query and yields them with no connection held, so the caller may write to the store inside its loop.
func (*Store) ReapLeases ¶ added in v0.10.0
ReapLeases implements agent.Leaser with one DELETE, which checks the expiry in the same statement, so a lease taken or renewed meanwhile is kept. The lapsed leases it deletes are those no recovery pass takes over: a finished run's, one on a run the steps table does not hold, and one on a run whose ID contains '>' (a session's or a sub-agent's run).
func (*Store) ReleaseLease ¶ added in v0.9.0
ReleaseLease implements agent.Leaser: it deletes runID's lease if holder holds it.