sqlite

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Aug 22, 2026 License: AGPL-3.0 Imports: 25 Imported by: 0

Documentation

Overview

Package sqlite implements the Wegweiser store on SQLite.

SQLite allows one writer at a time. Go's database/sql knows nothing about that and will happily hand two goroutines two connections to the same file, where they deadlock against each other: a deadlock the busy handler cannot resolve, because backing off does not release the lock the other connection is already holding. The result is SQLITE_BUSY under load with a busy timeout configured, which is the most common way to end up with a mysteriously flaky SQLite backend in Go.

  • the write pool, capped at a single connection, which every Store.Update goes through, and which opens its transactions as BEGIN IMMEDIATE so that a lock conflict with another process surfaces at the start rather than halfway in;
  • the read pool, which several readers share. Write-ahead logging lets them read while a write is in progress, and PRAGMA query_only makes a stray write through this pool an error instead of a violation of the single-writer assumption.

Index

Constants

View Source
const (
	// DefaultBusyTimeout is how long a connection waits for a lock held by
	// another process before giving up.
	DefaultBusyTimeout = 5 * time.Second
	// MinReaders is the smallest read pool that still lets one reader proceed
	// while another is blocked.
	MinReaders = 2
)

Defaults for Options.

Variables

This section is empty.

Functions

This section is empty.

Types

type Options

type Options struct {
	// Path is the database file. It is created if it does not exist.
	Path string

	// MaxReaders is the size of the read pool. Zero picks a default based on
	// the number of CPUs; anything below [MinReaders] is raised to it.
	MaxReaders int

	// BusyTimeout is how long to wait for a lock before failing. Zero picks
	// [DefaultBusyTimeout].
	BusyTimeout time.Duration

	// Now supplies the current time for the timestamps the store owns. Nil
	// picks [time.Now]. Tests set it to keep results reproducible.
	Now func() time.Time
}

Options configure a SQLite store.

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store is a SQLite-backed store.Store.

It embeds the read side, so a read outside a transaction runs the same statement against the read pool that a read inside one runs against the transaction. The write side is deliberately not embedded: it lives on the transaction alone, so that no caller can write without one.

func Open

func Open(ctx context.Context, opts Options) (_ *Store, err error)

Open opens the database at opts.Path, creating it if necessary, and verifies that both connection pools carry the settings they were configured with.

It does not apply migrations: a freshly opened store may be several schema versions behind, and whether to change the database on start is the caller's decision, not this package's. Call Store.Migrate for that.

func (*Store) Capabilities

func (s *Store) Capabilities() store.Capabilities

Capabilities reports what this backend can do.

func (*Store) Close

func (s *Store) Close() error

Close releases both pools. It is safe to call more than once.

func (Store) CommitByID

func (r Store) CommitByID(ctx context.Context, cid journal.CommitID) (*journal.Commit, error)

CommitByID returns one commit with its events.

func (Store) IterZoneRecords

func (r Store) IterZoneRecords(ctx context.Context, zid zone.ZoneID) iter.Seq2[*zone.Record, error]

IterZoneRecords streams every record of a zone in canonical order.

func (Store) IterZones

func (r Store) IterZones(ctx context.Context) iter.Seq2[*zone.Zone, error]

IterZones streams every zone in canonical name order.

func (Store) ListCommits

func (r Store) ListCommits(ctx context.Context, f store.CommitFilter) (store.Page[*journal.Commit], error)

ListCommits returns one page of commit metadata, newest first.

func (Store) ListRecords

func (r Store) ListRecords(ctx context.Context, f store.RecordFilter) (store.Page[*zone.Record], error)

ListRecords returns one page of records in canonical order.

func (Store) ListTokens

func (r Store) ListTokens(ctx context.Context) (_ []*store.Token, err error)

ListTokens returns every token, revoked and expired ones included, so the UI can show a full history. Secrets are not stored and cannot be returned.

func (Store) ListZones

func (r Store) ListZones(ctx context.Context, f store.ZoneFilter) (store.Page[*zone.Zone], error)

ListZones returns one page of zones in canonical name order.

func (Store) ManagedBy

func (r Store) ManagedBy(ctx context.Context, rid zone.RecordID) ([]*zone.Record, error)

ManagedBy returns the records generated from the given source record.

func (Store) ManagedByZone

func (r Store) ManagedByZone(ctx context.Context, zid zone.ZoneID) iter.Seq2[*zone.Record, error]

ManagedByZone streams the records generated from any record of a zone and living somewhere else.

func (*Store) Migrate

func (s *Store) Migrate(ctx context.Context) error

Migrate brings the schema up to what this build expects.

func (*Store) Path

func (s *Store) Path() string

Path returns the database file the store was opened on.

func (*Store) Ping

func (s *Store) Ping(ctx context.Context) error

Ping reports whether the database is reachable. It checks both pools, since a reader failing while the writer works is exactly the asymmetry a health check exists to catch.

func (Store) RecordByID

func (r Store) RecordByID(ctx context.Context, rid zone.RecordID) (*zone.Record, error)

RecordByID returns one record.

func (Store) RecordsByAddress

func (r Store) RecordsByAddress(ctx context.Context, addr netip.Addr) ([]*zone.Record, error)

RecordsByAddress returns every A and AAAA record pointing at addr, across all zones.

func (Store) ReverseZoneFor

func (r Store) ReverseZoneFor(ctx context.Context, addr netip.Addr) (*zone.Zone, error)

ReverseZoneFor returns the most specific reverse zone covering addr.

The obvious formulation (ask for all 33 possible IPv4 networks, or all 129 IPv6 ones, in a single row-value IN list) measured badly twice over. SQLite answers it by scanning every zone rather than by using the index, and the query text itself grows to 129 branches that have to be parsed on every call. At 2000 zones that was around 0.5 ms for one lookup.

So it asks a smaller question first: which prefix lengths exist at all? A deployment has a handful, not 129, and the answer comes from a covering index without touching the table. Only those lengths are then looked up, in one indexed query, and it no longer matters whether the address is IPv4 or IPv6 — see BenchmarkReverseZoneFor, which measures roughly 45 microseconds at ten zones, 55 at a hundred and 240 at two thousand.

func (Store) Setting

func (r Store) Setting(ctx context.Context, key string) ([]byte, error)

Setting returns one JSON-encoded setting value.

func (Store) TokenByHash

func (r Store) TokenByHash(ctx context.Context, hash []byte) (*store.Token, error)

TokenByHash resolves an API token by the SHA-256 of its secret.

The filtering happens in the query rather than after it, so that unknown, revoked and expired all leave by the same door: a caller comparing how long the three take, or which error comes back, learns nothing about which tokens exist.

func (*Store) Update

func (s *Store) Update(ctx context.Context, fn func(store.Tx) error) error

Update runs fn inside one write transaction on the single write connection.

func (*Store) View

func (s *Store) View(ctx context.Context, fn func(store.Reader) error) error

View runs fn inside one read transaction, so that several reads see one state. Write-ahead logging means it does not block the writer.

func (Store) ZoneByID

func (r Store) ZoneByID(ctx context.Context, zid zone.ZoneID) (*zone.Zone, error)

ZoneByID returns one zone.

func (Store) ZoneByName

func (r Store) ZoneByName(ctx context.Context, name zone.Name) (*zone.Zone, error)

ZoneByName returns the zone whose apex is exactly name.

Jump to

Keyboard shortcuts

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