Appends and queries events in a Chronicle event log.
The event log is the primary EventSequence in Chronicle. Use append/3 to
record domain events for a given event source (such as an aggregate root).
Usage
:ok = Chronicle.EventSequences.EventLog.append("account-1", %MyApp.Events.AccountOpened{
account_id: "account-1",
owner_name: "Alice",
initial_balance: 500
})To append to a specific client:
:ok = Chronicle.EventSequences.EventLog.append("account-1", event, client: :my_chronicle)Multiple events
events = [
%MyApp.Events.AccountOpened{account_id: "1", owner_name: "Alice"},
%MyApp.Events.FundsDeposited{account_id: "1", amount: 500}
]
:ok = Chronicle.EventSequences.EventLog.append_many("account-1", events)Transactions
When a Chronicle.Transactions.UnitOfWork is active, append operations are
buffered locally and only sent to Chronicle when the unit of work is committed.
Summary
Functions
Appends a single event to the event log for the given event source.
Appends a single event, then waits for every observer (reactor, reducer, ++) affected by the append to either reach the appended sequence number or fail.
Appends multiple events to the event log for the given event source.
Appends a list of Chronicle.EventSequences.EventForEventSourceId entries as a
single atomic append-many, each carrying its own target event source id (and
other per-event metadata such as stream type/id, subject, tags, and occurred
time). Identity and causation are batch-level; see
Chronicle.EventSequences.EventForEventSourceId for their precedence.
Appends events atomically for one event source and returns the detailed outcome with every
assigned sequence number. See append_with_result/3.
Appends a single event and returns the detailed outcome.
Completes a named, non-default stream so that no further events can be appended to it.
Returns events for the given event source ID from the event log.
Returns events from (and including) the given sequence number onward.
Returns the sequence number that will be assigned to the next appended event.
Returns the tail sequence number for an event sequence.
Returns the tail sequence number scoped to only the event types that the
given reactor or reducer module subscribes to (its @handles declarations).
Checks whether an event sequence has events for an event source id.
Serializes event once and returns a Chronicle.EventSequences.PreparedEvent bound to this
client, namespace and event sequence, for reuse with append_prepared/3 and
Chronicle.EventSequences.Streams.verify_content/3. Mutating event afterward has no effect -
the content was captured at preparation time.
Redacts a single event at a specific sequence number, permanently replacing its content for compliance/GDPR erasure. This is destructive and irreversible.
Redacts all events for a given event source, optionally filtered to specific event types. Permanently replaces content for compliance/GDPR erasure. This is destructive and irreversible.
Waits for every observer affected by events up to sequence_number to reach it or fail.
Functions
Appends a single event to the event log for the given event source.
Options
:client— the client name (default:Chronicle.Client):namespace— overrides the client's default namespace:event_sequence_id— event sequence id (default:"event-log"):timeout— gRPC deadline in milliseconds or:infinity(default:60_000):event_source_type— explicit event source type, otherwise resolved by the kernel:event_stream_type— explicit event stream type, otherwise resolved by the kernel:event_stream_id— explicit event stream ID, otherwise resolved by the kernel:occurred— explicitDateTime; omission leaves the timestamp to the kernel:tags— list of tag strings:subject— the identity subject string:correlation_id— correlation id override (Chronicle.Correlation.CorrelationIdor string):identity— identity override (Chronicle.Identity):causation— causation chain override (list ofChronicle.Auditing.CausationEntry):concurrency_scope—Chronicle.Events.ConcurrencyScopeor keyword options with:sequence_number,:event_source_id,:event_stream_type,:event_stream_id,:event_source_type, and:event_types:event_source— aChronicle.EventSources.EventSourcemodule (or its name) the append goes through. Its name becomes the event source type andEventSourcemetadata of the event, and its concurrency dimensions apply when no explicit:concurrency_scopeis given:event_stream— the name of a stream declared by:event_source; written as the event stream type. Unknown sources/streams and contradicting:event_source_type/:event_stream_typevalues return{:error, {:unknown_event_source, _}},{:error, {:event_stream_does_not_belong_to_event_source, _, _}}or{:error, {:event_routing_contradicts_event_source, _, _, _, _}}
Omitted, nil, and empty routing values travel as empty strings. The kernel
resolves these to "Default" / "All" / "Default"; nonempty values are exact.
Concurrency filters are independent of routing and are never filled from it.
Returns :ok on success or {:error, reason} on failure, including concurrency
and validation rejection. Append requests require a successful descriptor
compatibility check before they are sent. A successful check is shared by all
callers on that connection until it reconnects. Failed checks are retried on
the next append.
The check uses the installed 18.3.0 contract descriptor. It does not yet distinguish older kernels from kernels implementing canonical append metadata; that requires a future contracts pin carrying the new contract.
@spec append_and_wait_for_completion(String.t(), struct(), keyword()) :: {:ok, %{success: boolean(), failed_partitions: list()}} | {:error, term()}
Appends a single event, then waits for every observer (reactor, reducer, ++) affected by the append to either reach the appended sequence number or fail.
This is additive rather than a change to append/3's return shape: append/3
and append_many/3 return only :ok | {:error, term()} today (no
AppendResult-equivalent carrying a sequence number), so building
WaitForCompletion on top of them would either be a breaking change to their
return contract or require a second round-trip to re-discover the sequence
number. This function does the append and the wait as one call instead.
Mirrors the C# client's AppendResult.WaitForCompletion() extension.
Returns {:ok, %{success: boolean(), failed_partitions: [Chronicle.FailedPartitions.FailedPartition.t()]}}
on success (whether or not every observer completed — check :success), or
{:error, reason} if the append itself failed.
Options
Same as append/3, plus:
:timeout— how long to wait for observer completion, in milliseconds (default:5_000, matching the C# client's default).
Appends multiple events to the event log for the given event source.
All events are appended atomically. Each event must be a struct that
use Chronicle.Events.EventType.
Options
Same as append/3, including :event_sequence_id and :concurrency_scope.
@spec append_many_for_event_sources( [Chronicle.EventSequences.EventForEventSourceId.t()], keyword() ) :: :ok | {:error, term()}
Appends a list of Chronicle.EventSequences.EventForEventSourceId entries as a
single atomic append-many, each carrying its own target event source id (and
other per-event metadata such as stream type/id, subject, tags, and occurred
time). Identity and causation are batch-level; see
Chronicle.EventSequences.EventForEventSourceId for their precedence.
Use this when a batch of events needs to target several, possibly different,
event source ids in one transaction — e.g. reactor side-effect dispatch (see
Chronicle.Reactors.Handler).
Options
:client— the client name (default:Chronicle.Client):namespace— overrides the client's default namespace:event_sequence_id— event sequence id (default:"event-log"):timeout— gRPC deadline in milliseconds or:infinity(default:60_000)
@spec append_many_with_result(String.t(), [struct()], keyword()) :: {:ok, Chronicle.EventSequences.AppendResult.t()} | {:error, Chronicle.EventSequences.AppendResult.t() | term()}
Appends events atomically for one event source and returns the detailed outcome with every
assigned sequence number. See append_with_result/3.
@spec append_prepared( String.t(), Chronicle.EventSequences.PreparedEvent.t(), keyword() ) :: :ok | {:error, term()}
Appends a prepare/2d event. Accepts the options of append/3. The event was serialized once,
at preparation time - this never re-serializes it.
Returns {:error, :prepared_event_belongs_to_another_sequence} when prepared_event was
prepared for a different client, namespace, or event sequence than this call targets.
@spec append_with_result(String.t(), struct(), keyword()) :: {:ok, Chronicle.EventSequences.AppendResult.t()} | {:error, Chronicle.EventSequences.AppendResult.t() | term()}
Appends a single event and returns the detailed outcome.
Unlike append/3 (which returns :ok | {:error, term}), this returns a
Chronicle.EventSequences.AppendResult carrying the assigned sequence number and, on a
rejection, the structured constraint and concurrency violations:
case Chronicle.EventSequences.EventLog.append_with_result("acc-1", %Opened{}) do
{:ok, %{sequence_number: n}} -> n
{:error, %{constraint_violations: [violation | _]}} -> violation.message
{:error, %{concurrency_violations: _}} -> :retry
{:error, other} -> other
endAccepts the options of append/3. {:error, result} is returned for a kernel rejection;
transport and client errors are returned as {:error, reason} with a non-result term.
@spec complete_stream(String.t(), String.t(), keyword()) :: {:ok, non_neg_integer()} | {:error, :default_stream_cannot_be_completed | :already_completed | term()}
Completes a named, non-default stream so that no further events can be appended to it.
The default stream (event stream type "All" paired with the default event
stream id) can never be completed. Completing an already-completed stream
leaves it in its completed state.
Options
:client— the client name (default:Chronicle.Client):namespace— overrides the client's default namespace:event_sequence_id— event sequence id (default:"event-log")
Returns {:ok, tail_sequence_number} on success, or
{:error, :default_stream_cannot_be_completed | :already_completed}.
Returns events for the given event source ID from the event log.
Options
:client— the client name (default:Chronicle.Client):namespace— overrides the client's default namespace:event_sequence_id— event sequence id (default:"event-log"):event_types— list of event type modules to filter by (default: all):event_source_type— the event source type to filter by (default: all):event_stream_type— the event stream type to filter by (default: all):event_stream_id— the event stream id to filter by (default: all):decode— whentrue, returnsChronicle.Events.AppendedEventstructs whose:eventis the event struct (default:false, the raw wire events)
Returns {:ok, [appended_event]} or {:error, reason}.
@spec get_from_sequence_number(non_neg_integer(), keyword()) :: {:ok, list()} | {:error, term()}
Returns events from (and including) the given sequence number onward.
Mirrors the Chronicle C# and TypeScript clients' GetFromSequenceNumber().
Options
:client— the client name (default:Chronicle.Client):namespace— overrides the client's default namespace:event_sequence_id— event sequence id (default:"event-log"):event_source_id— optional event source id to filter by (default: all):event_types— list of event type modules to filter by (default: all):decode— whentrue, returnsChronicle.Events.AppendedEventstructs whose:eventis the event struct (default:false, the raw wire events)
Returns {:ok, [appended_event]} or {:error, reason}.
@spec get_next_sequence_number(String.t() | nil, keyword()) :: {:ok, non_neg_integer()} | {:error, term()}
Returns the sequence number that will be assigned to the next appended event.
Mirrors the Chronicle C# and TypeScript clients' GetNextSequenceNumber().
Options
Same as get_tail_sequence_number/2.
@spec get_tail_sequence_number(String.t() | nil, keyword()) :: {:ok, non_neg_integer()} | {:error, term()}
Returns the tail sequence number for an event sequence.
Options
:client— the client name (default:Chronicle.Client):namespace— overrides the client's default namespace:event_sequence_id— event sequence id (default:"event-log"):event_source_type— the event source type to filter by (default: all):event_stream_type— the event stream type to filter by (default: all):event_stream_id— the event stream id to filter by (default: all):event_types— list of event type modules to filter by (default: all)
@spec get_tail_sequence_number_for_observer(module(), keyword()) :: {:ok, non_neg_integer()} | {:error, term()}
Returns the tail sequence number scoped to only the event types that the
given reactor or reducer module subscribes to (its @handles declarations).
Mirrors the Chronicle C# client's GetTailSequenceNumberForObserver().
Options
Same as get_tail_sequence_number/2, minus :event_types (derived from
observer_module).
Checks whether an event sequence has events for an event source id.
Options
:client— the client name (default:Chronicle.Client):namespace— overrides the client's default namespace:event_sequence_id— event sequence id (default:"event-log")
@spec prepare(struct(), keyword()) :: {:ok, Chronicle.EventSequences.PreparedEvent.t()} | {:error, term()}
Serializes event once and returns a Chronicle.EventSequences.PreparedEvent bound to this
client, namespace and event sequence, for reuse with append_prepared/3 and
Chronicle.EventSequences.Streams.verify_content/3. Mutating event afterward has no effect -
the content was captured at preparation time.
Options
:client— the client name (default:Chronicle.Client):namespace— overrides the client's default namespace:event_sequence_id— event sequence id (default:"event-log")
Returns {:error, {:unknown_event_type, module}} when event does not
use Chronicle.Events.EventType.
@spec redact(non_neg_integer(), String.t(), keyword()) :: :ok | {:error, term()}
Redacts a single event at a specific sequence number, permanently replacing its content for compliance/GDPR erasure. This is destructive and irreversible.
Options
:client— the client name (default:Chronicle.Client):namespace— overrides the client's default namespace:event_sequence_id— event sequence id (default:"event-log")
@spec redact_for_event_source(String.t(), String.t(), [module()], keyword()) :: :ok | {:error, term()}
Redacts all events for a given event source, optionally filtered to specific event types. Permanently replaces content for compliance/GDPR erasure. This is destructive and irreversible.
Options
Same as redact/3.
@spec wait_for_sequence_number(non_neg_integer(), keyword()) :: {:ok, map()} | {:error, term()}
Waits for every observer affected by events up to sequence_number to reach it or fail.
Options: :client, :namespace, :event_sequence_id and :timeout.
Returns {:ok, %{success: boolean(), failed_partitions: [...]}}.