§ reference

eventlog.events

The durable event table written with each action transaction.

eventlog.events is the durable event table used by the default SingleTableJsonEventPersister. Domain rows and event rows are committed in the same database transaction, so this table becomes the reliable source that local handlers, CDC connectors, routers, and broker publishers consume after commit.

Required when: you use the default SingleTableJsonEventPersister. This is the normal Ekbatan setup.

Writer: ActionExecutor, through SingleTableJsonEventPersister.

Row model: io.ekbatan.core.action.persister.event.single_table_json.EventEntity.

EventEntity is an internal row representation, not a domain Model or Entity. It has no optimistic-lock version and no soft-delete state.

DDL

PostgreSQL can create the eventlog schema in the same migration that creates the table. MariaDB and MySQL create a separate eventlog database in V0000, because those engines treat schema and database as the same namespace.

src/main/resources/db/migration/ V0001__eventlog.sql
sql
1 CREATE SCHEMA IF NOT EXISTS eventlog;
2
3 CREATE TABLE eventlog.events (
4 id UUID PRIMARY KEY,
5 namespace VARCHAR(255) NOT NULL,
6 action_id UUID NOT NULL,
7 action_name VARCHAR(255) NOT NULL,
8 action_params JSONB NOT NULL,
9 started_date TIMESTAMP NOT NULL,
10 completion_date TIMESTAMP NOT NULL,
11 model_id VARCHAR(255),
12 model_type VARCHAR(255),
13 event_type VARCHAR(255),
14 payload JSONB,
15 event_date TIMESTAMP NOT NULL,
16 delivered BOOLEAN NOT NULL
17 );
18
19 CREATE INDEX idx_events_action_id ON eventlog.events(action_id);

The connecting user must have permission to create and access the eventlog database. The examples grant that permission from the container init script before Flyway runs.

src/main/resources/db/migration/ V0000__create_eventlog_database.sql
sql
1 CREATE DATABASE IF NOT EXISTS eventlog;
src/main/resources/db/migration/ V0001__eventlog.sql
sql
1 CREATE TABLE eventlog.events (
2 id UUID PRIMARY KEY,
3 namespace VARCHAR(255) NOT NULL,
4 action_id UUID NOT NULL,
5 action_name VARCHAR(255) NOT NULL,
6 action_params JSON NOT NULL,
7 started_date DATETIME(6) NOT NULL,
8 completion_date DATETIME(6) NOT NULL,
9 model_id VARCHAR(255),
10 model_type VARCHAR(255),
11 event_type VARCHAR(255),
12 payload JSON,
13 event_date DATETIME(6) NOT NULL,
14 delivered BOOLEAN NOT NULL
15 );
16
17 CREATE INDEX idx_events_action_id ON eventlog.events(action_id);

The connecting user must have permission to create and access the eventlog database. The examples grant that permission from the container init script before Flyway runs.

src/main/resources/db/migration/ V0000__create_eventlog_database.sql
sql
1 CREATE DATABASE IF NOT EXISTS eventlog;
src/main/resources/db/migration/ V0001__eventlog.sql
sql
1 CREATE TABLE eventlog.events (
2 id CHAR(36) CHARACTER SET ascii NOT NULL,
3 namespace VARCHAR(255) NOT NULL,
4 action_id CHAR(36) CHARACTER SET ascii NOT NULL,
5 action_name VARCHAR(255) NOT NULL,
6 action_params JSON NOT NULL,
7 started_date DATETIME(6) NOT NULL,
8 completion_date DATETIME(6) NOT NULL,
9 model_id VARCHAR(255),
10 model_type VARCHAR(255),
11 event_type VARCHAR(255),
12 payload JSON,
13 event_date DATETIME(6) NOT NULL,
14 delivered BOOLEAN NOT NULL,
15 PRIMARY KEY (id)
16 );
17
18 CREATE INDEX idx_events_action_id ON eventlog.events(action_id);

Columns

ColumnNullableMeaning
idNoPrimary key of this event row. For cross-shard actions, each shard receives rows with the same event ids so every shard has a self-contained action/event picture.
namespaceNoLogical producer namespace configured on ActionExecutor. Use it to distinguish services or bounded contexts when several applications emit events.
action_idNoOne UUID per action execution. If one action emits multiple events, all rows from that action share the same action_id.
action_nameNoSimple class name of the producing action, such as WalletDepositMoneyAction.
action_paramsNoThe action params object serialized as JSON. This lets consumers understand the command that produced the event without joining to application tables.
started_dateNoUTC instant when the action’s perform() phase began.
completion_dateNoUTC instant when the action’s persist phase completed.
model_idYesIdentifier of the affected model. NULL only on sentinel rows for actions that emitted no model event.
model_typeYesSimple class name of the affected model, such as Wallet. NULL only on sentinel rows.
event_typeYesSimple class name of the event class, such as WalletMoneyDepositedEvent. NULL on sentinel rows. The default persister rejects ambiguous event simple-name collisions inside one process.
payloadYesEvent payload serialized as JSON. NULL on sentinel rows.
event_dateNoUTC instant when the event was logically produced, normally aligned with the action completion time.
deliveredNoStarts as FALSE on every insert. The local-event-handler fan-out job flips it to TRUE after it materializes handler notifications. CDC-only deployments can ignore it.

Sentinel rows

If an action emits zero events, Ekbatan still writes one sentinel row with model_id, model_type, event_type, and payload set to NULL. This preserves the fact that the action happened. Event consumers should skip sentinel rows by checking event_type IS NULL; Ekbatan’s Debezium SMTs already do that.

Indexes

IndexPurpose
idx_events_action_id on (action_id)Lets tests, diagnostics, and consumers find all rows created by one action execution.

The local event handler adds one extra fan-out index on this table. That index is documented with eventlog.event_notifications because it is only needed when local event handling is installed.

Dialect types

Logical column groupPostgreSQLMariaDBMySQL
id, action_idUUIDUUIDCHAR(36) CHARACTER SET ascii
action_params, payloadJSONBJSONJSON
started_date, completion_date, event_dateTIMESTAMPDATETIME(6)DATETIME(6)
Text columnsVARCHAR(255)VARCHAR(255)VARCHAR(255)
deliveredBOOLEANBOOLEANBOOLEAN

See also