Skip to main content

Events

Gravitino emits an event around every metadata operation, and event listeners receive them. A listener is how another system follows changes to the catalog without polling it. The server itself uses listeners to write the audit log, keep the search index current, feed the dashboard counts, and register the built-in job templates in each new metalake, and you can register your own alongside them.

Quick start​

  1. Build a listener. Implement EventListenerPlugin and choose its dispatch mode. The chart has no value that puts a jar on the server classpath, so a listener of your own needs a custom server image that contains its jar.

  2. Register it by name. Add the name to the end of gravitino.eventListener.names and point {name}.class at the implementation. Replacing the list instead of extending it removes any listener already named there.

    The list has a dedicated chart value, gravitino.eventListener.names, which defaults to search,dashboard-metrics. The chart writes class entries only for search and dashboard-metrics, so the class of your listener goes under gravitino.additionalConfigItems.

    my-values.yaml
    gravitino:
    eventListener:
    names: "search,dashboard-metrics,{listener_name}"
    additionalConfigItems:
    gravitino.eventListener.{listener_name}.class: "{listener_class}"
  3. Apply the change and confirm the listener started. The Helm upgrade restarts the server pod. The listener is running once the pod is Running and its log contains Start event listener {listener_name}.

    Find started listeners
    kubectl exec -n {namespace} deploy/{release}-gravitino -c gravitino -- \
    sh -c 'grep "Start event listener" /opt/gravitino/logs/$HOSTNAME/gravitino-server.log'

    A name in gravitino.eventListener.names with no matching class entry makes the server throw at startup and exit, and so does a class that cannot be loaded or whose init throws. On Helm the pod then crash-loops.

Events tab​

Configure → Events in the UI shows the event settings from the chart's gravitino.conf ConfigMap. It reads the ConfigMap, not the running server, and it is read only. Under the description "Listeners notified when metadata changes." it shows:

  • Queue capacity and Dispatcher join (seconds): gravitino.eventListener.queueCapacity and gravitino.eventListener.dispatcherJoinSeconds, or 3000 and 3 when the ConfigMap does not set them.
  • Listeners: a table with Name, Type, and Mode columns, and a count of listeners below it.

The listener rows come from the ConfigMap, so they do not match what the server runs:

  • audit-log appears, with Type Built-in, when gravitino.audit.enabled is true.
  • Every name in gravitino.eventListener.names appears with Type Custom, the chart's own search and dashboard-metrics included.
  • Mode reads ASYNC_ISOLATED on every row, whatever the listener declares.
  • builtin-job-template, which the server always registers, never appears.

The tab shows no queue depth and no count of dropped events. Dropped events show only in the server pod's log, as described in Queue behavior.

Events​

Each operation emits up to three events: a pre-event before it runs, a post-event after it succeeds, and a failure event after it throws. The names follow the operation, so createTable produces CreateTablePreEvent, CreateTableEvent, and CreateTableFailureEvent. Operations served by the Iceberg REST service carry an Iceberg prefix, as in IcebergCreateTableEvent. Not every operation defines all three. The full set of classes is in the org.apache.gravitino.listener.api.event package.

A request that produces no operation event still produces one event of its own: HttpRequestEvent when it succeeds, and HttpRequestFailureEvent when it ends with a 4xx or 5xx status. A request rejected with 401 before it reaches any operation is one of these. Both reach every listener, the audit log included.

A pre-event handler that throws ForbiddenException stops the operation before it runs, but only when the listener's mode is SYNC. From any other listener the exception is logged and the operation goes ahead. An exception a listener throws while handling a post-event or a failure event is logged and swallowed, so it never changes the result the client receives.

1. The client sends an operation. 2. The server sends the pre-event to inline listeners. 3. An inline listener continues or vetoes, SYNC only. 4. The server queues the pre-event. 5. The server runs the operation. 6. The server sends the post or failure event inline. 7. The server queues the post or failure event. 8. The server returns the result. 9. The queue delivers, or drops if full

Inline listeners run inside the request, so the client waits for them. Asynchronous listeners receive the same events from a queue after the fact, as described in Queue behavior.

Listeners​

Dispatch modes​

Every listener declares how its events reach it.

ModeBehavior
SYNCCalled inline, before the operation's result reaches the client. A slow listener slows every request.
ASYNC_ISOLATEDCalled from a queue and dispatcher thread of its own, so one slow listener cannot hold up another.
ASYNC_SHAREDCalled from one queue and dispatcher shared by every listener in this mode, so a slow listener delays the others on the same queue.

Built-in listeners​

ListenerRegistered whenDelivery
audit-loggravitino.audit.enabled is trueInline, on the request thread
builtin-job-templateAlwaysInline, on the request thread
searchNamed in gravitino.eventListener.namesIts own queue (ASYNC_ISOLATED)
dashboard-metricsNamed in gravitino.eventListener.namesIts own queue (ASYNC_ISOLATED)

Only search and dashboard-metrics go through a queue. The server registers audit-log and builtin-job-template directly, without one, so they run on the request thread whatever mode they declare. A slow audit log writer therefore slows every request.

Configuration​

Configuration itemDescriptionDefault value
gravitino.eventListener.namesComma-separated listener names.search,dashboard-metrics
gravitino.eventListener.{name}.classClass of the listener registered under {name}.(none)
gravitino.eventListener.{name}.{key}Any other property under a listener's name, passed to that listener unchanged.(none)
gravitino.eventListener.queueCapacityEvents each asynchronous queue holds before it starts dropping them.3000
gravitino.eventListener.dispatcherJoinSecondsSeconds shutdown waits for each dispatcher thread to exit.3

Set gravitino.eventListener.names through the chart value of the same name, and the others under gravitino.additionalConfigItems in my-values.yaml as quoted strings; see Set other server properties.

Queue behavior​

Each ASYNC_ISOLATED listener has its own queue of queueCapacity events, and all ASYNC_SHARED listeners share one more. When a queue is full, a new event is dropped rather than waited for, so the operation that produced it is never slowed. The server logs a warning with the number dropped, at most once a minute per queue.

A dropped event never reaches its listener. For the search index it is a change that search does not reflect until the object changes again. The audit log has no queue, so a full queue never costs it a record.

Warnings about dropped events mean a listener cannot keep up, and the fix is a faster listener or a larger queue.

At shutdown the server stops each dispatcher and waits up to dispatcherJoinSeconds for the thread to exit. Events still queued at that point are dropped and counted in a warning, so a listener that must see every event needs its own durable delivery rather than relying on the queue.