Chronicle.EventSequences.EventLog (cratis_chronicle v3.16.1)

Copy Markdown View Source

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 prepare/2d event. Accepts the options of append/3. The event was serialized once, at preparation time - this never re-serializes it.

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

append(event_source_id, event, opts \\ [])

@spec append(String.t(), struct(), keyword()) :: :ok | {:error, term()}

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 — explicit DateTime; omission leaves the timestamp to the kernel

  • :tags — list of tag strings

  • :subject — the identity subject string

  • :correlation_id — correlation id override (Chronicle.Correlation.CorrelationId or string)

  • :identity — identity override (Chronicle.Identity)

  • :causation — causation chain override (list of Chronicle.Auditing.CausationEntry)

  • :concurrency_scope — Chronicle.Events.ConcurrencyScope or keyword options with :sequence_number, :event_source_id, :event_stream_type, :event_stream_id, :event_source_type, and :event_types

  • :event_source — a Chronicle.EventSources.EventSource module (or its name) the append goes through. Its name becomes the event source type and EventSource metadata of the event, and its concurrency dimensions apply when no explicit :concurrency_scope is 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_type values 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.

append_and_wait_for_completion(event_source_id, event, opts \\ [])

@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).

append_many(event_source_id, events, opts \\ [])

@spec append_many(String.t(), [struct()], keyword()) :: :ok | {:error, term()}

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.

append_many_for_event_sources(events, opts \\ [])

@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)

append_many_with_result(event_source_id, events, opts \\ [])

@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.

append_prepared(event_source_id, prepared_event, opts \\ [])

@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.

append_with_result(event_source_id, event, opts \\ [])

@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
end

Accepts 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.

complete_stream(event_stream_type, event_stream_id, opts \\ [])

@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}.

get_for_event_source(event_source_id, opts \\ [])

@spec get_for_event_source(String.t(), keyword()) :: {:ok, list()} | {:error, term()}

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 — when true, returns Chronicle.Events.AppendedEvent structs whose :event is the event struct (default: false, the raw wire events)

Returns {:ok, [appended_event]} or {:error, reason}.

get_from_sequence_number(sequence_number, opts \\ [])

@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 — when true, returns Chronicle.Events.AppendedEvent structs whose :event is the event struct (default: false, the raw wire events)

Returns {:ok, [appended_event]} or {:error, reason}.

get_next_sequence_number(event_source_id \\ nil, opts \\ [])

@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.

get_tail_sequence_number(event_source_id \\ nil, opts \\ [])

@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)

get_tail_sequence_number_for_observer(observer_module, opts \\ [])

@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).

has_events_for?(event_source_id, opts \\ [])

@spec has_events_for?(String.t(), keyword()) :: {:ok, boolean()} | {:error, term()}

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")

prepare(event, opts \\ [])

@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.

redact(sequence_number, reason, opts \\ [])

@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")

redact_for_event_source(event_source_id, reason, event_types \\ [], opts \\ [])

@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.

wait_for_sequence_number(sequence_number, opts \\ [])

@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: [...]}}.