runnerspool

package
v0.9.3 Latest Latest
Warning

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

Go to latest
Published: Oct 18, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

Documentation

Overview

Package runnerspool stores proxy decisions until the authenticated runner stream acknowledges their durable org ingestion. The proxy and daemon use the same private directory, serialized by a process-shared file lock.

Index

Constants

View Source
const (
	// ReplayBatch bounds the uncommitted local ACK window retained by org receipts.
	ReplayBatch = 64
	MaxRecords  = 50000
	MaxBytes    = 32 << 20
	// MaxRecordBytes leaves the record envelope below the wire control bound.
	MaxRecordBytes = (1 << 20) - 4096
)
View Source
const (
	GrantRecords      = 5000
	GrantReservations = 16
	GrantBytes        = GrantReservations * MaxRecordBytes
	LossReserveBytes  = 4 << 10
)

Remote-sink grant ceilings (design §3.2 item 2). The last record slot and the last LossReserveBytes stay free for the builtin:spool-lost row. The byte credit covers GrantReservations requests reserved at the largest record (a scanned body-bearing request reserves MaxRecordBytes), as the local spool admits them, within the spool's own MaxBytes.

Variables

View Source
var (
	// ErrProducer refuses a frame from a foreign, settled or out-of-order producer.
	ErrProducer = errors.New("spool refuses this producer sequence")
	// ErrStaleGen refuses a frame from a superseded sink session.
	ErrStaleGen = errors.New("spool sink session superseded")
)
View Source
var ErrFull = errors.New("runner decision spool is full")
View Source
var ErrSealed = errors.New("runner spool is durably sealed")

ErrSealed refuses new evidence production after exact settled retirement.

View Source
var ErrWriterHeld = errors.New("spool writer lock is held")

ErrWriterHeld means another handle owns the spool's writer lock.

Functions

This section is empty.

Types

type Disposition added in v0.9.3

type Disposition string

Disposition is the one durable outcome of a producer sequence.

const (
	Appended Disposition = "appended"
	Refused  Disposition = "refused"
	Lost     Disposition = "lost"
)

type Drained added in v0.9.3

type Drained struct {
	Gen      uint64
	FinalSeq int64
	Consumed uint64
}

Drained is a proxy's drained reply: its last sequence and the credit units it consumed (sequences plus reservations still held).

type Grant added in v0.9.3

type Grant struct {
	Gen     uint64 `json:"gen"`
	Base    int64  `json:"base"`
	Through int64  `json:"through"`
	Bytes   int64  `json:"bytes"`
}

Grant is write-ahead credit: sequences Base+1..Through carrying at most Bytes of record bodies. Its liability is Through - Producer.Seq.

type Liability added in v0.9.3

type Liability struct {
	Gen         uint64
	Producer    *Producer
	Grant       *Grant
	PendingLoss *PendingLoss
	Sealed      bool
}

Liability is the remote producer's durable state, and whether the spool is sealed.

func (Liability) Settled added in v0.9.3

func (l Liability) Settled() bool

Settled reports a producer whose liability has been written off.

type Loss added in v0.9.3

type Loss struct {
	Count uint64
	Basis LossBasis
}

Loss is one settlement's count and basis.

type LossBasis added in v0.9.3

type LossBasis string

LossBasis says whether a pending loss count is exact or an upper bound.

const (
	LossExact      LossBasis = "exact"
	LossUpperBound LossBasis = "upper_bound"
)

type PendingLoss added in v0.9.3

type PendingLoss struct {
	Count uint64    `json:"count"`
	Basis LossBasis `json:"basis"`
}

PendingLoss is settled loss not yet emitted as a builtin:spool-lost row.

type Produced added in v0.9.3

type Produced struct {
	Gen      uint64
	Instance string
	Seq      int64
	Hash     string
	Admitted bool
	Decision egress.DecisionLog
}

Produced is one record frame from the remote proxy.

type Producer added in v0.9.3

