dbcache

package
v0.2.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 3, 2026 License: MPL-2.0 Imports: 15 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type DbCache

type DbCache struct {
	Conf   config.Config
	Ctx    context.Context
	Db     *sql.DB
	Logger *zap.Logger
	Mu     sync.Mutex

	// Source is the chassis's runtime *sql.DB handle — the live,
	// configured connection pool that the rest of the chassis writes
	// through. Reload() dumps from this handle (via
	// sqlite3dump.DumpDB) rather than opening its own connection to
	// the file: in WAL mode a second uncoordinated connection races
	// the main one's .db-shm state and fails with "database is
	// locked" on first boot. Going through the same pool means there
	// is no second connection to race.
	Source *sql.DB

	// OnReload, if set, runs at the end of every Reload against the
	// freshly-built in-memory DB while the cache lock is still held —
	// so no request ever observes the snapshot before the overlay is
	// applied. Used by chassis/sysops to re-apply the trusted system
	// opstacks (Reload rebuilds :memory: from the runtime.db dump and
	// would otherwise drop them). A hook error is logged, not fatal:
	// the previous snapshot stays live rather than going dark.
	OnReload func(*sql.DB) error
}

DbCache structure

func New

func New(conf config.Config, logger *zap.Logger, ctx context.Context, source *sql.DB) (*DbCache, error)

New Create a new in-memory DB cache.

`source` is the chassis's runtime *sql.DB — the live connection pool that Reload() reads from. Required: passing nil here would fail at the first Reload. See DbCache.Source for the WAL rationale.

Critical: go-sqlite3 gives each *connection* in the pool its own `:memory:` database. So if connection #1 loads the schema and a later concurrent query opens connection #2, that second connection sees an empty DB and the query fails with "no such table: ops".

To avoid that, pin the in-memory cache to a single connection. Reads are fast and the cache is read-only on the hot path; serializing through one connection costs nothing visible but guarantees consistency under concurrent load.

func (*DbCache) Reload

func (dbc *DbCache) Reload() error

Reload a db file into Memory. Sources from the runtime DB only — the auth DB (when present) is owned exclusively by the admin role and is never mirrored into the read cache.

Concurrency: the entire dump+rebuild+swap runs under the mutex. Two concurrent writers each calling Reload after their commits would otherwise dump in parallel (each capturing a snapshot before some of the OTHER writer's commits land), then queue at the swap mutex; the reload that finishes its dump LAST would publish a STALE snapshot, silently clobbering durably-committed rows from the mirror. Symptom: a row on disk but missing from the resolver until the next (unrelated) reload happens to dump after every commit settled. Moving the dump inside the lock costs serial reloads under write bursts, but the dump was the dominant cost regardless — concurrent dumps were a parallelism mirage.

func (*DbCache) Snapshot

func (dbc *DbCache) Snapshot() *sql.DB

Snapshot returns the current mirror handle under the lock. Callers that live longer than one reload (e.g. the ingress resolver) MUST call this per use rather than capturing dbc.Db once: Reload() swaps dbc.Db to a fresh *sql.DB, so a captured handle goes stale and never sees rows written after it was captured. The returned *sql.DB stays valid for the caller's immediate query (the old handle isn't closed on swap); at worst it's one reload-cycle stale, which is the same guarantee the rest of the read path has.

func (*DbCache) Watch

func (dbc *DbCache) Watch()

Watch a db file for changes

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL