The append-only log of the sequenced events of one deck session, one file <id>.events per
session beside the session history, on the same Mead.Session.Record framing (length and
checksum per record, a torn last record cut on open).
The value is plain data: events are held in memory, newest first, so read_after/2 never
touches the disk, and every append/2 opens the file, writes, syncs and closes it, the way
Mead.Session.Store.append/3 does. The whole log is read on open/2. compact/2 keeps it
bounded: the oldest events are dropped, except the ones the channel rebuilds its permission
state from, the latest policy.updated and every remembered tool rule still in force. first_sequence/1 is where a replay can start.
With a nil directory the log lives in memory only, for sessions that have no storage.
Sequences must strictly increase; an event that does not carry a larger sequence than the last
one is refused before anything is written.
Summary
Functions
Appends event. With sync: false the file is written and closed but not forced to disk, for
the many small events (streamed text) whose loss in a power cut costs nothing the next synced
event does not make up for: that sync flushes everything written before it.
Present for symmetry with the file lifecycle; the log holds no open handle.
Drops the oldest events until keep remain, plus the latest policy.updated and the live
remembered-tool rules (permission.resolved with remember, not undone by a later
permission.forgotten) when they would otherwise go. The new file is written beside the old one and renamed over it, so a crash leaves
either the whole old log or the whole new one. A log within keep is returned unchanged.
Removes the file of a session; a missing file or an invalid id is not an error.
The lowest sequence a replay can start from: 1 for a log that was never compacted, and the first
sequence of the retained tail after a compaction. The kept policy.updated older than the tail
is not part of it.
Types
@type t() :: %Mead.Deck.EventLog{ count: non_neg_integer(), events: [map()], last_sequence: non_neg_integer(), path: Path.t() | nil }
Functions
Appends event. With sync: false the file is written and closed but not forced to disk, for
the many small events (streamed text) whose loss in a power cut costs nothing the next synced
event does not make up for: that sync flushes everything written before it.
@spec close(t()) :: :ok
Present for symmetry with the file lifecycle; the log holds no open handle.
@spec compact(t(), pos_integer()) :: {:ok, t()} | {:error, term()}
Drops the oldest events until keep remain, plus the latest policy.updated and the live
remembered-tool rules (permission.resolved with remember, not undone by a later
permission.forgotten) when they would otherwise go. The new file is written beside the old one and renamed over it, so a crash leaves
either the whole old log or the whole new one. A log within keep is returned unchanged.
@spec count(t()) :: non_neg_integer()
Removes the file of a session; a missing file or an invalid id is not an error.
@spec first_sequence(t()) :: non_neg_integer()
The lowest sequence a replay can start from: 1 for a log that was never compacted, and the first
sequence of the retained tail after a compaction. The kept policy.updated older than the tail
is not part of it.
@spec last_sequence(t()) :: non_neg_integer()
@spec read_after(t(), non_neg_integer()) :: [map()]