Documentation
¶
Index ¶
- Variables
- func IsNotFound(err error) bool
- func WrapNotFound(err error) error
- type BatchDependentStore
- type BatchStore
- type BuildStore
- type ChangeStore
- type Config
- type Factory
- type PathBuildStore
- type QueueBatchStateStore
- type RequestBatchStore
- type RequestLogStore
- type RequestQueueSummaryCursor
- type RequestQueueSummaryQuery
- type RequestQueueSummaryStore
- type RequestStore
- type RequestSummaryStore
- type RequestURIStore
- type SpeculationPathSetStore
- type Storage
Constants ¶
This section is empty.
Variables ¶
var ErrAlreadyExists = errors.New("record already exists")
ErrAlreadyExists is returned by storage implementations when attempting to create a record with an ID that already exists.
var ErrNotFound = errors.New("record not found")
ErrNotFound is returned by storage implementations when the requested record is not found in the database.
var ErrVersionMismatch = errs.NewRetryableError(errors.New("version mismatch"))
ErrVersionMismatch is returned by storage implementations when the expected entity version does not match the current version of the object. This is used to implement an optimistic locking mechanism, allowing multiple clients to update the same entity concurrently and either retry or implement idempotent operations. It is intrinsically a retryable infrastructure error.
Functions ¶
func IsNotFound ¶
IsNotFound returns true if any error in the error chain is a ErrNotFound.
func WrapNotFound ¶
WrapNotFound wraps ErrNotFound with the original error from the storage implementation.
Types ¶
type BatchDependentStore ¶
type BatchDependentStore interface {
// Get retrieves the batch dependent by batch ID.
// If the batch contains no dependents, the returned BatchDependent will have an empty Dependents list.
// Returns ErrNotFound if no reverse-index row exists for the batch.
Get(ctx context.Context, batchID string) (entity.BatchDependent, error)
// Create creates a new batch dependent.
// Returns ErrAlreadyExists if the entry already exists for the given batch ID.
Create(ctx context.Context, batchDependent entity.BatchDependent) error
// Update replaces the non-key fields of a batch dependent and persists newVersion
// if the current persisted version matches oldVersion. If versions do not match, returns ErrVersionMismatch.
// Version arithmetic is owned by the caller; the store performs a pure conditional write.
Update(ctx context.Context, batchDependent entity.BatchDependent, oldVersion, newVersion int32) error
}
BatchDependentStore is an interface that defines methods for managing batch dependent information in the database.
A BatchDependent is a reverse index ("batches that depend on me") paired one-to-one with a Batch. The batch-creation flow creates this row while the Batch is Creating and before making the Batch eligible for pipeline processing. A Creating Batch can briefly exist without its row; every Batch that reaches Created is guaranteed to have one.
type BatchStore ¶
type BatchStore interface {
// Get retrieves a batch by ID. Returns ErrNotFound if the batch is not found.
Get(ctx context.Context, id string) (entity.Batch, error)
// Create creates a new batch. The batch must have a unique ID already assigned.
// Returns ErrAlreadyExists if a batch with the same ID already exists.
Create(ctx context.Context, batch entity.Batch) error
// Update replaces every non-key field of a batch and writes newVersion
// if the current persisted version matches oldVersion. If versions do not match, returns ErrVersionMismatch.
// Version arithmetic is owned by the caller; the store performs a pure conditional write.
Update(ctx context.Context, batch entity.Batch, oldVersion, newVersion int32) error
}
BatchStore is an interface that defines methods for managing batches in the database.
type BuildStore ¶
type BuildStore interface {
// Get retrieves a build by ID. Returns ErrNotFound if the build is not found.
Get(ctx context.Context, id string) (entity.Build, error)
// Create creates a new build. The build must have a unique ID already assigned.
// Returns ErrAlreadyExists if a build with the same ID already exists.
Create(ctx context.Context, build entity.Build) error
// Update replaces all non-key fields of a build.
Update(ctx context.Context, build entity.Build) error
}
BuildStore is an interface that defines methods for managing builds in the database.
type ChangeStore ¶
type ChangeStore interface {
// Create persists a single ChangeRecord (identity + Details) in one write. A
// primary-key conflict on (Queue, URI, RequestID) is silently ignored, which makes
// the call idempotent under queue redeliveries of the same request (first write
// wins). Records belonging to different requests do not conflict on the PK —
// cross-request overlap is detected by GetByURI, not by Create.
Create(ctx context.Context, record entity.ChangeRecord) error
// GetByURI returns every ChangeRecord the bound queue holds for the given
// URI. Multiple requests can have claimed the same URI over time, so the
// slice may have any number of entries; an empty slice means no claim has
// ever been recorded for this URI in this queue.
//
// The store does not filter by request_id or by the owning request's
// state — callers that want to skip self filter by RequestID, and callers
// that want only live owners consult RequestStore for liveness.
GetByURI(ctx context.Context, uri string) ([]entity.ChangeRecord, error)
}
ChangeStore manages per-URI claim records for in-flight land requests. Each row records that a specific URI was claimed by a specific request, scoped to a queue. The (Queue, URI, RequestID) triple is the immutable identity of a record, and a record's Details are captured at claim time and never updated — records are immutable.
The interface is intentionally per-record / per-URI so that any backend (SQL, DynamoDB, Bigtable, …) can implement it without needing batch-atomicity or multi-key query support. Callers loop when they have multiple URIs to claim or check; the typical request has a small number of URIs (a single PR or a short stack), so the loop overhead is negligible.
type Config ¶
type Config struct {
// QueueName is the name of the queue whose data the resolved Storage is
// scoped to.
QueueName string
}
Config identifies the queue a Storage instance is resolved for. Like every other extension config, it carries only the queue name — everything an implementation needs beyond that is injected at construction by the integrator.
type Factory ¶
type Factory interface {
// For returns the Storage aggregate bound to the queue named in config.
For(config Config) (Storage, error)
}
Factory resolves the queue-scoped Storage aggregate for a queue. Mirrors the extension contract: the host wiring decides which backend serves which queue; implementations bind the queue over their backend so a resolved instance can only read and write that queue's data.
type PathBuildStore ¶
type PathBuildStore interface {
// Get resolves an attempt to its build.
// Returns ErrNotFound if no build has been recorded for the attempt.
Get(ctx context.Context, pathID string, attempt int) (entity.PathBuild, error)
// Create records the build for an attempt, permanently.
// Returns ErrAlreadyExists if the attempt already has a build, which means
// a concurrent dispatch recorded its own first.
Create(ctx context.Context, pathBuild entity.PathBuild) error
}
PathBuildStore resolves one attempt of one speculation path to the build started for it, keyed by (path ID, attempt).
It exists because the runner chooses the build ID: a caller holding a path and an attempt cannot derive it, and there is no lookup by attribute in this contract. Promoting the relationship to a keyed record is the mechanism for a reverse lookup here, not a workaround.
A record is write-once and complete from the start: it is created only once the runner has named the build, and never updated afterwards. It answers two questions — absent means no build is recorded for the attempt, present names the attempt's build permanently. A retried path is a new attempt under a different key.
Because creation is the only write, a duplicate Create is how concurrent dispatches for the same attempt are decided: the first insert wins, and ErrAlreadyExists tells the loser the attempt's build is someone else's.
type QueueBatchStateStore ¶
type QueueBatchStateStore interface {
// List returns every record filed under the bound queue's given state bucket.
// An empty slice means the bucket is empty. Order is unspecified.
List(ctx context.Context, state entity.BatchState) ([]entity.QueueBatchState, error)
// Put persists a record. The record's Queue must match the instance's bound
// queue. Writing an already-existing (queue, state, batchID) record is a no-op
// success, so the call is idempotent under redeliveries.
Put(ctx context.Context, record entity.QueueBatchState) error
// Delete removes the bound queue's record identified by (state, batchID).
// Deleting an absent record is a no-op success, so the call is idempotent
// under redeliveries.
Delete(ctx context.Context, state entity.BatchState, batchID string) error
}
QueueBatchStateStore manages the per-queue membership records that file each in-queue batch under a lifecycle state bucket, so "the batches of queue Q filed under state S" is a single read keyed by (queue, state) — a primary-key prefix on any backend, with no secondary index or server-side filtering required.
Records are advisory: the authoritative state is on the Batch entity, and a record may transiently file a batch under a bucket it has already left. Readers therefore treat a listing as a set of candidate batch IDs — they load each Batch by key and classify it by its own State, never by the bucket the record was found in.
The interface is intentionally per-state and per-record so that any backend (SQL, DynamoDB, Bigtable, …) can implement it without multi-key queries or batch atomicity. Callers loop over the states they care about; a batch moves buckets via Put of the new record followed by Delete of the old one, which keeps at least one record visible throughout. All writes are idempotent so queue redeliveries can safely repeat them.
type RequestBatchStore ¶
type RequestBatchStore interface {
// GetByRequestID retrieves every batch association for a request.
GetByRequestID(ctx context.Context, requestID string) ([]entity.RequestBatch, error)
// Create inserts an immutable association. Returns ErrAlreadyExists if the request and batch are already associated.
Create(ctx context.Context, association entity.RequestBatch) error
}
RequestBatchStore persists immutable associations between requests and the batch attempts containing them.
type RequestLogStore ¶
type RequestLogStore interface {
// Insert appends a new request log record. Timestamps should be generated by the caller and not modified by the implementation.
Insert(ctx context.Context, log entity.RequestLog) error
// List retrieves all request log records for a given request ID, ordered by timestamp ascending.
// Returns ErrNotFound if no records exist for the given request ID.
List(ctx context.Context, requestID string) ([]entity.RequestLog, error)
}
RequestLogStore is an interface that defines methods for managing request log records in an append-only database. Request logs are used to reconcile request statuses with eventual consistency into a separate database from RequestStore.
type RequestQueueSummaryCursor ¶
type RequestQueueSummaryCursor struct {
// ReceivedAtMs is the receipt timestamp of the last item from the previous page.
ReceivedAtMs int64
// RequestID is the request ID of the last item from the previous page.
RequestID string
}
RequestQueueSummaryCursor is the exclusive keyset boundary for a descending queue-summary query.
type RequestQueueSummaryQuery ¶
type RequestQueueSummaryQuery struct {
// ReceivedAtOrAfterMs is the inclusive lower receipt-time bound.
ReceivedAtOrAfterMs int64
// ReceivedBeforeMs is the exclusive upper receipt-time bound.
ReceivedBeforeMs int64
// Cursor is an exclusive continuation boundary when HasCursor is true.
Cursor RequestQueueSummaryCursor
// HasCursor selects whether Cursor participates in the query.
HasCursor bool
// Limit is the maximum number of rows returned and must be positive.
Limit int
}
RequestQueueSummaryQuery specifies one bounded queue-summary page query against the bound queue's partition.
type RequestQueueSummaryStore ¶
type RequestQueueSummaryStore interface {
// Create inserts summary and returns ErrAlreadyExists when its full primary key already exists.
// The summary's Queue must match the instance's bound queue.
Create(ctx context.Context, summary entity.RequestQueueSummary) error
// Get returns the bound queue's row identified by (receivedAtMs, requestID), or ErrNotFound when absent.
Get(ctx context.Context, receivedAtMs int64, requestID string) (entity.RequestQueueSummary, error)
// Update conditionally replaces all non-key fields when the persisted projection version equals oldVersion.
// The store writes newVersion exactly as supplied and returns ErrVersionMismatch when the guard does not match.
Update(ctx context.Context, summary entity.RequestQueueSummary, oldVersion, newVersion int32) error
// List returns at most query.Limit rows ordered by received_at_ms descending, then request_id descending.
List(ctx context.Context, query RequestQueueSummaryQuery) ([]entity.RequestQueueSummary, error)
}
RequestQueueSummaryStore persists the queue-ordered request projection.
type RequestStore ¶
type RequestStore interface {
// Get retrieves a land request by ID. Returns ErrNotFound if the request is not found.
Get(ctx context.Context, id string) (entity.Request, error)
// Create creates a new land request. The request must have a unique ID already assigned.
// Returns ErrAlreadyExists if a request with the same ID already exists.
Create(ctx context.Context, request entity.Request) error
// Update replaces every non-key field of a land request and writes newVersion if the current persisted version matches oldVersion.
// If versions do not match, returns ErrVersionMismatch. Version arithmetic is owned by the caller; the store performs a pure conditional write.
Update(ctx context.Context, request entity.Request, oldVersion, newVersion int32) error
}
RequestStore is an interface that defines methods for managing land requests in the database.
type RequestSummaryStore ¶
type RequestSummaryStore interface {
// Create inserts summary and returns ErrAlreadyExists when RequestID already exists.
// The caller owns retry identity and decides whether an existing row is an identical retry or a conflict.
Create(ctx context.Context, summary entity.RequestSummary) error
// Get returns the summary for requestID, or ErrNotFound when absent.
Get(ctx context.Context, requestID string) (entity.RequestSummary, error)
// Update conditionally replaces every non-key field when the persisted projection version equals oldVersion.
// The store writes newVersion exactly as supplied and returns ErrVersionMismatch when the guard does not match.
Update(ctx context.Context, summary entity.RequestSummary, oldVersion, newVersion int32) error
}
RequestSummaryStore persists the authoritative request-ID materialized view.
type RequestURIStore ¶
type RequestURIStore interface {
// Create inserts mapping and returns ErrAlreadyExists when its full primary key already exists.
Create(ctx context.Context, mapping entity.RequestURI) error
// ListByURI returns at most limit mappings ordered by received_at_ms descending, then request_id descending.
ListByURI(ctx context.Context, changeURI string, limit int) ([]entity.RequestURI, error)
}
RequestURIStore persists the immutable change-URI reverse mapping.
type SpeculationPathSetStore ¶
type SpeculationPathSetStore interface {
// Get retrieves a head's path set, where head is the head batch's ID.
// Returns ErrNotFound if the head has no set yet, which is the normal state
// for a batch nothing has speculated on.
Get(ctx context.Context, head string) (entity.SpeculationPathSet, error)
// Create stores a head's first path set.
// Returns ErrAlreadyExists if the head already has one.
Create(ctx context.Context, set entity.SpeculationPathSet) error
// Update replaces the stored set with set and writes newVersion, but only if
// the persisted version still matches oldVersion. If it does not, returns
// ErrVersionMismatch and writes nothing.
//
// The whole entity goes in rather than the fields being changed: a set is
// replaced wholesale, so this is a conditional put on a key — the primitive
// every backend offers directly, instead of a field-level update each
// non-SQL backend would have to emulate with a read-modify-write.
//
// set.Version is ignored. oldVersion is the guard and newVersion is the
// value written, so version arithmetic stays with the caller: compute
// newVersion, call, and assign it to the in-memory set only once this
// returns nil.
Update(ctx context.Context, set entity.SpeculationPathSet, oldVersion, newVersion int32) error
}
SpeculationPathSetStore persists one head batch's chosen speculation paths.
A set is keyed by its head batch ID within the bound queue and versioned as a whole: every path in it shares that head, and the set is the unit of both replacement and optimistic locking. There is no lookup by anything but the head — callers that need a queue's live sets enumerate the heads from the batch listing they already hold and read each set by key.
type Storage ¶
type Storage interface {
// GetRequestStore returns the RequestStore instance.
GetRequestStore() RequestStore
// GetRequestBatchStore returns the RequestBatchStore instance.
GetRequestBatchStore() RequestBatchStore
// GetChangeStore returns the ChangeStore instance.
GetChangeStore() ChangeStore
// GetBatchStore returns the BatchStore instance.
GetBatchStore() BatchStore
// GetBatchDependentStore returns the BatchDependentStore instance.
GetBatchDependentStore() BatchDependentStore
// GetQueueBatchStateStore returns the QueueBatchStateStore instance.
GetQueueBatchStateStore() QueueBatchStateStore
// GetBuildStore returns the BuildStore instance.
GetBuildStore() BuildStore
// GetSpeculationPathSetStore returns the SpeculationPathSetStore instance.
GetSpeculationPathSetStore() SpeculationPathSetStore
// GetPathBuildStore returns the PathBuildStore instance.
GetPathBuildStore() PathBuildStore
// GetRequestQueueSummaryStore returns the RequestQueueSummaryStore instance.
GetRequestQueueSummaryStore() RequestQueueSummaryStore
// GetRequestSummaryStore returns the RequestSummaryStore instance.
GetRequestSummaryStore() RequestSummaryStore
// GetRequestLogStore returns the RequestLogStore instance.
GetRequestLogStore() RequestLogStore
// GetRequestURIStore returns the RequestURIStore instance.
GetRequestURIStore() RequestURIStore
}
Storage aggregates the queue-scoped entity stores into a single injectable dependency. An instance is resolved per queue through Factory and is bound to that queue: entity arguments whose Queue field disagrees with the binding are rejected, and reads never surface another queue's records.