type Producer struct {
	Instance    string      `json:"instance"`
	Seq         int64       `json:"seq"`
	Hash        string      `json:"hash,omitempty"`
	Disposition Disposition `json:"disposition,omitempty"`
	Refused     uint64      `json:"refused,omitempty"`
}

Producer is the remote proxy's durable position: the last sequence with a disposition, its body hash, and the refusal total already counted in Drops.

type Record

type Record struct {
	SpoolID  uuid.UUID          `json:"spool_id"`
	Seq      int64              `json:"seq"`
	Hash     string             `json:"hash"`
	Decision egress.DecisionLog `json:"decision"`
	Count    uint64             `json:"count,omitempty"`
	DropEnd  uint64             `json:"drop_end,omitempty"`
}

Record is one stable, replayable decision. Seq zero is an overflow summary, whose durable identity is its hash; ordinary records have positive sequences.

func (Record) Marshal

func (r Record) Marshal() ([]byte, error)

Marshal is the bounded EVENT.spool payload; no run token authenticates replay.

func (Record) Position

func (r Record) Position() ackcursor.Pos

Position is the acknowledgement cursor's sequence and content identity.

func (Record) ValidHash

func (r Record) ValidHash() bool

ValidHash refuses modified or cross-stream rows before durable ingestion.

type Session added in v0.9.3

type Session struct {
	Gen   uint64
	Seq   int64
	Grant Grant
}

Session is an accepted sink session's durable start.

type Spool

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

Spool is safe across goroutines and independently opened proxy/daemon handles.

func Open

func Open(dir string, runID uuid.UUID) (*Spool, error)

Open refuses unsafe or corrupt existing state. A missing directory starts a new private stream; an existing stream never silently resets its cursor.

func (*Spool) Accept added in v0.9.3

func (s *Spool) Accept(ctx context.Context, instance string) (out Session, err error)

Accept starts a session for instance at a new generation and, in the same write, issues its grant or re-issues the outstanding one no lower. A settled producer is never reopened; only Succeed opens the spool to a new instance.

func (*Spool) AckOverflow

func (s *Spool) AckOverflow(ctx context.Context, row Record) error

AckOverflow records only the frozen interval, retaining later refusals.

func (*Spool) After

func (s *Spool) After(ctx context.Context, seq int64, limit int) (rows []Record, err error)

After implements ackcursor.Source. The acknowledged row's identity lives in the manifest, so reclaimed payloads do not make ackcursor infer a reset.

func (*Spool) Append

func (s *Spool) Append(ctx context.Context, decision egress.DecisionLog) error

Append commits before returning. An unreserved producer must refuse on any error; the proxy separately reserves capacity before forwarding requests.

func (*Spool) AppendAdmitted

func (s *Spool) AppendAdmitted(ctx context.Context, decision egress.DecisionLog) error

AppendAdmitted finishes a request whose proxy already reserved capacity. Overflow refuses new requests, never discards this previously admitted tail.

func (*Spool) CanRecord

func (s *Spool) CanRecord(ctx context.Context) error

CanRecord checks durable capacity. Concurrent producers must also reserve capacity; checking alone cannot guarantee space for a later append. Refusals not yet reported do not refuse a spool with room (see append).

func (*Spool) Close

func (s *Spool) Close() error

Close releases this process's handles; it never deletes pending decisions.

func (*Spool) EmitLoss added in v0.9.3

func (s *Spool) EmitLoss(ctx context.Context) (emitted bool, err error)

EmitLoss turns pending_loss into one ordinary builtin:spool-lost row in the loss reserve, clearing pending_loss in the same write. Unreported Drops never refuse it; while an earlier unacknowledged row still fills the reserve it waits (emitted=false) for the next call.

func (*Spool) Get

func (s *Spool) Get(ctx context.Context) (pos ackcursor.Pos, err error)

Get implements ackcursor.Store.

func (*Spool) HoldWriter added in v0.9.3

func (s *Spool) HoldWriter() (*os.File, error)

