Documentation
¶
Overview ¶
Package sqlite is the one store of Section 12: it owns SQL, unit publication, generation pinning, retention metadata and transactions. It imports only internal/model and the pinned driver; no provider, CLI or config package.
Index ¶
- Constants
- Variables
- func IsGenerationCollected(err error) bool
- func NewGraphReader(ctx context.Context, r *PinnedReader) (graph.GraphReader, error)
- func OwnerLeaseConflict(err error) bool
- func SetTempDir(dir string) error
- type AliasTable
- type CarriedUnit
- type Carry
- type CarryOverStats
- type CoverageCounts
- type ExcludedEntries
- type NodeFilter
- type OccurrenceStream
- type Options
- type PackedStream
- type PageClamps
- type PinnedReader
- func (r *PinnedReader) Binding() model.Binding
- func (r *PinnedReader) Capabilities(ctx context.Context) ([]model.CapabilityState, error)
- func (r *PinnedReader) ChangedFiles(ctx context.Context, after model.FileID, statuses []model.FileStatus, ...) ([]model.FileVersion, error)
- func (r *PinnedReader) Close() error
- func (r *PinnedReader) Containers(ctx context.Context, kinds []model.NodeKind, after model.NodeID, limit int) ([]StoredNode, error)
- func (r *PinnedReader) Continuable() bool
- func (r *PinnedReader) DistinctNodesInFile(ctx context.Context, file model.FileID, afterStart int64, after model.NodeID, ...) ([]StoredNode, error)
- func (r *PinnedReader) DocumentFrequency(ctx context.Context, terms []string) ([]int64, error)
- func (r *PinnedReader) Evidence(ctx context.Context, node model.NodeID, relation model.RelationID, ...) ([]StoredEvidence, error)
- func (r *PinnedReader) EvidenceBatch(ctx context.Context, relations []model.RelationID, perRelation int) (map[model.RelationID][]StoredEvidence, error)
- func (r *PinnedReader) File(ctx context.Context, id model.FileID) (model.FileVersion, error)
- func (r *PinnedReader) FileByPath(ctx context.Context, path string) (model.FileID, error)
- func (r *PinnedReader) Files(ctx context.Context, after model.FileID, limit int) ([]model.FileVersion, error)
- func (r *PinnedReader) FilesByID(ctx context.Context, ids []model.FileID) ([]model.FileVersion, error)
- func (r *PinnedReader) Match(ctx context.Context, expression string, after int64, limit int) ([]int64, error)
- func (r *PinnedReader) Node(ctx context.Context, id model.NodeID) (StoredNode, error)
- func (r *PinnedReader) Nodes(ctx context.Context, f NodeFilter, after model.NodeID, limit int) ([]StoredNode, error)
- func (r *PinnedReader) NodesByID(ctx context.Context, ids []model.NodeID) ([]StoredNode, error)
- func (r *PinnedReader) NodesInFile(ctx context.Context, file model.FileID, afterStart int64, after model.NodeID, ...) ([]StoredNode, error)
- func (r *PinnedReader) OpenPostings(ctx context.Context) (*PostingSession, error)
- func (r *PinnedReader) Relations(ctx context.Context, node model.NodeID, direction model.Direction, ...) ([]model.Relation, error)
- func (r *PinnedReader) SearchStats(ctx context.Context) (documents, tokens int64, err error)
- func (r *PinnedReader) SearchUnit(ctx context.Context, rowid int64) (model.SearchUnit, error)
- func (r *PinnedReader) SearchUnitRowIDs(ctx context.Context, after int64, limit int) ([]int64, error)
- type PostingSession
- func (p *PostingSession) Close() error
- func (p *PostingSession) PackedDocuments(ctx context.Context, rowids []int64) ([]SearchDocument, error)
- func (p *PostingSession) TermCounts(ctx context.Context, term string) (*PackedStream, error)
- func (p *PostingSession) TermOccurrences(ctx context.Context, term string) (*OccurrenceStream, error)
- type Replaced
- type RetentionPolicy
- type RetentionReport
- type RunScope
- type SearchColumn
- type SearchDocument
- type SessionRecord
- type Stats
- type Store
- func (s *Store) Abort(ctx context.Context, gen model.GenerationID) error
- func (s *Store) AcknowledgeFile(ctx context.Context, session model.SessionID, actor string, file model.FileID) error
- func (s *Store) AcquireLease(ctx context.Context, lease model.Lease, ownerRef string) error
- func (s *Store) Activate(ctx context.Context, gen, expectedActive model.GenerationID, ...) (model.Binding, error)
- func (s *Store) ActiveGeneration(ctx context.Context, repo model.RepositoryID) (model.GenerationID, error)
- func (s *Store) AdvanceSession(ctx context.Context, req model.AdvanceRequest) (model.WorkflowStatus, error)
- func (s *Store) AttachCarried(ctx context.Context, gen model.GenerationID, unit model.UnitID, c Carry) error
- func (s *Store) AttachUnit(ctx context.Context, gen model.GenerationID, unit model.UnitID) error
- func (s *Store) BeginGeneration(ctx context.Context, repo model.RepositoryID, snapshot model.SnapshotID, ...) (model.GenerationID, error)
- func (s *Store) BeginProviderRun(ctx context.Context, gen model.GenerationID, ...) (model.ProviderRunID, error)
- func (s *Store) BeginUnit(ctx context.Context, gen model.GenerationID, build model.UnitBuild, ...) (*UnitWriter, error)
- func (s *Store) Blob(ctx context.Context, hash string) (model.BlobRecord, error)
- func (s *Store) Capsule(ctx context.Context, session model.SessionID, actor string) (model.Capsule, error)
- func (s *Store) CapsuleRows(ctx context.Context, session model.SessionID, actor string, ...) ([]model.CapsuleRow, error)
- func (s *Store) CarriedUnits(ctx context.Context, gen model.GenerationID, ...) ([]CarriedUnit, error)
- func (s *Store) CarryRunFailures(ctx context.Context, gen model.GenerationID, sealed []RunScope, ...) error
- func (s *Store) Check(ctx context.Context, deep bool) error
- func (s *Store) ClearWatchHeartbeat(ctx context.Context, repo model.RepositoryID, session string) error
- func (s *Store) Close() error
- func (s *Store) CollectBlobs(ctx context.Context, deadline time.Time, limit int) (deleted []string, restored int64, err error)
- func (s *Store) CompleteProviderRun(ctx context.Context, result model.ProviderResult, diagnosticCode string) error
- func (s *Store) ConfirmChunks(ctx context.Context, session model.SessionID, actor string, chunkIDs []string) error
- func (s *Store) Coverage(ctx context.Context, session model.SessionID, actor string, after model.FileID, ...) ([]model.FileCoverage, error)
- func (s *Store) CoverageSummary(ctx context.Context, session model.SessionID, actor string) (CoverageCounts, error)
- func (s *Store) DeleteGeneration(ctx context.Context, gen model.GenerationID) error
- func (s *Store) DeltaState(ctx context.Context, unit model.UnitID, kind string, dst io.Writer) error
- func (s *Store) DependencyAliases(ctx context.Context, units []model.UnitID) (*AliasTable, error)
- func (s *Store) EnsureRepository(ctx context.Context, id model.RepositoryID, rootPath string) error
- func (s *Store) ExpireSessions(ctx context.Context, now time.Time, limit int) (int64, error)
- func (s *Store) FailedRuns(ctx context.Context, gen model.GenerationID) ([]model.RunFailure, int64, error)
- func (s *Store) Flush(ctx context.Context) error
- func (s *Store) GenerationCapturedAt(ctx context.Context, gen model.GenerationID) (time.Time, error)
- func (s *Store) GenerationStatus(ctx context.Context, gen model.GenerationID) (model.GenerationStatus, error)
- func (s *Store) IncludeManifest(ctx context.Context, req model.IncludeRequest, manifest model.ManifestID) (model.WorkflowStatus, error)
- func (s *Store) IssueChunk(ctx context.Context, c model.IssuedChunk) error
- func (s *Store) KnownBlobs(ctx context.Context, hashes []string) (map[string]struct{}, error)
- func (s *Store) LeaseExpiry(ctx context.Context, id string) (time.Time, error)
- func (s *Store) Manifest(ctx context.Context, id model.ManifestID) (model.ContextManifest, error)
- func (s *Store) ManifestEntries(ctx context.Context, id model.ManifestID, afterOrdinal int, limit int) ([]model.ContextEntry, error)
- func (s *Store) ManifestExcluded(ctx context.Context, id model.ManifestID, afterOrdinal int, limit int) ([]model.ExcludedContextEntry, error)
- func (s *Store) ManifestScopeNodes(ctx context.Context, id model.ManifestID, after model.NodeID, limit int) ([]model.NodeID, error)
- func (s *Store) ManifestSlices(ctx context.Context, id model.ManifestID, afterIndex int, limit int) ([]model.ContextSlice, error)
- func (s *Store) Observations(ctx context.Context, session model.SessionID, actor string, ...) ([]model.Observation, error)
- func (s *Store) OpenSession(ctx context.Context, open model.SessionOpen) (model.SessionID, error)
- func (s *Store) PinGeneration(ctx context.Context, repo model.RepositoryID, gen model.GenerationID, ...) (*PinnedReader, error)
- func (s *Store) ProviderRun(ctx context.Context, id model.ProviderRunID) (model.ProviderRun, error)
- func (s *Store) PruneSessions(ctx context.Context, now time.Time, retention time.Duration, limit int) (int64, error)
- func (s *Store) PutBlob(ctx context.Context, b model.BlobRecord) error
- func (s *Store) PutCapsule(ctx context.Context, c model.Capsule, src model.CapsuleListSource) (model.Capsule, error)
- func (s *Store) PutManifest(ctx context.Context, m model.ContextManifest, requestJSON []byte, ...) error
- func (s *Store) PutObservation(ctx context.Context, o model.Observation) error
- func (s *Store) PutSnapshot(ctx context.Context, snap model.Snapshot, ...) error
- func (s *Store) QuarantineBlobs(ctx context.Context, now time.Time, limit int) (int64, error)
- func (s *Store) RangeConfirmed(ctx context.Context, session model.SessionID, actor string, file model.FileID, ...) (bool, error)
- func (s *Store) RecordRunFailure(ctx context.Context, id model.ProviderRunID, failure model.RunFailure) error
- func (s *Store) RecordSuppliedIndex(ctx context.Context, gen model.GenerationID, path string, resolved bool) error
- func (s *Store) RecordWatchHeartbeat(ctx context.Context, repo model.RepositoryID, hb WatchHeartbeat) error
- func (s *Store) RecordedBlob(ctx context.Context, hash string) (model.BlobRecord, error)
- func (s *Store) Recover(ctx context.Context, now time.Time) error
- func (s *Store) ReleaseLease(ctx context.Context, id string) error
- func (s *Store) RenewLease(ctx context.Context, id string, expiresAt time.Time) error
- func (s *Store) RetainByRef(ctx context.Context, repo model.RepositoryID, p RetentionPolicy, now time.Time) (RetentionReport, error)
- func (s *Store) RetainsLeases() bool
- func (s *Store) SampleBlobs(ctx context.Context, limit int) ([]string, error)
- func (s *Store) SealUnit(ctx context.Context, w *UnitWriter) error
- func (s *Store) SelectedUnit(ctx context.Context, gen model.GenerationID, providerID, scopeKey string) (model.UnitID, error)
- func (s *Store) Session(ctx context.Context, id model.SessionID, actor string) (SessionRecord, error)
- func (s *Store) SessionFilePaths(ctx context.Context, session model.SessionID, actor string, ids []model.FileID) (map[model.FileID]string, error)
- func (s *Store) Snapshot(ctx context.Context, id model.SnapshotID) (model.Snapshot, error)
- func (s *Store) SnapshotFile(ctx context.Context, id model.SnapshotID, file model.FileID) (model.FileVersion, error)
- func (s *Store) SnapshotFiles(ctx context.Context, id model.SnapshotID, afterPath string, limit int) ([]model.FileVersion, error)
- func (s *Store) Stats(ctx context.Context) (Stats, error)
- func (s *Store) StoreSizes(ctx context.Context) (databaseBytes, walBytes int64, err error)
- func (s *Store) SuppliedIndexes(ctx context.Context, gen model.GenerationID) ([]SuppliedIndex, error)
- func (s *Store) SynchronousMode(ctx context.Context) (string, error)
- func (s *Store) Tokenize(ctx context.Context, text string) ([]string, error)
- func (s *Store) TrashBlobs(ctx context.Context, limit int) (trashed, restored int64, err error)
- func (s *Store) UnconfirmedChunks(ctx context.Context, session model.SessionID) (int64, error)
- func (s *Store) UnitInputs(ctx context.Context, unit model.UnitID) iter.Seq2[model.UnitInput, error]
- func (s *Store) UnitOrigin(ctx context.Context, unit model.UnitID) (model.ProviderRunID, error)
- func (s *Store) UnitState(ctx context.Context, id model.UnitID) (state model.UnitState, exists bool, err error)
- func (s *Store) WALBoundBytes() int64
- func (s *Store) Waive(ctx context.Context, req model.WaiverRequest) (model.WaiverRecord, error)
- func (s *Store) WaiversAfter(ctx context.Context, session model.SessionID, actor string, after model.FileID, ...) ([]model.WaiverRecord, error)
- func (s *Store) WatchHeartbeats(ctx context.Context, repo model.RepositoryID) ([]WatchHeartbeat, error)
- func (s *Store) WriterBusy() time.Duration
- type StoredAlias
- type StoredEvidence
- type StoredNode
- type SuppliedIndex
- type TermOccurrence
- type UnitWriter
- func (w *UnitWriter) Abandon(ctx context.Context) error
- func (w *UnitWriter) CarryOver(ctx context.Context, prev model.UnitID, replaced Replaced) (CarryOverStats, error)
- func (w *UnitWriter) EvidenceClipped() int64
- func (w *UnitWriter) Fail(ctx context.Context) error
- func (w *UnitWriter) PutAliases(ctx context.Context, aliases []model.NativeAlias) error
- func (w *UnitWriter) PutDeltaState(ctx context.Context, kind string, src io.Reader) error
- func (w *UnitWriter) PutKeyedNodes(ctx context.Context, facts []model.NodeFact, keys [][]string) error
- func (w *UnitWriter) PutKeyedRelations(ctx context.Context, facts []model.RelationFact, keys [][]string) error
- func (w *UnitWriter) PutNodes(ctx context.Context, facts []model.NodeFact) error
- func (w *UnitWriter) PutRelations(ctx context.Context, facts []model.RelationFact) error
- func (w *UnitWriter) PutSearchUnits(ctx context.Context, docs []model.SearchUnit) error
- func (w *UnitWriter) UnitID() model.UnitID
- type WatchHeartbeat
Constants ¶
const MaxTermOffsets = 512
MaxTermOffsets bounds the offsets one TermOccurrence carries.
const ReasonNotFound = "not_found"
ReasonNotFound is the Details["reason"] value that marks a lookup which found no row, so a caller can tell an absent snapshot or file from a malformed argument under the shared CTX_ARGUMENT_INVALID code.
Variables ¶
var Fingerprint = func() string { sum := sha256.Sum256([]byte(schemaSQL)) return hex.EncodeToString(sum[:]) }()
Fingerprint is the SHA-256 of the embedded DDL text. It is stored in schema_meta and folded into every AnalysisKey (Section 9.1).
Functions ¶
func IsGenerationCollected ¶
IsGenerationCollected reports whether err is a pin refused because the generation it named has been collected.
func NewGraphReader ¶
func NewGraphReader(ctx context.Context, r *PinnedReader) (graph.GraphReader, error)
NewGraphReader opens the packed adjacency of the reader's pinned generation. A generation with no generation_graph row was never published with one, which is a corrupt store rather than an empty graph: the row is written last, so its absence means the build did not finish.
func OwnerLeaseConflict ¶
OwnerLeaseConflict reports whether err is that refusal, so an idempotent acquire can treat the lease the owner already holds as its own answer.
func SetTempDir ¶
SetTempDir names the directory the engine creates its temporary files in for the rest of the process: the spill files of a sort that outgrows its cache, the statement journal of a savepoint past statementJournalSpillBytes, and the temporary tables of a query too large for memory. The engine's own default is the first writable one of the process temp directory and the system's, which on a host whose temp directory is a memory filesystem turns a sort of a large table into a memory allocation of its size. The product calls this once at start-up with a directory under its data directory, so every temporary file lives on the disk the user gave the data and is bounded by that disk, not by memory.
The directory is a process-wide engine setting. It is set through a throwaway connection, before the store or any other database of the process is opened, and never changed afterwards: the engine reads it without a lock whenever it opens a temporary file.
It is also where the pool of those temporaries lives. The file-system shim serves the engine's unnamed delete-on-close opens from this directory's scratch arena, so what the engine would create and unlink once per sort is taken and given back instead; the directory the engine is told about is the fallback for a process that reaches a temporary before a store is opened.
Types ¶
type AliasTable ¶
type AliasTable struct {
// contains filtered or unexported fields
}
AliasTable is every alias row of a unit's declared dependencies, loaded in one read so resolving a unit's candidates costs no read per candidate. It is the one alias path: the index build and the provider test harness both resolve through it. It is immutable once loaded and safe for concurrent use.
The heap holds the aliases of the declared dependencies and nothing else. A unit declares at most its own file's filesystem unit, whose aliases are that file's path chain, the file and its classified node, so the table is sized by one path's depth, never by the repository.
func (*AliasTable) LookupAliases ¶
func (t *AliasTable) LookupAliases(_ context.Context, units []model.UnitID, scopeKey, nativeKey string) ([]StoredAlias, error)
LookupAliases returns every distinct identity that (scopeKey, nativeKey) is aliased to by the given units, from the loaded table: a copy of the key's identities ordered by canonical key and then node id, which depends only on the sealed units' alias rows and never on the order they were written.
The table holds one merged list per key, so it can answer only the whole set it was loaded for. A unit set that differs from it, by a unit the table never read or by one it read and the caller left out, is refused as an internal fault: the answer would carry aliases of rows the caller did not name, or miss aliases of rows it did.
type CarriedUnit ¶
CarriedUnit is one stale member of a generation with its provenance distance, which is what the coordinator projects into the capability report.
type Carry ¶
type Carry struct{ DistanceGenerations, DistanceFiles int }
Carry is how far behind a carried unit is: the number of generations, and the number of changed files, between the unit's snapshot and the one the generation is being built over. Both surface as the provenance distance on the capability's state (Section 13.3).
type CarryOverStats ¶
type CarryOverStats struct {
Nodes int64
Relations int64
Aliases int64
SearchUnits int64
Evidence int64
}
CarryOverStats reports what a carry-over inherited. The Section 11.1 bound on evidence per fact is not applied here: SealUnit applies it to every unit, delta-built or not, so the two cannot diverge.
type CoverageCounts ¶
CoverageCounts is the one coverage aggregate's whole answer, for one actor's session, over the required_full files in scope. It is a struct rather than four positional int64s because Served and FullyRead differ only by the waiver clause: a transposed pair would read as a plausible count and silently answer a different question.
FullyRead is read completeness (Section 17.1) -- every required file fully confirmed at its pinned hash, whether or not it also carries a waiver. Served is the reported fully_served_files: fully confirmed AND not waived. Waived is the waiver count, audited beside coverage and never as coverage, so strict readiness -- which no waiver ever grants -- stays a separate question (Section 17.3).
type ExcludedEntries ¶
type ExcludedEntries func(yield func(model.ExcludedContextEntry) error) error
ExcludedEntries is a manifest's exclusion projection as a push sequence: it yields every excluded entry in ordinal order and returns the first error yield reported, or its own read failure, unchanged.
It must be RESTARTABLE -- callable more than once, yielding the identical sequence each time -- because the compiler folds the canonical manifest hash over the same exclusions it then writes, and a one-shot cursor would hash a sequence and persist an empty one. A sorted run over a spooled projection satisfies this; a channel drained by the first caller does not. A nil sequence is a manifest with no exclusions.
type NodeFilter ¶
type NodeFilter struct {
Name string
QualifiedName string
QualifiedPrefix string
Kinds []model.NodeKind
}
NodeFilter selects nodes by exact name, exact qualified name or qualified name prefix (indexed range scan), optionally restricted to kinds.
type OccurrenceStream ¶
type OccurrenceStream struct {
// contains filtered or unexported fields
}
OccurrenceStream pulls one term's posting list in page-sized refills from a single statement, so peak memory is one page no matter how frequent the term is. A refill always ends on a document boundary, with the first row of the next document carried as lookahead: cutting a document in half would split its (column, offsets) groups and understate Count.
func (*OccurrenceStream) Close ¶
func (s *OccurrenceStream) Close() error
Close releases the statement. Safe to call more than once.
func (*OccurrenceStream) Next ¶
func (s *OccurrenceStream) Next(ctx context.Context, limit int) ([]TermOccurrence, error)
Next returns the next refill: the grouped occurrences of whole documents, at least limit rows unless the stream is exhausted, in the same (document, column, offset) order the former per-page statement produced. A nil result with a nil error means the stream is exhausted.
type Options ¶
type Options struct {
BusyTimeout time.Duration // busy_timeout, default 5s
// ReadConnections sizes the short-read pool: unit states, dependency
// aliases and every query's short reads.
ReadConnections int
// PostingConnections sizes the posting-stream pool, which only a query's
// candidate walk reads (OpenPostings).
PostingConnections int
WriterCacheKiB int // writer_cache_kib, default 1 GiB; also the ingestion group bound
ReaderCacheKiB int // reader_cache_kib, default 4096
BatchRecords int // index.batch_records, default 1000
BatchBytes int64 // index.batch_bytes, default 4 MiB
// MaxJSONBytes bounds every stored JSON column that has no tighter model
// ceiling (manifest requests, capsules). Default 8 MiB, the Section 20.1
// context.max_manifest_bytes / max_capsule_bytes default.
MaxJSONBytes int64
// MaxEvidencePerFact is the effective per-fact evidence clip the providers
// of this process emitted under: index.max_evidence_per_fact when the
// operator set one, otherwise the model's record ceiling. Seal enforces it
// over the union of fresh and carried occurrences, so a delta cannot leave
// a fact holding more than the run's providers were allowed to publish.
// Zero selects the record ceiling, which is what unlimited means here.
MaxEvidencePerFact int
// Synchronous is the synchronous mode of the WRITER connection:
// "normal" (the default, and what config.SynchronousNormal spells) or
// "full". Readers are pinned to FULL regardless -- they are query_only
// and never write, so the mode is behaviourally inert for them, and
// pinning it keeps every pooled connection's pragma set verified.
// An empty value selects the default; an unrecognized value fails Open.
// See docs/adr/ADR-0004-wal-synchronous-mode.md.
Synchronous string
// ReadOnly opens the store for a process that answers questions and
// changes nothing. No writer connection is opened at all, so this process
// can neither wait on the write transaction another process holds nor
// delay it: the reader pool is `query_only` and reads a write-ahead log
// snapshot, which never blocks on a writer. Every connection is opened
// read-only at the FILE level besides, which is what query_only does not
// do: see openPool, and the workspace such a process would otherwise
// rewrite when it closed. A database that is not there is therefore not
// created; it is the workspace nothing has published, reported as such. The schema fingerprint is
// verified by reading instead of inside a write transaction, and every
// mutating entry point refuses with CTX_INTERNAL, because a command that
// mutates must be composed with the writer rather than discover at runtime
// that it cannot.
ReadOnly bool
// LazyWriter opens a writing store that performs no write at all until
// something actually writes. The writer pool is built, but database/sql
// establishes no connection until a statement runs on it, so the only
// write a writing open ever performed -- the schema check inside an
// immediate transaction -- is what this removes: the schema is verified by
// READING, and created only when the cache holds no tables at all, which
// is a cache no run has ever built here and therefore one no run can be
// holding a transaction against.
//
// It exists for the process that both answers and indexes: it must come up
// and serve beside an index another process is already running, and a
// schema check in a write transaction queues behind that run's ingestion
// group and is refused `database is busy: begin` when the busy timeout
// runs out. Ignored when ReadOnly is set: that store has no writer.
LazyWriter bool
}
Options are the storage settings of Section 20.1 `[storage]` plus the `[index]` batch bounds the store enforces on unit writes. For the numeric settings zero selects the documented default; no value means unlimited. The two connection counts have no default: they are derived from the work that reads each pool (config.ReadConnections, config.PostingConnections), which only the caller can see, and Open refuses a store opened without them.
type PackedStream ¶
type PackedStream struct {
// contains filtered or unexported fields
}
PackedStream merges one term's posting lists across the generation's segments by document rowid, in page-sized refills. A refill always ends on a document boundary, so a document's (column, count) groups are never split across two refills, and the rowids it delivers strictly ascend -- the order the candidate walk requires.
func (*PackedStream) Close ¶
func (s *PackedStream) Close() error
Close releases the stream. It holds no statement; the method exists so a packed stream and a live one are the same contract to the caller.
func (*PackedStream) Next ¶
func (s *PackedStream) Next(ctx context.Context, limit int) ([]TermOccurrence, error)
Next returns the next refill: the grouped occurrences of whole documents, at least limit rows unless the lists are exhausted. A nil result with a nil error means they are exhausted.
type PageClamps ¶
type PageClamps = pagination.PageClamps
The page-clamp collector lives in internal/pagination, beside the cursors its notice tells the caller to continue with, so a package that drains one into its answer -- the graph engine, the search service, the coverage session -- does not have to import the store for a type the store only writes into.
PageClamps and WithPageClamps are re-exported because this package's callers install a collector on the context they hand to the store and drain it into their answer's notices, so they name the collector type without importing the pagination package for it alone.
func WithPageClamps ¶
func WithPageClamps(ctx context.Context) (context.Context, *PageClamps)
WithPageClamps returns a context that collects page-bound clamps and the collector to read them from once the reads are done.
type PinnedReader ¶
type PinnedReader struct {
// contains filtered or unexported fields
}
PinnedReader reads exactly one generation. A writer-bearing process pins it together with its retention lease in one short write transaction, so the generation cannot be collected while the reader is open; a process that opened the store read-only holds it by snapshot instead, one deferred read transaction per call. Every read joins visible membership through generation_units; staging, failed and non-member units are never returned. Close releases the lease, if there is one.
func (*PinnedReader) Binding ¶
func (r *PinnedReader) Binding() model.Binding
Binding is the generation every result from this reader is qualified by.
func (*PinnedReader) Capabilities ¶
func (r *PinnedReader) Capabilities(ctx context.Context) ([]model.CapabilityState, error)
Capabilities returns the generation's stored capability report -- every row of it. The bare `LIMIT 256` this once carried silently dropped rows off the tail of a large generation's report, and QueryMeta.Completeness is derived from what this returns on every answer, so the drop made every answer over-claim. The scan streams in one read transaction under the primary key's order, but the ANSWER is the whole report and so is the peak: every row is decoded into the returned slice and there is no page API here. That is deliberate rather than an omission -- the report is written pre-folded. The index publishes it through capabilityReport.finish, which collapses the rows to one per (provider, capability, scope) in foldCollapsed and bounds what survives in boundStates (internal/index/status.go), so a generation's stored report is bounded by the provider/capability/scope product and not by the repository. A page API would page a set whose size the writer already fixed.
func (*PinnedReader) ChangedFiles ¶
func (r *PinnedReader) ChangedFiles(ctx context.Context, after model.FileID, statuses []model.FileStatus, limit int) ([]model.FileVersion, error)
ChangedFiles is Files with one predicate: only the snapshot rows whose status is one of statuses are returned, keyset-paged by file id exactly as Files is. It exists so a caller that wants the captured working-tree changes of a 250,000-file workspace does not have to read every file row to find them; the status column is part of the snapshot_files row the same query already scans, so this is a filter, not a second file listing.
An empty statuses is a caller asking for nothing and is answered with nothing, never with every row: a status filter that silently becomes "no filter" would turn the bounded read this exists to provide back into the full scan.
func (*PinnedReader) Close ¶
func (r *PinnedReader) Close() error
Close releases the lease. It is safe to call more than once.
func (*PinnedReader) Containers ¶
func (r *PinnedReader) Containers(ctx context.Context, kinds []model.NodeKind, after model.NodeID, limit int) ([]StoredNode, error)
Containers pages the visible CONTAINER nodes of the pinned generation by keyset on node_id, restricted to kinds. It is the repository map's enumeration: Nodes above demands exactly one of name, qualified name or qualified-name prefix, and a map has no such seed -- it asks for the containers themselves.
One row per node, chosen by nodePrecedence inside the grouped subquery exactly as NodesByID does, so the outer LIMIT counts NODES and not facts. That is load-bearing rather than tidiness: node_facts holds one row per (node, unit), and a container published by two units would otherwise make a full page come back short, which the caller reads as the end of the map -- a repository silently missing its remaining packages.
Visibility is the generation's: memberOf restricts every fact to a unit this generation selected, so a failed or unsealed unit's containers are invisible.
func (*PinnedReader) Continuable ¶
func (r *PinnedReader) Continuable() bool
Continuable reports whether a query served through this reader may record a retention LEASE. A process that opened the store read-only cannot: it still hands back a continuation -- the spool is a filesystem write and the token is signed, not stored -- but that continuation names no lease and the state it names is reclaimed by the expiry its header carries instead.
func (*PinnedReader) DistinctNodesInFile ¶
func (r *PinnedReader) DistinctNodesInFile(ctx context.Context, file model.FileID, afterStart int64, after model.NodeID, limit int) ([]StoredNode, error)
DistinctNodesInFile is NodesInFile restricted to ONE row per node: the row of the unit the Section 9.4 precedence order picks for that node, at that unit's offset. It exists because a retrieval tier that emits candidates by identity must not emit one node twice, while document-symbols keeps every declared offset; the two readings are separate methods so neither narrows the other.
node_facts is keyed (unit_id, node_id), so a node at two offsets in one file is always two UNITS disagreeing about where it is declared, never two declarations of one unit. Taking the precedence winner's row -- offset and attributes from the same unit -- is what Nodes and Containers already do for the same disagreement (query.go:252, query.go:297), so an identity read of a node reports the same byte range whichever of them serves it.
The order and the (start_byte, node_id) keyset are NodesInFile's, unchanged: each node now appears at exactly one offset, so the key stays total and exact across pages.
func (*PinnedReader) DocumentFrequency ¶
DocumentFrequency returns, per term in order, how many visible documents contain it. It is EXACT: a segment every one of whose units the generation carries answers from its directory entry, and a segment holding documents the generation hides is counted by walking that term's posting list against the visible bitmap. An approximate frequency that counted hidden documents would make a score depend on the store's history, so two stores holding the same code would rank the same query differently.
It bounds the term count itself at nothing: a term is one binary search per segment, which reads one fixed-width entry and one term text per probe and frees both. How many terms one request may carry is the search tier's decision, under resources.max_query_terms.
func (*PinnedReader) Evidence ¶
func (r *PinnedReader) Evidence(ctx context.Context, node model.NodeID, relation model.RelationID, after model.EvidenceID, limit int) ([]StoredEvidence, error)
Evidence pages the visible evidence for one node or one relation, keyset on evidence id. Exactly one subject must be given.
func (*PinnedReader) EvidenceBatch ¶
func (r *PinnedReader) EvidenceBatch(ctx context.Context, relations []model.RelationID, perRelation int) (map[model.RelationID][]StoredEvidence, error)
EvidenceBatch returns the visible evidence backing each relation in one round trip, at most perRelation rows per relation in evidence-id order. The per- relation cap is applied inside SQL, so one heavily evidenced relation cannot consume the whole batch and starve the relations after it. perRelation is an internal hydration cap rather than a frozen request field, so zero takes the model.MaxPageItems default the rest of the reader uses.
func (*PinnedReader) File ¶
func (r *PinnedReader) File(ctx context.Context, id model.FileID) (model.FileVersion, error)
File returns the pinned snapshot's manifest row for id.
func (*PinnedReader) FileByPath ¶
FileByPath resolves a normalized path to its file identity; unknown is CTX_ARGUMENT_INVALID, not an empty result.
func (*PinnedReader) Files ¶
func (r *PinnedReader) Files(ctx context.Context, after model.FileID, limit int) ([]model.FileVersion, error)
Files pages the pinned manifest by keyset on file_id. This is the one paginated source of truth for public file listings (Section 10.1).
func (*PinnedReader) FilesByID ¶
func (r *PinnedReader) FilesByID(ctx context.Context, ids []model.FileID) ([]model.FileVersion, error)
FilesByID hydrates snapshot file metadata for a bounded batch of ids in one query (len(ids) <= model.MaxPageItems). Ids absent from the pinned snapshot are omitted rather than erroring, exactly like NodesByID (adjacency.go:230). It exists because the budget pass needs Size/Path/Status for a selected set, and Files (keyset paging, query.go:452) and File (one row, query.go:429) would make that an N+1.
Rows come back ordered by file_id, which is the snapshot's primary key order and NOT the order of ids: the statement is one indexed probe, so a caller that needs its own order indexes the result by model.FileVersion.ID.
func (*PinnedReader) Match ¶
func (r *PinnedReader) Match(ctx context.Context, expression string, after int64, limit int) ([]int64, error)
Match returns visible rowids whose indexed columns match the FTS5 expression, keyset by rowid. The caller owns query encoding: pass an expression built by the literal-text encoder, never raw user text (Section 14.2).
func (*PinnedReader) Node ¶
func (r *PinnedReader) Node(ctx context.Context, id model.NodeID) (StoredNode, error)
Node returns the preferred visible fact for id, or CTX_ARGUMENT_INVALID when the node is not visible in this generation: a public NodeID is valid only within its binding (Section 9.1).
func (*PinnedReader) Nodes ¶
func (r *PinnedReader) Nodes(ctx context.Context, f NodeFilter, after model.NodeID, limit int) ([]StoredNode, error)
Nodes pages visible node facts matching f by keyset on node_id. One row per node: when several units publish the same node the precedence order picks one. limit is capped at model.MaxPageItems.
func (*PinnedReader) NodesByID ¶
func (r *PinnedReader) NodesByID(ctx context.Context, ids []model.NodeID) ([]StoredNode, error)
NodesByID hydrates a batch of node ids into their preferred visible facts, ordered by node id. Ids that are not visible in the pinned generation are absent from the result rather than an error: a traversal frontier legitimately contains edge endpoints whose node facts this generation does not publish.
func (*PinnedReader) NodesInFile ¶
func (r *PinnedReader) NodesInFile(ctx context.Context, file model.FileID, afterStart int64, after model.NodeID, limit int) ([]StoredNode, error)
NodesInFile pages visible node facts declared in file, keyset on (start_byte, node_id), through idx_nodes_file. Every declared offset of a node is returned; DistinctNodesInFile is the one-row-per-node reading.
func (*PinnedReader) OpenPostings ¶
func (r *PinnedReader) OpenPostings(ctx context.Context) (*PostingSession, error)
OpenPostings begins a posting session for this generation.
func (*PinnedReader) Relations ¶
func (r *PinnedReader) Relations(ctx context.Context, node model.NodeID, direction model.Direction, kinds []model.RelationKind, after model.RelationID, limit int) ([]model.Relation, error)
Relations pages visible edges touching node in direction, keyset on relation_id. Reverse traversal uses the indexed target column.
func (*PinnedReader) SearchStats ¶
func (r *PinnedReader) SearchStats(ctx context.Context) (documents, tokens int64, err error)
SearchStats returns the visible document count and total token length from the generation's packed term statistics, where activation recorded them.
func (*PinnedReader) SearchUnit ¶
func (r *PinnedReader) SearchUnit(ctx context.Context, rowid int64) (model.SearchUnit, error)
SearchUnit reads one visible document by doc id. A posting is shared along a carry chain, but generation_units admits one unit per (provider, scope) and CarryOver refuses a predecessor from another, so at most one row of a chain is visible here and the doc id names one document. Body is always empty: the database stores no source body (ADR-0003 §2.1), and a caller that needs the text reads it from the content store over the returned file identity and byte range.
func (*PinnedReader) SearchUnitRowIDs ¶
func (r *PinnedReader) SearchUnitRowIDs(ctx context.Context, after int64, limit int) ([]int64, error)
SearchUnitRowIDs pages the visible search document ids by keyset. The id is search_units.doc_id -- the search_fts rowid the document's text lives at -- which is what Match returns and what the vocabulary joins on.
type PostingSession ¶
type PostingSession struct {
// contains filtered or unexported fields
}
PostingSession is one read transaction dedicated to a query's term streams. Every stream it opens reads the same snapshot, and that snapshot is held for the whole candidate walk instead of being re-taken per page. It runs on the posting pool, never the reader pool, so a query that holds a session can still issue the short reads (Match) the same walk needs. Close rolls the transaction back and is safe to call more than once; closing it also closes every stream still open on it.
func (*PostingSession) Close ¶
func (p *PostingSession) Close() error
Close ends the session. Cancelling or timing out the context the streams were opened with makes their statements fail and the caller unwind to this Close, which is what releases the connection back to the posting pool.
func (*PostingSession) PackedDocuments ¶
func (p *PostingSession) PackedDocuments(ctx context.Context, rowids []int64) ([]SearchDocument, error)
PackedDocuments hydrates a page of candidates from the segments' packed per-document attribute streams (ADR-0007 Decision 2). It issues NO statement per candidate: the fields are read out of the same parts the query is already reading, through the session's window, so a page of 200 hits costs the parts its documents lie in rather than 200 index descents into the document table.
The answer is in the requested order, and a rowid the generation does not make visible is omitted rather than zeroed -- a document an inherited segment still holds but whose unit left the generation is not a document of this generation at all.
func (*PostingSession) TermCounts ¶
func (p *PostingSession) TermCounts(ctx context.Context, term string) (*PackedStream, error)
TermCounts opens a stream over one single-token term's packed posting lists: one TermOccurrence per (document, column), documents ascending across the whole generation and a document's columns in column-name order, carrying the counts the scorer sums and no offsets. A phrase needs offsets and uses TermOccurrences instead.
func (*PostingSession) TermOccurrences ¶
func (p *PostingSession) TermOccurrences(ctx context.Context, term string) (*OccurrenceStream, error)
TermOccurrences opens a stream over every visible instance of term, one row per (document, column), documents ascending. The stream lives until Close or until the session ends.
type Replaced ¶
type Replaced struct {
// Files are the per-path evidence buckets the import replaced or dropped.
// Each must be a valid FileID.
Files iter.Seq[model.FileID]
// Scopes are the alias scope keys the import replaced or dropped. Each
// must be non-empty and within model.MaxScopeKeyBytes.
Scopes iter.Seq[string]
// Keys are the producer fact keys the import replaced or dropped.
// Each key must be a lowercase hex digest (model.ValidHexID); anything
// else is refused.
Keys iter.Seq[string]
// IndexLevel drops the previous unit's index-level bucket: the facts and
// evidence that name no file. Providers that republish every unlocated
// fact on every run set it; a provider that republishes them only for the
// documents it reparsed must not, or edges carried from untouched
// documents lose their endpoints and the unit cannot seal.
IndexLevel bool
}
Replaced names everything of the previous unit that the current import has already re-emitted or that no longer exists. Everything else is carried. Naming the complement rather than the survivors is deliberate: for both importers the replaced set is a handful of entries against tens of thousands of survivors.
All three sets are streams, consumed once, and none is ever materialized here: the replaced set is small for the refreshes that motivate a delta, but a branch switch or a formatting pass names every document of the repository, and Section 6 forbids holding that list in the Go heap. A producer that cannot stream its set has a whole-repository list of its own, which is the same defect one level up.
A producer's stream may read the store — the dependence applier's Files is a merge join against Store.UnitInputs — and it is drained from inside CarryOver's ingestion call, on the single writer connection. Such a stream sees the store as of the last commit: it takes a connection from the reader pool (Options.ReadConnections) once per page of its own scan, and the ingestion group stays open across every one of those round-trips. What a stream reads is the predecessor unit, sealed and committed before this run began, so the last commit is the state it needs; a stream must never call a read that runs on the group's own connection, because the ingestion call draining it holds that connection and the two would wait on each other. WAL readers never wait on the writer, so the pool reads cannot deadlock; a saturated reader pool stalls the carry-over, and the open group blocks every other writer in the process for as long as the walk lasts. A producer whose stream reads the store must therefore keep those reads bounded and proportional to the unit it is replacing, and must not discover this nesting by measuring a stall.
type RetentionPolicy ¶
RetentionPolicy is the user-set retention window of Section 20.1. Both settings spell 0 as "unlimited", exactly as the `retain_refs` and `max_retained_bytes` configuration keys document it: 0 refs retains every ref the repository has ever activated, and 0 bytes leaves retention governed by RetainRefs alone. A nonzero MaxRetainedBytes evicts least-recently-used refs first and never the active generation's ref.
type RetentionReport ¶
type RetentionReport struct {
RefsRetained int
GenerationsSwept int
UnitsDeleted int
BytesReclaimed int64
BytesReclaimable int64
// GenerationsDeleted names the generations this pass actually deleted, so
// a caller that keeps its own rows against a generation can delete them
// with it. It holds exactly the swept generations, which is what this pass
// already materialized as its candidates, and never a repository-sized
// list.
GenerationsDeleted []int64
}
RetentionReport is what one pass did and what a stricter limit could still reclaim. BytesReclaimable is measured even when MaxRetainedBytes is 0 (unlimited), so `status` can warn before a disk fills: it is the accounted size of the retained non-active generations this pass kept, including any it skipped because a live lease still holds them.
Both byte figures are this store's own record accounting — the fixed per-record overhead plus the stored text — over the fact, evidence, alias, search and delta-state rows of a generation's units, and both count only the units the generation holds EXCLUSIVELY, which are the ones deleting it would actually free; a unit another generation still selects reclaims nothing. The size max_retained_bytes is measured against is a different question and counts every unit a retained generation selects, shared or not (see keepByRef). Both exclude FTS index overhead, page rounding and the content blobs, which snapshots and the Section 10.4 grace protocol retain, not generations. The figures are an accounting estimate for a warning, not a page count.
type RunScope ¶
type RunScope struct {
ProviderID, ScopeKey string
}
RunScope names one scope of one provider: the pair a failure reason is about.
type SearchColumn ¶
type SearchColumn string
SearchColumn names one indexed column of search_fts, in its declared order.
const ( ColumnName SearchColumn = "name" ColumnQualifiedName SearchColumn = "qualified_name" ColumnSignature SearchColumn = "signature" ColumnPath SearchColumn = "path" // ColumnBody is an indexed column of search_fts; it is not a stored // column of search_units, which keeps no copy of the source text. ColumnBody SearchColumn = "body" )
type SearchDocument ¶
type SearchDocument struct {
RowID int64
ID string
NodeID model.NodeID
FileID model.FileID
Path string
Kind model.NodeKind
Name string
QualifiedName string
Signature string
Bytes model.ByteRange
TokenCount int64
}
SearchDocument is a visible lexical document without its body: Section 14.2 forbids source bodies in generic results, and the database stores no body to return (ADR-0003 §2.1). Ranking and hydration use it, and it is what one record of a segment's per-document attribute stream decodes to.
type SessionRecord ¶
type SessionRecord struct {
ID model.SessionID
ActorID string
Binding model.Binding
ManifestID model.ManifestID
Phase model.Phase
State model.WorkflowState
StateVersion int
ScopeVersion int
CreatedAt time.Time
ExpiresAt time.Time
ClosedAt *time.Time
}
SessionRecord is one read_sessions row with its generation binding. Readiness is computed by the workflow service from this plus Coverage and observations.
type Stats ¶
type Stats struct {
Generations int64
Snapshots int64
Files int64
Blobs int64
Units int64
NodeFacts int64
RelationFacts int64
Evidence int64
SearchUnits int64
Leases int64
Sessions int64
DatabaseBytes int64
WALBytes int64
}
Stats is the storage accounting status and doctor report (Section 22): bounded row counts plus database and WAL bytes on disk.
type Store ¶
type Store struct {
// Version is the embedded engine's sqlite_version() as observed at open.
Version string
// SourceID is sqlite_source_id() as observed at open.
SourceID string
// contains filtered or unexported fields
}
Store is one workspace database: a single writer connection, a short-read pool, a posting-stream pool and a private in-memory connection for query tokenization.
func Open ¶
Open opens or initializes the database at path (its parent directory is created 0700). It verifies the embedded engine version, applies and checks the Section 12.1 pragmas on every pooled connection, and fails closed with CTX_SCHEMA_MISMATCH on a foreign schema. A rebuild is a different path chosen by the caller; this function never alters an incompatible database.
func (*Store) Abort ¶
Abort fails a staging generation: its status and health become failed, its still-running provider runs are marked failed, and the unsealed units those runs were building are deleted. Sealed units stay reusable and the active pointer is untouched.
func (*Store) AcknowledgeFile ¶
func (s *Store) AcknowledgeFile(ctx context.Context, session model.SessionID, actor string, file model.FileID) error
AcknowledgeFile records the client's separate full-file statement. It fails unless the file is already fully served for this session and hash; an acknowledgment never substitutes for receipts (Section 16.2).
func (*Store) AcquireLease ¶
AcquireLease records a retention lease. The generation and snapshot it names must exist; leases on staging or failed generations are permitted so a build's own state stays retained.
ownerRef names the owner the lease is taken for -- a session id, say -- and makes the lease findable from its owner, so the transaction that ends the owner ends the lease with it (releaseOwnerLease, and AdvanceSession's close). At most one live lease exists per (owner kind, owner ref): a second acquire is refused with OwnerLeaseConflict rather than pinning the generation twice. An owner with no stable identity of its own passes "".
func (*Store) Activate ¶
func (s *Store) Activate(ctx context.Context, gen, expectedActive model.GenerationID, health model.GenerationHealth, capabilities []model.CapabilityState, normalizationVersion string) (model.Binding, error)
Activate validates the frozen membership, computes the AnalysisKey and runs exactly the Section 12.3 pointer transaction. expectedActive is the generation the caller believes is published (zero: none); it is compared with active_generations inside the same immediate transaction and a mismatch is CTX_VERSION_CONFLICT, so a coordinator never publishes over a pointer it has not seen. Any failure rolls back every status and pointer change.
func (*Store) ActiveGeneration ¶
func (s *Store) ActiveGeneration(ctx context.Context, repo model.RepositoryID) (model.GenerationID, error)
ActiveGeneration returns the published generation for repo, or CTX_NO_ACTIVE_GENERATION.
func (*Store) AdvanceSession ¶
func (s *Store) AdvanceSession(ctx context.Context, req model.AdvanceRequest) (model.WorkflowStatus, error)
AdvanceSession performs one compare-and-swap workflow transition.
func (*Store) AttachCarried ¶
func (s *Store) AttachCarried(ctx context.Context, gen model.GenerationID, unit model.UnitID, c Carry) error
AttachCarried makes a sealed unit a stale member of gen, recording how far behind it is. Unlike AttachUnit it does not require the unit's inputs to be the snapshot's version of those files — a carried unit differs from the snapshot by definition — but every input must still exist in the snapshot (CTX_SNAPSHOT_CHANGED otherwise), because Section 13.3 does not carry a unit whose files were deleted.
func (*Store) AttachUnit ¶
AttachUnit reuses a sealed unit in gen. Reuse is admitted only when every declared input is exactly the selected snapshot's version of that file (content hash and mode); otherwise CTX_SNAPSHOT_CHANGED. Membership of the unit's dependencies is validated at Activate.
func (*Store) BeginGeneration ¶
func (s *Store) BeginGeneration(ctx context.Context, repo model.RepositoryID, snapshot model.SnapshotID, semanticConfigHash, ref string) (model.GenerationID, error)
BeginGeneration opens a staging generation over snapshot. Staging facts are invisible to every reader until Activate publishes the pointer.
ref is the ref this generation is built from and is what retention groups by (Section 12.4): keeping the last retain_refs DISTINCT refs is what makes switching A -> B -> C -> A find A's units still on disk. Storage never interprets it. The coordinator supplies a branch name, the HEAD object id when HEAD is detached, or the sentinel "(none)" when the workspace is not a Git repository; an empty ref is refused rather than silently grouping every non-Git generation under one nameless bucket.
func (*Store) BeginProviderRun ¶
func (s *Store) BeginProviderRun(ctx context.Context, gen model.GenerationID, providerID, providerVersion string) (model.ProviderRunID, error)
BeginProviderRun records a running provider invocation for gen and returns its random operational ID.
func (*Store) BeginUnit ¶
func (s *Store) BeginUnit(ctx context.Context, gen model.GenerationID, build model.UnitBuild, inputs func(yield func(model.UnitInput) error) error) (*UnitWriter, error)
BeginUnit opens an immutable unit for gen. It recomputes the unit identity, requires every declared dependency to be sealed, streams and hashes the inputs in bounded batches against gen's snapshot, and rejects the unit if the streamed inputs do not reproduce Spec.InputHash. inputs must yield in ascending FileID order. A sealed unit with the same key cannot be rebuilt; use AttachUnit to reuse it.
func (*Store) Blob ¶
Blob reads a ready blob's integrity metadata: the record PutBlob persisted, with block digests in block order and line checkpoints in byte order. A blob that is absent, quarantined or in trash is CTX_SOURCE_INTEGRITY: no caller may serve bytes it cannot verify against stored digests.
func (*Store) Capsule ¶
func (s *Store) Capsule(ctx context.Context, session model.SessionID, actor string) (model.Capsule, error)
Capsule reads a session's stored capsule for its actor.
func (*Store) CapsuleRows ¶
func (s *Store) CapsuleRows(ctx context.Context, session model.SessionID, actor string, list model.CapsuleList, after string, limit int) ([]model.CapsuleRow, error)
CapsuleRows reads one keyset page of one list of a session's sealed capsule.
after is the cursor a previous page ended on -- a row's own key, not an offset -- and empty starts at the first record. A cursor naming no row in this list is refused as CTX_CURSOR_INVALID rather than silently restarting the list, which would make a continuation read as a complete answer.
The caller decides whether there is a next page from the capsule's count for this list and the last row's ordinal, so the last page is the last page: no extra round trip and no empty trailing page.
func (*Store) CarriedUnits ¶
func (s *Store) CarriedUnits(ctx context.Context, gen model.GenerationID, afterProviderID, afterScopeKey string, limit int) ([]CarriedUnit, error)
CarriedUnits pages gen's stale members in provider and scope order, keyset on that pair (two empty strings start from the beginning); limit is capped at model.MaxPageItems. A generation with none returns an empty slice, not an error: carrying nothing is the ordinary case.
It is a page and not a listing because a caller must not assume it returns everything: a generation may carry more stale units than one response may hold, and refusing that with CTX_RESOURCE_LIMIT would take the capability report down with it. A short page — fewer rows than limit — is the end.
The fan-out is real and not hypothetical. The planner (internal/index/plan, which storage must not import) admits up to 513 dependence units per language family that subdivides — 512 nested project units plus the root — across five such families, plus one C/C++ unit: 2566 carriable units in one generation, well past any single page. Page until a short page arrives.
func (*Store) CarryRunFailures ¶
func (s *Store) CarryRunFailures(ctx context.Context, gen model.GenerationID, sealed []RunScope, from ...model.GenerationID) error
CarryRunFailures copies the recorded failure reasons of every run of the source generations onto gen, which must still be staging and belong to the same repository, except the reasons about a scope in sealed.
A generation that publishes work other generations ran holds none of their run rows: a deferred publication extends the active generation with units a short-lived work generation built, and the scopes that failed along the way -- in the foreground pass the queue was planned over, in an earlier batch, or in this one -- are named failed by its capability rows while FailedRuns, which reads by generation, would find no reason for any of them. Copying the rows gives the published generation the reasons its own capability rows state, and the copies die with it exactly as the originals die with theirs. The sources are left as they are: a publication that loses its activation is aborted, and a moved row would take the active generation's reasons with it.
THE RULE: gen carries a reason only for a scope it does not seal. sealed is the set of scopes whose fresh unit gen publishes, and a reason about one of them is a failure that unit has since answered; carried on, it would list the scope as failed beside a capability row that reports it covered, in this generation and every one that extends it.
Each copy is a run row of its own under a fresh random id, so no two generations ever share one row. The copy is one statement per source and one per sealed scope, and holds nothing in memory, whatever number of reasons it moves; sealed is the caller's own batch, already in hand.
func (*Store) Check ¶
Check runs the integrity checks Section 12.1 reserves for initialization, doctor and recovery.
It is two different checks, because the expensive one is O(database bytes) and an ordinary `doctor` may not pay it: on a 1.9 GB index `quick_check` alone costs 30 s cold and ~4.5 s of CPU warm, which made the one command an operator runs on a sick workspace the slowest command in the product.
- shallow (deep == false): the constant-cost header reads -- page size and page count, the schema fingerprint row, and that the journal is still the write-ahead log this store requires. These catch a database that is not this schema, was truncated below its own header, or lost its WAL mode; they do NOT walk a single page of content, and the caller must report the content walk as unverified rather than as passed.
- deep (deep == true): everything shallow checks, then `quick_check`, the foreign key check and the full-text index walk -- the complete pass.
Both run on the reader pool (`s.read`), never `s.write`: an integrity walk reads, and taking the writer for it serialised `doctor` against any running indexer for the whole walk. The one exception is the FTS5 `integrity-check` command, which is spelled as an INSERT and is therefore refused by the readers' `query_only=ON`; it takes the writer for that statement alone, and only under deep.
search_fts is contentless (ADR-0003 §2.1), so its integrity-check proves the index is internally consistent, not that it agrees with a stored copy of the text -- there is no stored copy, and the text's own authority is the content store, which verifies every block digest on read. Document membership is still checked both ways: a posting is named by search_units.doc_id -- which a delta carry-over shares along a carry chain, so several rows may name one -- and the vocabulary scan in checkContent reports any indexed document no row names.
func (*Store) ClearWatchHeartbeat ¶
func (s *Store) ClearWatchHeartbeat(ctx context.Context, repo model.RepositoryID, session string) error
ClearWatchHeartbeat removes this watch's own heartbeat and no other's. A watch that ends deliberately withdraws its claim at once rather than leaving a fresh row that reads as live coverage until it expires; expiry remains the answer for a process that died without getting here.
func (*Store) Close ¶
Close commits any open ingestion group, stops the writer goroutine and releases every pool. It is safe to call once.
func (*Store) CollectBlobs ¶
func (s *Store) CollectBlobs(ctx context.Context, deadline time.Time, limit int) (deleted []string, restored int64, err error)
CollectBlobs is the final phase: a trashed blob referenced again is restored, and one trashed at or before deadline that the recheck in this same transaction still finds unreferenced has its row deleted. The hashes of the deleted rows are returned so the caller removes their CAS objects -- the database row goes first, because a published object with no row is an orphan the next capture simply republishes over, while a row with no object reads as present and fails only on the first ReadRange.
func (*Store) CompleteProviderRun ¶
func (s *Store) CompleteProviderRun(ctx context.Context, result model.ProviderResult, diagnosticCode string) error
CompleteProviderRun records a run's terminal state and counters.
func (*Store) ConfirmChunks ¶
func (s *Store) ConfirmChunks(ctx context.Context, session model.SessionID, actor string, chunkIDs []string) error
ConfirmChunks marks issued chunks confirmed and merges their intervals into served_ranges in one transaction (Section 16.3). Duplicate confirmations are idempotent; an unknown, expired or foreign chunk fails the whole batch.
func (*Store) Coverage ¶
func (s *Store) Coverage(ctx context.Context, session model.SessionID, actor string, after model.FileID, limit int) ([]model.FileCoverage, error)
Coverage pages the session's honest per-file coverage: pinned hash, size, confirmed bytes (the disjoint served union), requirement, state and waiver. A nonempty file is full_served only when the union is exactly [0,size); an empty file requires a confirmed zero-length EOF receipt.
func (*Store) CoverageSummary ¶
func (s *Store) CoverageSummary(ctx context.Context, session model.SessionID, actor string) (CoverageCounts, error)
CoverageSummary answers CoverageCounts in a single round trip. Like Coverage and Session it reports honestly beside CTX_SESSION_EXPIRED rather than pretending the session has no scope.
func (*Store) DeleteGeneration ¶
DeleteGeneration removes a failed or superseded generation's membership and metadata, then collects units no retained generation or dependent unit reaches, in reverse dependency order through the FTS-aware deletion procedure, then orphan identities and origin runs (Section 12.4). A live lease or a retained session referencing the generation blocks deletion with a retryable CTX_WORKSPACE_BUSY. The tail is the shared sweep; blob collection is a separate grace-protocol step and is not performed here.
func (*Store) DeltaState ¶
func (s *Store) DeltaState(ctx context.Context, unit model.UnitID, kind string, dst io.Writer) error
DeltaState streams what a sealed unit stored under kind into dst, part by part, in one read transaction so the artifact is the one the unit sealed. A unit that stored none is CTX_ARGUMENT_INVALID with reason not_found, so a refresh can tell "no previous state, import in full" from a failure.
func (*Store) DependencyAliases ¶
DependencyAliases loads every alias row of units in one read transaction on the reader pool, against the last commit. Each list is the distinct identities of one scoped native key, ordered by canonical key and then node id. No units is an empty table and no read.
Every unit must be sealed in that snapshot. One that is not -- building, failed, absent, or sealed only in the uncommitted ingestion group -- is refused naming the unit, never answered as aliased to nothing: an empty answer would mint an identity other than the one the committed dependency names, and the run would still succeed.
func (*Store) EnsureRepository ¶
EnsureRepository records the installation-local repository identity and its current root path. The row is required by every snapshot and generation.
func (*Store) ExpireSessions ¶
ExpireSessions closes open sessions whose TTL has passed, in one bounded batch, and reports how many it closed. Closing stops mutations; the audit artifacts stay until PruneSessions.
func (*Store) FailedRuns ¶
func (s *Store) FailedRuns(ctx context.Context, gen model.GenerationID) ([]model.RunFailure, int64, error)
FailedRuns is one page of the typed reasons the runs of one generation failed, in provider order, oldest first within a provider, with the number of reasons that did not fit the page.
It is keyed by GENERATION and not by run id, which is what makes it answerable at all: the reasons are on the run rows, a status report holds no run ids, and a reader keyed by one could only be called by something that already knew which run to ask about. Every surface that reports a failure asks the same question -- why does this generation have no facts for these scopes -- and this is the one answer to it.
A generation that has been collected takes its runs with it, so an empty page is "no reason is kept for this generation" and not an error: the question was why a scope has no facts, and "the generation that failed is no longer retained" is an answer.
The page is bounded like every other result list, and what did not fit is counted rather than dropped in silence: the one surface whose job is to report failures must never lose one without saying so.
func (*Store) Flush ¶
Flush commits the open ingestion group, if any. The index build calls it at each provider boundary, so the next provider's reads on the reader pool see the units the previous one sealed; a caller that opens a connection of its own -- a test or a tool reading the database file beside the store -- calls it to see what the store has written so far. Close commits whatever a process leaves open.
func (*Store) GenerationCapturedAt ¶
func (s *Store) GenerationCapturedAt(ctx context.Context, gen model.GenerationID) (time.Time, error)
GenerationCapturedAt reports when the capture a generation serves was taken: the creation time of its snapshot, not of the generation row. They differ whenever a generation is built from a snapshot an earlier run captured, and the snapshot's time is the one that answers "how old is the source this workspace is serving".
func (*Store) GenerationStatus ¶
func (s *Store) GenerationStatus(ctx context.Context, gen model.GenerationID) (model.GenerationStatus, error)
GenerationStatus reads one generation's lifecycle status.
func (*Store) IncludeManifest ¶
func (s *Store) IncludeManifest(ctx context.Context, req model.IncludeRequest, manifest model.ManifestID) (model.WorkflowStatus, error)
IncludeManifest points an open sweep/verify session at a recompiled manifest of the same generation and snapshot: history is appended, scope and state versions advance, and the new manifest's files join the session while same-hash coverage already confirmed is retained.
func (*Store) IssueChunk ¶
IssueChunk persists an issued, not yet confirmed, chunk for the session's actor. Like every session mutation it loads the session first, so a wrong actor, a closed or an expired session is refused before any row is written. The chunk must name a file and content hash in the session's pinned scope; the schema's foreign key to session_files makes another snapshot's bytes unrepresentable.
func (*Store) KnownBlobs ¶
KnownBlobs reports which of hashes the index still holds a blobs row for, as a set: a hash present in the result is named by the store, and absence means no row exists. It is the oracle (*snapshot.CAS).SweepOrphans asks before removing a published object, so the direction of an error matters -- a partial answer must never reach the sweep, and this returns nothing but a complete set or an error.
The chunking is a statement-shape bound only: every chunk is read inside ONE read transaction, so the answer is a single consistent snapshot of blobs rather than one stitched from as many snapshots as it took chunks. A set assembled across snapshots could omit a hash a concurrent writer inserted after an earlier chunk and report the object an orphan.
No state filter: 'quarantined' and 'trash' rows are blobs mid-grace-protocol, whose objects that protocol deletes after its own reachability recheck. Only a file with no row at all is an orphan.
func (*Store) LeaseExpiry ¶
LeaseExpiry reports when a live lease expires. A lease that was released or never existed is CTX_CURSOR_INVALID, so spools and cursors that consult it end with their lease rather than with the expiry they were created under.
func (*Store) Manifest ¶
func (s *Store) Manifest(ctx context.Context, id model.ManifestID) (model.ContextManifest, error)
Manifest reads one manifest header with its binding, exactly as written.
func (*Store) ManifestEntries ¶
func (s *Store) ManifestEntries(ctx context.Context, id model.ManifestID, afterOrdinal int, limit int) ([]model.ContextEntry, error)
ManifestEntries pages a manifest's entries by ordinal.
func (*Store) ManifestExcluded ¶
func (s *Store) ManifestExcluded(ctx context.Context, id model.ManifestID, afterOrdinal int, limit int) ([]model.ExcludedContextEntry, error)
ManifestExcluded pages a manifest's exclusions by ordinal.
func (*Store) ManifestScopeNodes ¶
func (s *Store) ManifestScopeNodes(ctx context.Context, id model.ManifestID, after model.NodeID, limit int) ([]model.NodeID, error)
ManifestScopeNodes pages the DISTINCT node ids a manifest's entries name, in node-id order, by keyset on the node id itself.
The capsule's scope list is exactly this set, and deriving it in SQL is what keeps the seal's memory a function of the page size: a Go-side walk of ManifestEntries would have to hold a dedup set and a sort buffer over every entry the manifest carries, and context.max_manifest_bytes is unlimited by default. Entries that name no node (a file pulled in whole) contribute nothing and are excluded here rather than skipped by the caller.
The rows carry node_ids.id surrogates, which are storage-internal and meaningless outside this database file, so the page is both ordered and keyset on the CANONICAL 32-byte id reached through node_ids: the caller (workflow's capsule scope collector) feeds the last id it received back as `after`, and a cursor must carry a canonical identity. node_id is not indexed on context_entries (the primary key is (manifest_id, ordinal)) and the ordering column lives in the joined dictionary, so SQLite satisfies the DISTINCT and the ORDER BY from a temporary b-tree over one manifest's entries, each row's canonical fetched by the node_ids integer primary key. That cost is the database's, not the process heap's, and it is bounded by the manifest.
func (*Store) ManifestSlices ¶
func (s *Store) ManifestSlices(ctx context.Context, id model.ManifestID, afterIndex int, limit int) ([]model.ContextSlice, error)
ManifestSlices pages a manifest's slices by index, with their entry ordinals.
func (*Store) Observations ¶
func (s *Store) Observations(ctx context.Context, session model.SessionID, actor string, kind model.ObservationKind, scopeVersion int, after model.ObservationID, limit int) ([]model.Observation, error)
Observations pages a session's observations by ID, optionally one kind and one scope version (zero means every version).
func (*Store) OpenSession ¶
OpenSession creates an actor-specific session over a manifest and populates its file scope. With an idempotency key, a retry by the same actor with the same open-request hash returns the existing session; the same key with a different hash fails (Section 12.4). Identical requests from different actors always get separate sessions.
func (*Store) PinGeneration ¶
func (s *Store) PinGeneration(ctx context.Context, repo model.RepositoryID, gen model.GenerationID, ttl time.Duration) (*PinnedReader, error)
PinGeneration resolves gen (zero selects the active generation) and acquires a query lease for ttl in the same transaction (Section 12.3). A staging or failed generation cannot be pinned.
func (*Store) ProviderRun ¶
func (s *Store) ProviderRun(ctx context.Context, id model.ProviderRunID) (model.ProviderRun, error)
ProviderRun reads one run's provenance. GenerationID is zero once the creating generation has been deleted.
func (*Store) PruneSessions ¶
func (s *Store) PruneSessions(ctx context.Context, now time.Time, retention time.Duration, limit int) (int64, error)
PruneSessions deletes closed or complete sessions whose closed_at is older than retention, in one bounded batch. Their chunks, ranges, observations and capsules cascade; their manifests become collectable with their generation.
func (*Store) PutBlob ¶
PutBlob records one retained CAS object with its block digests and line checkpoints (Section 10.3). It is idempotent for the same hash and size; the same hash with a different size is a source integrity failure. The caller has already flushed and published the object file.
func (*Store) PutCapsule ¶
func (s *Store) PutCapsule(ctx context.Context, c model.Capsule, src model.CapsuleListSource) (model.Capsule, error)
PutCapsule stores the deterministic completion record once, for a session whose consolidate phase is open. A second call returns the first stored capsule unchanged (Section 17.3).
The capsule blob carries identity and counts; src streams the records themselves into context_capsule_rows inside this same write transaction, after the "a capsule already exists" check, so the seal stays write-once and a capsule is never committed without the rows its identity was derived from. A second call returns the stored capsule and writes no rows.
func (*Store) PutManifest ¶
func (s *Store) PutManifest(ctx context.Context, m model.ContextManifest, requestJSON []byte, entries []model.ContextEntry, slices []model.ContextSlice, excluded ExcludedEntries) error
PutManifest stores an immutable compiled manifest: header, ordered entries, slices and exclusions in one transaction. Every entry's node must be visible in the manifest's generation and every file must be in its snapshot. Storing the same manifest ID again with the same canonical hash is a no-op; a different hash under the same ID is CTX_VERSION_CONFLICT.
Entries must arrive in the Section 15.3 reading order: ordinals 0..n-1, with requirement rank non-decreasing (so required_full is a prefix) and score descending within a rank. The workflow walks those ordinals directly, so an order this method accepted and never checked would be a defect visible only one task later.
Exclusions arrive as a SEQUENCE and not as a slice. An entry list is bounded by the compile's resolved budget -- the caller's own declared window -- but the exclusion list is bounded only by the repository, so materializing it here would put a repository-sized slice in heap for the length of the transaction. Rows are validated and inserted one at a time as the sequence yields them, against a prepared statement inside the manifest's single transaction, so peak heap is one row however many were excluded.
func (*Store) PutObservation ¶
PutObservation appends an immutable observation. Its ID is derived from its content, so a resubmission is idempotent; an existing observation is never rewritten. The scope version must be the session's current one.
func (*Store) PutSnapshot ¶
func (s *Store) PutSnapshot(ctx context.Context, snap model.Snapshot, files func(yield func(model.FileVersion) error) error) error
PutSnapshot stores a snapshot header and its manifest rows. files streams the manifest; rows are written in bounded batches and never collected. Every nondeleted row must name a blob already retained with PutBlob. The header's file count and byte total are validated against the rows actually written.
The call is idempotent for a complete existing snapshot. An incomplete one left by a crash is replaced only while no generation references it.
func (*Store) QuarantineBlobs ¶
QuarantineBlobs demotes up to limit retained blobs that nothing references any more, stamping each with now, and reports how many it demoted. Nothing is deleted here: quarantine only starts the clock.
func (*Store) RangeConfirmed ¶
func (s *Store) RangeConfirmed(ctx context.Context, session model.SessionID, actor string, file model.FileID, hash string, r model.ByteRange) (bool, error)
RangeConfirmed reports whether [r.Start,r.End) of one pinned file is already fully confirmed served to this actor, at this content hash (Sections 16.2, 17.2). It is the check no landed method answers: a scope review may cite source intervals, and a citation over bytes the actor never confirmed would let a note mark a file read without coverage.
It returns a bounded boolean and never an interval list. A file outside this session's pinned scope, or one at a hash this session never pinned, is simply not confirmed -- (false, nil) -- because a citation over it is a claim to refuse, not a storage failure. Unlike the reporting reads it does not swallow CTX_SESSION_EXPIRED: it exists to gate a write, and an expired session confirms nothing.
func (*Store) RecordRunFailure ¶
func (s *Store) RecordRunFailure(ctx context.Context, id model.ProviderRunID, failure model.RunFailure) error
RecordRunFailure keeps the typed reason a run produced no unit on the run row. It is the only durable home for a failed unit's message and details: a unit that never sealed has no row of its own, and the capability row the fold publishes carries one exemplar scope per provider capability and excludes the raw tool output entirely.
It is written after CompleteProviderRun and therefore does not require the run to still be running; a run recorded as succeeded has no failure to write, which is why the empty reason is refused rather than stored.
func (*Store) RecordSuppliedIndex ¶
func (s *Store) RecordSuppliedIndex(ctx context.Context, gen model.GenerationID, path string, resolved bool) error
RecordSuppliedIndex records one supplied index against a generation. It is called while the generation is still staging, so the row is removed with the generation by the foreign key if the run never activates.
The write is idempotent on (generation_id, path): a caller that supplies the same path twice records one row, and the later resolution wins rather than failing the publish over a duplicate.
func (*Store) RecordWatchHeartbeat ¶
func (s *Store) RecordWatchHeartbeat(ctx context.Context, repo model.RepositoryID, hb WatchHeartbeat) error
RecordWatchHeartbeat publishes hb as this watch's live heartbeat, replacing whatever this same watch left. There is one row per watching process and no history: this is liveness, and a watch that has ended leaves nothing worth keeping.
The same write deletes the repository's rows whose deadline has elapsed, by the same rule and the same clock reading a reader judges them by, so what a reader is told is alive and what this sweep is willing to delete can never disagree. Without it the rows of processes that died without withdrawing would accumulate for ever. The sweep runs after the upsert, so the beat being published can never be its own victim.
func (*Store) RecordedBlob ¶
RecordedBlob is Blob as the ingestion side sees it: through the open group, so a capture asking whether it has already recorded a blob sees the ones it recorded since the last commit.
func (*Store) Recover ¶
Recover is startup recovery for the indexing owner (Section 12.3). The caller must hold the workspace indexing lock: it marks every staging generation failed, deletes the unsealed units and running runs those generations left behind, then runs the same collection tail as DeleteGeneration (unreachable units, orphan runs, expired leases, unreferenced snapshots and files), so a crash between a deletion's steps leaves nothing a second Recover would treat differently. The active pointer is never touched.
func (*Store) ReleaseLease ¶
ReleaseLease drops a lease; a lease already gone is not an error.
func (*Store) RenewLease ¶
RenewLease extends a live lease. An expired or released lease is CTX_CURSOR_INVALID: continuation never silently repins.
func (*Store) RetainByRef ¶
func (s *Store) RetainByRef(ctx context.Context, repo model.RepositoryID, p RetentionPolicy, now time.Time) (RetentionReport, error)
RetainByRef applies the policy to repo and reports what it did. It keeps the most recent generation of each of the last p.RetainRefs distinct refs (every ref, when the window is the unlimited 0), and when p.MaxRetainedBytes is set evicts the least recently used of those refs until the accounted retained size fits, never evicting the active generation's ref and never touching a staging generation (Recover owns those). Failed generations are swept unconditionally: a generation that never published is not a result the user indexed.
A generation a live lease or a retained session still holds is skipped rather than failing the pass; it is retained until its lease expires and counts toward BytesReclaimable.
func (*Store) RetainsLeases ¶
RetainsLeases is Continuable for a caller that holds the lease store rather than a pinned reader -- the graph engine, whose continuations are minted far from the reader that pinned them. It satisfies pagination.LeaseRetainer, so pagination.Leases answers it for the whole process.
func (*Store) SampleBlobs ¶
SampleBlobs reports up to limit retained blob hashes. It is the hash source the Section 22 content-addressed-storage check needs: Blob verifies a hash the caller already holds, and nothing else lists them. The result is bounded by maxBlobSample however large limit is, so no caller can turn a diagnostic probe into a full table scan.
func (*Store) SealUnit ¶
func (s *Store) SealUnit(ctx context.Context, w *UnitWriter) error
SealUnit validates the whole unit and makes it immutable and a member of the generation it was begun for (Section 12.2 invariants beyond DDL): evidence behind every fact, ranges inside their source, relation endpoints and alias targets visible through the unit/dependency closure, an acyclic dependency graph, and the exact FTS document count its transactions emitted.
func (*Store) SelectedUnit ¶
func (s *Store) SelectedUnit(ctx context.Context, gen model.GenerationID, providerID, scopeKey string) (model.UnitID, error)
SelectedUnit reports the unit a generation selects for one provider and scope, which is how a refresh finds the predecessor to carry over from: the previous generation's unit for the same work. A generation that selects none is CTX_ARGUMENT_INVALID with reason not_found.
func (*Store) Session ¶
func (s *Store) Session(ctx context.Context, id model.SessionID, actor string) (SessionRecord, error)
Session reads a session record for its actor. Unlike the mutating operations, a closed or expired session is still returned so status can report it honestly.
func (*Store) SessionFilePaths ¶
func (s *Store) SessionFilePaths(ctx context.Context, session model.SessionID, actor string, ids []model.FileID) (map[model.FileID]string, error)
SessionFilePaths resolves a bounded batch of file identifiers to the paths they carry in this session's pinned snapshot. Coverage rows and manifest entries carry identifiers and no path, and naming a file the operator cannot locate is not an answer.
It is scoped by session_files, not by the snapshot alone: only files this session pinned are named, so a batch can never be used to enumerate paths outside the actor's scope. An identifier that is not in the pinned scope, or whose pinned hash is no longer in the snapshot, is absent from the map; the caller decides what its own absence means. Like Coverage and CoverageSummary it answers honestly beside CTX_SESSION_EXPIRED rather than pretending the session has no scope.
func (*Store) SnapshotFile ¶
func (s *Store) SnapshotFile(ctx context.Context, id model.SnapshotID, file model.FileID) (model.FileVersion, error)
SnapshotFile reads one manifest row of a snapshot.
func (*Store) SnapshotFiles ¶
func (s *Store) SnapshotFiles(ctx context.Context, id model.SnapshotID, afterPath string, limit int) ([]model.FileVersion, error)
SnapshotFiles pages a snapshot's manifest in canonical path order, keyset on the path itself (the empty string starts from the beginning). Paths compare bytewise under the column's BINARY collation, which is the order the capture walk emits and the manifest hash folds, so a page boundary never reorders or repeats a row. limit is capped at model.MaxPageItems.
func (*Store) Stats ¶
Stats reads the row counts in one read transaction and sizes the files. A file size that cannot be read is an error, never reported as zero.
func (*Store) StoreSizes ¶
StoreSizes reports the on-disk database and write-ahead-log sizes without running a query. It is what a shallow `doctor` reads instead of Stats, whose row counts are O(rows) per table.
func (*Store) SuppliedIndexes ¶
func (s *Store) SuppliedIndexes(ctx context.Context, gen model.GenerationID) ([]SuppliedIndex, error)
SuppliedIndexes reports the supplied indexes recorded against a generation, ordered by path so two calls produce the same report. A generation with no recorded path returns an empty list, which is the answer "this generation was built with no supplied index" -- a measurement now that the recording half exists, where before the same emptiness was merely the absence of a feature.
func (*Store) SynchronousMode ¶
SynchronousMode reads `PRAGMA synchronous` back from the WRITER connection and reports it in the configuration's own spelling. It is the writer's because the writer is the only connection whose mode is a choice: readers are pinned to FULL above, so asking a reader would report FULL for every workspace and say nothing about the durability the operator configured.
It exists so a diagnostic can report the LIVE mode rather than echo the configured one. The two cannot drift while a store is open -- every pooled connection has its pragma set read back before it joins the pool -- but a check that reports what the configuration says has verified nothing, and ADR-0004 Decision 1a asks an operator to be able to read this mode back.
A mode the mapping does not know is reported as the raw pragma value rather than guessed at or failed: this is a diagnostic read, and an unexpected number is itself the answer.
func (*Store) Tokenize ¶
Tokenize splits text with the exact unicode61 tokenizer search_fts uses. Tokens are returned in document order, duplicates included, so the caller can count term and phrase occurrences without reimplementing Unicode tokenization.
func (*Store) TrashBlobs ¶
TrashBlobs is the second phase: a quarantined blob that is referenced again is restored to 'ready', and one that is still unreferenced moves to 'trash'. It takes no clock, because trashed_at marks the demotion out of 'ready' and must not be rewritten here: restamping it would restart the grace window on every pass and nothing would ever be collected.
func (*Store) UnconfirmedChunks ¶
UnconfirmedChunks counts a session's issued but unconfirmed, unexpired chunks so the coverage service can enforce its per-session cap.
func (*Store) UnitInputs ¶
func (s *Store) UnitInputs(ctx context.Context, unit model.UnitID) iter.Seq2[model.UnitInput, error]
UnitInputs streams the sealed unit's declared inputs in ascending FileID order — the same order BeginUnit required when they were written — so a caller can merge-join them against a fresh input stream without holding either list. It refuses a unit that is not sealed: a building unit's unit_inputs rows are still arriving, and a delta that diffed against a partial set would name too few replaced buckets and carry a stale fact.
The first failure ends the sequence: it is yielded with a zero UnitInput and nothing follows it.
Each page is one read transaction on the reader pool, so the stream sees the last commit and not the ingestion group in progress: the unit it reads is a predecessor, sealed and committed before the run that reads it began. The dependence delta applier walks this from inside UnitWriter.CarryOver's ingestion call, which holds the group's connection, so those reads are nested inside the writer's and compete for reader connections with the rest of the process; see Replaced.
func (*Store) UnitOrigin ¶
UnitOrigin reports the run that produced a retained unit. A reused unit keeps its original run; a rerun never rewrites it (Section 9.3).
func (*Store) UnitState ¶
func (s *Store) UnitState(ctx context.Context, id model.UnitID) (state model.UnitState, exists bool, err error)
UnitState reports the lifecycle state of the unit keyed by id and whether the row exists at all, as of the last commit: a unit the open ingestion group has begun, sealed or failed is answered as the last commit left it. The coordinator consults it before BeginUnit: a sealed unit with a matching key is reused through AttachUnit, never rebuilt. Every unit it can meet sealed was sealed by an earlier run or tick, and each of those ends in a committing Activate or Abort.
func (*Store) WALBoundBytes ¶
WALBoundBytes is the largest write-ahead log an ingestion group leaves behind: the writer's page cache, which the group fills before it commits, plus one batch of spilled pages. The log is rewound in place at every reset and keeps its high-water length, so a log past this bound is one whose frames a group actually reached: a reader kept the old ones open across several groups, or the log was never checkpointed.
func (*Store) Waive ¶
func (s *Store) Waive(ctx context.Context, req model.WaiverRequest) (model.WaiverRecord, error)
Waive records an audited exception for one scoped file. It changes no coverage and never grants strict readiness (Section 17.3).
func (*Store) WaiversAfter ¶
func (s *Store) WaiversAfter(ctx context.Context, session model.SessionID, actor string, after model.FileID, limit int) ([]model.WaiverRecord, error)
WaiversAfter pages this session's recorded coverage exceptions by keyset on file_id, the key Coverage pages session_files on. Waive is a writer whose returned record echoes the request's reason, and FileCoverage carries only the Waived flag, so this is the only reader of the stored reasons -- the text a sealed capsule has to carry to be an honest durable artifact (Section 17.3).
coverage_waivers is append-only and has no actor column: the exception is a property of the session, so ActorID comes from the resolved session record exactly as Waive sets it. Like Coverage, Observations and ManifestScopeNodes each page is its own read transaction rather than one loop inside a single snapshot: coverage_waivers is only ever appended to, and a Waive that lands between a seal's counting pass and its digest pass is caught by CapsuleCanonicalHash's count-versus-stream refusal instead of being sealed. The caller therefore never holds a session-sized list, which is the property a whole-list read could not give.
Like Coverage and CoverageSummary it answers honestly beside CTX_SESSION_EXPIRED: a capsule is sealed from what the session recorded, and an expired session still recorded it.
The keyset is file_id alone. Both coverage_waivers and session_files are keyed (session_id, file_id, content_hash), so both carry the same latent boundary if one session ever pinned one file at two content hashes; nothing in the writer path produces that today, and answering it differently here would be the only place in the package that does.
func (*Store) WatchHeartbeats ¶
func (s *Store) WatchHeartbeats(ctx context.Context, repo model.RepositoryID) ([]WatchHeartbeat, error)
WatchHeartbeats reads every heartbeat row this repository holds, freshest deadline first. An empty result is a workspace no watch has ever published in, which is a different answer from a row that has expired: nothing has run here, rather than something ran and stopped.
Expired rows are returned rather than filtered, because whether a row is live is the caller's judgement, made against ExpiresAt through Live -- and an expired row is the only evidence that a watch ran here and stopped, which is what the doctor must warn about. This method reports what was written and interprets nothing.
The order is by deadline and then by session, so two readers see one order. It is the deadline and not the beat time because two watches need not share a configuration, and under different windows the two orders diverge; the deadline is the stamp liveness is judged by, so it is the one that ranks.
func (*Store) WriterBusy ¶
WriterBusy is the writer's busy time: how long the goroutine that owns the writer connection has spent running jobs, commits and checkpoints included, since the store opened. The index build reports it against a stage's wall (ADR-0012 decision 5): the share of the wall the one writer the log admits was busy says whether writing is what bounds the stage. A read-only store has no writer and reports zero.
type StoredAlias ¶
StoredAlias is one canonical identity a scoped native key resolves to. The canonical key is what the resolver orders and tie-breaks on (Section 9.4: never by completion order), and what a provider must copy into the node facts it publishes under that identity.
type StoredEvidence ¶
StoredEvidence is one visible evidence row, with the byte interval separate for the same reason as StoredNode.
type StoredNode ¶
type StoredNode struct {
Node model.Node
Bytes *model.ByteRange
UnitID model.UnitID
SourceBinding model.SourceBinding
}
StoredNode is one visible node fact. Ranges are stored as bytes only; the consumer derives line/column positions from the source's line checkpoints, so the model.Node here carries no Range and Bytes holds the interval.
type SuppliedIndex ¶
SuppliedIndex is one `--scip-index` path recorded against a generation, with whether the generation ended up holding a unit built from it.
Path is recorded even when nothing resolved: that is the whole point of the record. A path that matched no file plans no unit, so a reader looking only at units cannot tell a typo from a run that supplied no index at all.
type TermOccurrence ¶
type TermOccurrence struct {
RowID int64
Column SearchColumn
Count int64
Offsets []int64
Truncated bool
}
TermOccurrence is one term's instances in one column of one visible document. Offsets ascend, truncated at MaxTermOffsets; a phrase count from truncated offsets is a lower bound, which Truncated says.
type UnitWriter ¶
type UnitWriter struct {
// contains filtered or unexported fields
}
UnitWriter receives one unit's facts in bounded batches while the unit is building. Only the store hands one out; providers reach it through the Task 6 sink and never see SQL.
func (*UnitWriter) Abandon ¶
func (w *UnitWriter) Abandon(ctx context.Context) error
Abandon gives up a building unit without deleting the rows it wrote: the unit is marked failed, which is all that is needed to keep it invisible (every read joins generation_units, and an abandoned unit is a member of no generation) and to keep a rebuild of the same scope from colliding with it. Its rows are reclaimed by the next Recover and by retention, through collectUnreachableUnits.
This is the cancel path and only the cancel path. Fail's cascade walks every fact, identity and search document the unit wrote, which for a large unit is seconds to minutes of uncancellable work; running it when the caller has already asked the process to stop turns Ctrl-C into a long wait for a rollback whose result is discarded either way.
func (*UnitWriter) CarryOver ¶
func (w *UnitWriter) CarryOver(ctx context.Context, prev model.UnitID, replaced Replaced) (CarryOverStats, error)
CarryOver inherits the sealed unit prev's facts into the unit being built, minus everything Replaced names. It must run after the provider's own batches and before SealUnit: every insert it issues yields to a row already present, so the fresh import always wins over its predecessor.
The whole body is one ingestion call on the single writer connection, and draining Replaced's three streams happens inside it: a producer stream that reads the store runs nested in this call and on the reader pool, seeing the last commit (see Replaced).
Carrying a row asserts that its source has not changed, so the whole call is refused unless every file the previous unit located facts in, and that Replaced does not name, is declared by this unit with the same content hash. A file that was edited but not named, or deleted from the snapshot, is therefore a typed refusal and never a stale fact.
func (*UnitWriter) EvidenceClipped ¶
func (w *UnitWriter) EvidenceClipped() int64
EvidenceClipped is the number of occurrence rows SealUnit dropped to hold the Section 11.1 bound on evidence per fact. It is zero until the unit is sealed. A nonzero count is not a failure; it is what keeps the truncation from being silent, and SealUnit itself logs one bounded warning naming the unit and the count, so the drop is on the record even for a caller that never reads this.
func (*UnitWriter) Fail ¶
func (w *UnitWriter) Fail(ctx context.Context) error
Fail discards a building unit and every row it wrote. Unsealed output is deleted, never quarantined into visibility.
func (*UnitWriter) PutAliases ¶
func (w *UnitWriter) PutAliases(ctx context.Context, aliases []model.NativeAlias) error
PutAliases stores scoped native aliases. The target must be a registered identity now and a visible node in the unit/dependency closure at seal.
func (*UnitWriter) PutDeltaState ¶
PutDeltaState stores one provider-owned incremental-refresh artifact with the unit it describes: the SCIP DocumentManifest, the dependence KeySet, or whatever a later provider needs to diff its next run without recomputing the previous one. The payload is opaque to storage and read from src in parts, so an artifact is never held whole; there is no bound on its size. It shares the unit's lifetime exactly, so a retired unit takes its artifact with it and no refresh can diff against state whose facts were collected. A second call under the same kind replaces the first.
func (*UnitWriter) PutKeyedNodes ¶
func (w *UnitWriter) PutKeyedNodes(ctx context.Context, facts []model.NodeFact, keys [][]string) error
PutKeyedNodes stores node facts with their evidence in one transaction. Each fact's identity is recomputed from the repository, its kind and its canonical key (Section 9.1) and registered in the node_ids dictionary before the fact row references it; a fact whose ID does not derive, or whose kind disagrees with the identity already registered under that ID, is malformed provider output.
keys is either empty or parallel to facts: keys[i] is every producer key backing facts[i] — the keys its next incremental run will classify as changed, unchanged or removed (Section 11.4) — sorted, without duplicates, each a lowercase hex digest. A fact is backed by more than one key whenever the producer's keys are finer than the identities they resolve to, which is ordinary: a canonical edge is published once and derived from N occurrences. Storage never derives or interprets a key; it records every one so CarryOver can drop the fact when ANY of them is replaced, which is the same condition the producer re-emits the whole fact on.
A repeated node identity within one unit is not an error while it is the same fact: the same declaration can be observed by more than one worker, by two parts of a subdivided unit or, under a delta, by both the fresh import and the carried predecessor, and its keys accumulate. A repeat whose stored columns DIVERGE from the row already written is refused (CTX_PROVIDER_OUTPUT_INVALID) naming the node and the first differing column: the row would otherwise keep whichever description arrived first, and a delta-built unit would keep a different one from a full re-import of the same source. Nothing of a refused fact is stored, its evidence included.
func (*UnitWriter) PutKeyedRelations ¶
func (w *UnitWriter) PutKeyedRelations(ctx context.Context, facts []model.RelationFact, keys [][]string) error
PutKeyedRelations stores canonical edges with their occurrence evidence. Relation identity is recomputed; both endpoints must already be registered identities. keys carries every producer key backing each fact, exactly as PutKeyedNodes documents. A relation_facts row stores nothing but its identity, so a repeat cannot diverge the way a node fact can; it adds its keys to the ones already recorded and is otherwise a no-op.
func (*UnitWriter) PutNodes ¶
PutNodes stores node facts with their evidence, recording no delta keys.
func (*UnitWriter) PutRelations ¶
func (w *UnitWriter) PutRelations(ctx context.Context, facts []model.RelationFact) error
PutRelations stores canonical edges with their occurrence evidence, recording no delta keys.
func (*UnitWriter) PutSearchUnits ¶
func (w *UnitWriter) PutSearchUnits(ctx context.Context, docs []model.SearchUnit) error
PutSearchUnits stores lexical documents and their FTS index rows in the same transaction. The FTS insert is explicit: search_fts is contentless (ADR-0003 §2.1), so nothing maintains it by cascade or by row counts and the body text reaches the index from this document and nowhere else -- the database keeps no second copy of it (Section 12.2). Each posting's rowid is recorded as the row's doc_id, which is what every read resolves a document by and what a delta carry-over inherits instead of re-indexing.
token_count is computed here with the index's own unicode61 tokenizer over exactly the columns search_fts indexes, so the Section 12.4 length statistics agree with the index. SearchUnit.TokenCount is informational on input and ignored; on read it is the stored, authoritative value.
func (*UnitWriter) UnitID ¶
func (w *UnitWriter) UnitID() model.UnitID
UnitID is the immutable key of the unit being written.
type WatchHeartbeat ¶
type WatchHeartbeat struct {
SessionID string
WriterPID int
BeatAt time.Time
LastPassAt *time.Time
PendingEvents *int64
ExpiresAt time.Time
}
WatchHeartbeat is what one running watch publishes about itself so a second process can read it: which watch it is, which process owns it, when it last published, when its last pass completed, how many notification events are still pending, and the deadline the writer promised to refresh before.
SessionID is the watch's own identity, minted once per watch. It, and not the pid, is what tells two watching processes apart: a pid is reused by the operating system, so a row keyed by one could be claimed by an unrelated process that inherited the number.
ExpiresAt is the writer's own deadline and the only liveness signal. A reader compares it to the clock and never recomputes it from configuration: the two processes need not share a configuration, and a reader that derived the window would grant or deny liveness the writer never promised.
BeatAt is when the writer last published the row. It is reported beside the watch, never judged: deriving liveness from it would be the reader reconstructing the writer's window, which is what ExpiresAt exists to prevent.
LastPassAt and PendingEvents are pointers because absent and zero are different answers. A watch driven only by periodic reconciliation has no notification queue to count, and a watch that has not completed a pass yet has no pass time and publishes no pending count either; reporting either as zero would state that the watch is caught up. A row with no pass time is therefore a watch that is running and covering nothing -- waiting for whichever process holds the workspace -- which is a third answer beside a covering watch and no row at all.
func (WatchHeartbeat) Live ¶
func (hb WatchHeartbeat) Live(now time.Time) bool
Live reports whether the process that published this row is still refreshing it, which is the one rule this product answers "is that process still there" by: the writer's own unelapsed deadline, compared against the reader's clock. It is the rule the run ledger judges a run by, stated once here so that every surface reading a heartbeat -- the status listing, the doctor's check, the resource block -- and the sweep below cannot come to different conclusions about the same row. No pid is probed: probing one is neither portable nor race-free, and the number may since have been reused.
Source Files
¶
- adjacency.go
- capsule_rows.go
- carry.go
- context.go
- delta.go
- diraccess_unix.go
- engine.go
- gc.go
- graphbuild.go
- graphreader.go
- heartbeat.go
- ids.go
- intern.go
- lease.go
- lexicalbuild.go
- lexicaldoc.go
- lexicalmerge.go
- lexicalread.go
- lexicalstage.go
- open.go
- pageclamp_alias.go
- query.go
- reconcile.go
- retention.go
- schema.go
- search.go
- snapshots.go
- source.go
- state.go
- stmtcache.go
- supplied.go
- units.go