transaction

package
v0.5.0 Latest Latest
Warning

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

Go to latest
Published: Aug 7, 2026 License: MIT Imports: 19 Imported by: 0

Documentation

Overview

Package transaction commits a bounded set of managed file writes inside one selected root as a single recoverable unit.

The filesystem gives no multi-file atomicity, so this package adds the smallest mechanism that keeps the promise the sync and archive owners make: after any interruption every declared output holds either all of its old bytes or all of its planned bytes, and exactly one deterministic recovery action exists.

The mechanism is a write-ahead manifest. Staged bytes are written and synced first; the manifest write is the commit point; replacement is a replayable projection of the manifest. A manifest that exists is committed and rolls forward. Staging without a manifest was never committed and rolls back.

Everything here is local deterministic filesystem logic: no network, no LLM, no clock of its own — the caller injects the time recovery judges against.

Index

Constants

View Source
const (
	CodeInvalid   = "transaction_invalid"
	CodeConflict  = "transaction_conflict"
	CodeDrift     = "transaction_drift"
	CodeAmbiguous = "transaction_ambiguous"
	CodeIO        = "transaction_io"
)

Stable refusal codes. Each names one operator action that is never a manual edit of state, history, evidence, or a manifest.

View Source
const (
	ActionRollForward = "roll_forward"
	ActionRollback    = "rollback"
)

Recovery actions. A pending transaction resolves to exactly one of them.

View Source
const SchemaVersion = 1

SchemaVersion versions the manifest contract. A manifest from any other version is refused rather than reinterpreted.

Variables

This section is empty.

Functions

This section is empty.

Types

type Manifest

type Manifest struct {
	SchemaVersion int             `json:"schema_version"`
	ID            string          `json:"id"`
	Operation     string          `json:"operation"`
	Change        string          `json:"change"`
	Revision      uint64          `json:"revision"`
	Timestamp     string          `json:"timestamp"`
	Outputs       []Output        `json:"outputs"`
	Moves         []Move          `json:"moves,omitempty"`
	Record        json.RawMessage `json:"record,omitempty"`
}

Manifest is the write-ahead record. ID is derived from every other field, so a manifest cannot be edited without losing its identity.

Record is the one history record this transaction owes. Carrying it inside the manifest is what makes an interrupted history append recoverable: the transaction identity proves the exact committed outputs before the missing record is appended, and the record's own content-derived id makes the append idempotent.

type Move

type Move struct {
	From string `json:"from"`
	To   string `json:"to"`
}

Move is one managed directory relocation. Both paths are managed-relative. A move is old-or-new by rename, so it needs no staged bytes.

type Output

type Output struct {
	Path   string `json:"path"`
	Before string `json:"before,omitempty"`
	After  string `json:"after"`
}

Output is the committed description of one planned write. Before is empty exactly when the target did not exist when the plan was built.

type Pending

type Pending struct {
	ID       string    `json:"id"`
	Action   string    `json:"action"`
	Manifest *Manifest `json:"manifest,omitempty"`
}

Pending is one interrupted transaction and its sole legal recovery action.

func Inspect

func Inspect(owner *corepath.Owner, now time.Time) ([]Pending, error)

Inspect reports every interrupted transaction and its one legal action. It reads only; a malformed or ambiguous transaction fails closed here too, so a caller cannot proceed past one by not recovering.

func Recover

func Recover(owner *corepath.Owner, now time.Time) ([]Pending, error)

Recover performs the one legal action for every interrupted transaction and returns what it resolved. Repeating it is safe: the same manifest identity yields the same action, and a fully applied transaction resolves to nothing.

type Request

type Request struct {
	Operation string
	Change    string
	Revision  uint64
	Now       time.Time
	Outputs   []Write
	Moves     []Move
	Record    *record.Record
	Hook      persist.Hook
}

Request is one commit attempt. Hook is nil in production; tests use it to interrupt the sequence at a stable boundary.

type Result

type Result struct {
	ID       string   `json:"id"`
	Manifest Manifest `json:"manifest"`
	NoOp     bool     `json:"no_op"`
}

Result is one committed transaction. NoOp is true when every target already held the planned bytes, so nothing was written and no manifest existed.

func Commit

func Commit(owner *corepath.Owner, request Request) (Result, error)

Commit stages, commits, and applies every declared output as one unit. It first resolves any interrupted transaction, so a retry after a crash is the same call rather than a separate repair verb.

func CommitUnderRootLock

func CommitUnderRootLock(owner *corepath.Owner, request Request) (Result, error)

CommitUnderRootLock is Commit for a caller that already holds the root lock. It exists so an operation can take the root lock before the change lock — the one global lock order — instead of inverting it here.

type Write

type Write struct {
	Path   string
	Before string
	Bytes  []byte
}

Write is one planned output. Path is managed-relative with `/` separators, so a manifest never embeds an absolute machine path.

Jump to

Keyboard shortcuts

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