HoldWriter takes the writer lock for a remote producer's lifetime, as a live local proxy holds it: Seal defers until the returned file is closed.

func (*Spool) Liability added in v0.9.3

func (s *Spool) Liability(ctx context.Context) (out Liability, err error)

Liability reads the remote producer's durable state.

func (*Spool) Overflow

func (s *Spool) Overflow(ctx context.Context) (row *Record, err error)

Overflow freezes one cumulative refused-attempt interval before transmission. A crash or new refusals cannot change the identity/count of an in-flight row.

func (*Spool) Produce added in v0.9.3

func (s *Spool) Produce(ctx context.Context, p Produced) (disp Disposition, fresh bool, err error)

Produce gives a producer sequence its one durable disposition, in the same manifest write as its effect: appended (Last/Bytes), refused (Drops, an unreserved record only) or lost (pending_loss, an admitted record that still did not fit). A resend of the last sequence returns the stored disposition with fresh=false; an older one returns "" (durable, nothing to redo).

func (*Spool) ProducerRefused added in v0.9.3

func (s *Spool) ProducerRefused(ctx context.Context, gen uint64, instance string, total uint64) error

ProducerRefused counts the instance's cumulative refused admissions into Drops once: a resent or older total changes nothing.

func (*Spool) Receipt

func (s *Spool) Receipt(ctx context.Context) (out types.RunnerSpoolReceipt, err error)

Receipt reads durable acceptance, not the unacknowledged current head.

func (*Spool) Refuse

func (s *Spool) Refuse(ctx context.Context) error

Refuse durably counts one egress admission refused because capacity is full.

func (*Spool) Regrant added in v0.9.3

func (s *Spool) Regrant(ctx context.Context, gen uint64, instance string) (g Grant, changed bool, err error)

Regrant persists fresh credit for the current session before it is sent, returning changed=false when the outstanding grant already equals it.

func (*Spool) Replay

func (s *Spool) Replay(ctx context.Context, send func(context.Context, []Record) error) error

Replay uses the shared acknowledgement cursor. send gets each batch in seq order and must return nil only after durable checked org ingestion of every row in it (H2 Peer.EventsAck), not after a write.

Replay ends once it has delivered the head (rows and refused attempts) seen when it started. Rows a live proxy appends meanwhile wait for the next call, so a producer faster than the drain cannot keep Replay running.

func (*Spool) Seal

func (s *Spool) Seal(ctx context.Context, expected types.RunnerSpoolReceipt) (sealed bool, err error)

Seal requires the proxy's lifetime writer lock and an exact fully accepted current head. A still-running producer or unsettled evidence defers sealing.

func (*Spool) Set

func (s *Spool) Set(ctx context.Context, pos ackcursor.Pos) error

Set commits an acknowledged cursor before reclaiming any payload. A crash during reclamation leaves harmless acknowledged files, never missing history.

func (*Spool) SettleLoss added in v0.9.3

func (s *Spool) SettleLoss(ctx context.Context, drained *Drained) (loss Loss, err error)

SettleLoss writes off the remote producer's liability once it is proven gone (completion evidence; never a NotFound alone): exact when drained answered at the current generation with every sequence committed, else the upper bound Grant.Through - Producer.Seq. One write adds it to pending_loss and clears the grant; a settled producer is never accepted again.

func (*Spool) Stats

func (s *Spool) Stats(ctx context.Context) (depth int, bytes int64, drops uint64, err error)

Stats reports unacknowledged decisions and the total refused-overflow count.

func (*Spool) Succeed added in v0.9.3

func (s *Spool) Succeed(ctx context.Context) error

Succeed opens the spool to a new producer once the last one is settled: a Docker runner's revive starts a new proxy process, which is a new instance. The settled producer's pending loss stays until EmitLoss. A producer still holding a grant is refused, so the new one never overlaps it. The caller hands the run to the new proxy under a new key, so the settled instance cannot come back.

Jump to

Keyboard shortcuts

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