# How Maka's Runtime Event Log Defines System State: Event Sourcing Implementation

> Discover how Maka's Runtime Event Log defines system state through event sourcing. Learn how append-only events and computeState() reconstruct system conditions.

- Repository: [The Apache Software Foundation/maka](https://github.com/apache/maka)
- Tags: deep-dive
- Published: 2026-08-24

---

**Maka's Runtime Event Log defines system state by maintaining an append-only sequence of events in a thread-safe `CopyOnWriteArrayList` and deriving the current state through the `computeState()` method, which instantiates a fresh `RuntimeState` and replays each recorded event to reconstruct the system's condition.**

Apache Maka provides a distributed runtime core where durable system state emerges from an immutable event log rather than direct mutation. The `RuntimeEventLog` class, located in [`src/main/java/org/apache/maka/runtime/RuntimeEventLog.java`](https://github.com/apache/maka/blob/main/src/main/java/org/apache/maka/runtime/RuntimeEventLog.java), captures every lifecycle change and processing milestone as discrete `RuntimeEvent` objects. By replaying these chronologically ordered events through the `computeState()` method, the runtime reconstructs a deterministic snapshot of whether the system is active and how many items have been processed.

## Event Capture and Storage in RuntimeEventLog

The `RuntimeEventLog` serves as the append-only journal for all runtime activities. It stores events in a `CopyOnWriteArrayList<RuntimeEvent>` named `events`, ensuring thread-safe concurrent writes without locking overhead [lines 13, 18-20].

When a component emits a state change, the `record(RuntimeEvent event)` method appends it to the log. This operation is thread-safe due to the underlying `CopyOnWriteArrayList`, which creates a fresh copy of the backing array on each modification, making the `events` list safe for concurrent recording while maintaining read consistency.

```java
public void record(RuntimeEvent event) {
    events.add(event);
}

```

The `getEvents()` method returns an unmodifiable view of the event history, preventing external mutation while allowing inspection of the log [lines 25-27].

## How computeState() Derives System State from Events

System state is not stored as a static variable but calculated on demand through event sourcing logic. The `computeState()` method in [`RuntimeEventLog.java`](https://github.com/apache/maka/blob/main/RuntimeEventLog.java) implements this derivation [lines 32-38].

The process follows three steps:

1. **Instantiation**: Creates a new `RuntimeState` object with default values (`active = false`, `processedCount = 0`).
2. **Replay**: Iterates through the `events` list in insertion order.
3. **Mutation**: Invokes `event.apply(state)` for each entry, allowing the event to mutate the state object.

```java
public RuntimeState computeState() {
    RuntimeState state = new RuntimeState();
    for (RuntimeEvent event : events) {
        state.apply(event);
    }
    return state;
}

```

This approach ensures that the state is always **consistent** with the complete history of recorded events. Since `RuntimeState` is instantiated fresh during each computation, the method returns an immutable snapshot that reflects the log's contents at that exact moment.

## RuntimeState Structure and Mutation Logic

The `RuntimeState` class, defined in [`src/main/java/org/apache/maka/runtime/RuntimeState.java`](https://github.com/apache/maka/blob/main/src/main/java/org/apache/maka/runtime/RuntimeState.java), acts as the aggregate root for state mutations. It maintains two key fields tracking system condition [lines 7-8]:

- `active`: A boolean indicating whether the runtime is currently running.
- `processedCount`: An integer counter for successfully processed items.

State transitions occur within the `apply(RuntimeEvent event)` method, which uses `instanceof` checks to determine the specific mutation required [lines 18-26]:

```java
public void apply(RuntimeEvent event) {
    if (event instanceof RuntimeStartedEvent) {
        this.active = true;
    } else if (event instanceof RuntimeStoppedEvent) {
        this.active = false;
    } else if (event instanceof ProcessedItemEvent) {
        this.processedCount++;
    }
}

```

This design follows the **double-dispatch pattern**, where the event calls `apply` on the state, and the state routes to the appropriate handling logic based on the concrete event type.

## Event Types That Define State Transitions

Concrete event implementations reside in separate classes under `org.apache.maka.runtime`. Each extends `RuntimeEvent` and implements the abstract `apply(RuntimeState state)` method to delegate mutation back to the state object.

### Lifecycle Events: RuntimeStartedEvent and RuntimeStoppedEvent

`RuntimeStartedEvent` and `RuntimeStoppedEvent` manage the boolean `active` flag. When `computeState()` encounters a `RuntimeStartedEvent`, it triggers `state.apply(this)`, causing the state to set `active = true`. Conversely, `RuntimeStoppedEvent` sets `active = false` [see [`RuntimeState.java`](https://github.com/apache/maka/blob/main/RuntimeState.java) lines 19-22].

### Processing Events: ProcessedItemEvent

`ProcessedItemEvent` captures discrete work completion. It carries a specific `itemId` string identifying the processed entity, but its state mutation simply increments the `processedCount` counter [lines 23-25]. This allows the runtime to track throughput and completion metrics without maintaining a list of individual item IDs in the core state.

## Practical Implementation: Recording Events and Computing State

The following example demonstrates the complete workflow of event recording and state derivation using Apache Maka's runtime API:

```java
// Initialize the event log
RuntimeEventLog eventLog = new RuntimeEventLog();

// Record runtime lifecycle and processing events
eventLog.record(new RuntimeStartedEvent(System.currentTimeMillis()));
eventLog.record(new ProcessedItemEvent(System.currentTimeMillis(), "item-42"));
eventLog.record(new ProcessedItemEvent(System.currentTimeMillis(), "item-43"));
eventLog.record(new RuntimeStoppedEvent(System.currentTimeMillis()));

// Derive current state from the event history
RuntimeState state = eventLog.computeState();

// Access derived state properties
System.out.println("Runtime active: " + state.isActive());              // false
System.out.println("Items processed: " + state.getProcessedCount());    // 2

```

In this sequence, the `RuntimeEventLog` maintains the chronological record while `computeState()` reconstructs that the runtime was started, processed two distinct items, and subsequently stopped. The resulting `RuntimeState` accurately reflects `active = false` and `processedCount = 2` based solely on the event sequence.

## Summary

- **Append-Only Log**: `RuntimeEventLog` stores events in a thread-safe `CopyOnWriteArrayList`, ensuring durable, ordered history.
- **State Derivation**: The `computeState()` method reconstructs system condition by instantiating `RuntimeState` and replaying all recorded events sequentially.
- **Double-Dispatch Pattern**: Events implement `apply(RuntimeState)` to delegate mutation, while `RuntimeState.apply()` uses `instanceof` routing to execute specific state transitions.
- **Deterministic Snapshots**: Each call to `computeState()` produces a fresh, immutable snapshot reflecting the exact sequence of `RuntimeStartedEvent`, `RuntimeStoppedEvent`, and `ProcessedItemEvent` instances recorded.

## Frequently Asked Questions

### How does the Runtime Event Log ensure thread safety when recording events?

The `RuntimeEventLog` uses a `CopyOnWriteArrayList` as its backing store for the `events` field. This concurrent collection creates a new copy of the underlying array on every write operation, allowing multiple threads to safely call `record()` simultaneously without explicit synchronization while maintaining consistent iteration for `computeState()` readers.

### What happens if an unknown event type is passed to RuntimeState.apply()?

The `apply(RuntimeEvent event)` method in [`RuntimeState.java`](https://github.com/apache/maka/blob/main/RuntimeState.java) contains an if-else chain checking only for `RuntimeStartedEvent`, `RuntimeStoppedEvent`, and `ProcessedItemEvent`. If an event of an unhandled type is passed, the method performs no mutation and returns silently. This design allows forward compatibility but requires developers to ensure all new event types update the state mutation logic.

### Can the system state be reconstructed at a specific point in historical time?

Yes, because `computeState()` derives state from the immutable event list, you could reconstruct historical state by providing a view of the events list truncated at a specific timestamp or index. However, the base implementation replays the entire list; custom logic would be required to stop replay at a specific event boundary to view past states.

### Why does each event's apply() method delegate back to RuntimeState.apply()?

This double-dispatch pattern decouples the event type definition from the state mutation logic. It allows `RuntimeState` to centralize all mutation rules in a single method using `instanceof` checks, while concrete event classes (`RuntimeStartedEvent`, `ProcessedItemEvent`) remain simple data holders that merely forward the call to the state object with themselves as the argument.