CARLOS rastrillo docs

๐Ÿค– eventlog

amadan.net/rastrillo/rastrillo/eventlog

The mergeable store shape: a command never UPDATEs. It appends an immutable event to a resource's stream, and a pure Derive fold recomputes the read model.

Because the fold is pure and total over the history, replay works for free โ€” truncate and refold โ€” and multi-edge works for free: the visible state is the merge of many single-writer streams.

This is the app-side half of the platform's mergeable contract, and Manifests can generate a store on it with store = "mergeable".

The table

One table holds every stream. Each row is one immutable event, keyed (stream, writer, seq): writer is the appending instance's identity, seq its own dense counter. A writer never rewrites its history.

eventlog.Schema is the migration set.

Open, Append, Events

func Open(db *sql.DB) (*Log, error)
func (l *Log) Append(ctx context.Context, stream string, e Event) error
func (l *Log) Events(ctx context.Context, stream string) ([]Event, error)
func (l *Log) EventsByPrefix(ctx context.Context, prefix string) ([]Event, error)

EventsByPrefix escapes its LIKE pattern, so a stream name containing % or _ matches literally rather than as a wildcard.

The merge order

The merged read order is (lamport, ts, writer, seq): a total order, deterministic across edges by construction. Any two logs holding the same events in any ingest order read them back identically, which is what makes Derive converge.

Log.Order is the app-supplied comparator seam for a different merge rule. The lamport default is the framework's answer until a real one is confirmed against this shape.

Derive

func Derive[S any](events []Event, fold func(S, Event) S, zero S) S

A pure generic fold, and you should keep yours pure. The whole design rests on recomputing state from history at any time, and a fold that reads the clock or the database cannot be replayed.

ErrDiverged reports a fold that disagreed with a recorded result.

Ingest

func (l *Log) Ingest(ctx context.Context, events []Event) error

Idempotent, and the seam the platform's transport will call. Re-ingesting an event you already hold is a no-op instead of a duplicate, which is what lets a transport retry freely.

LocalWriter

func LocalWriter(db *sql.DB) (string, error)

This instance's writer identity, stable across restarts because it is persisted in its own table.

What is deliberately not here

Read this page as markdown โ€” exact, unstyled, and cheap for an agent to fetch.