Handling Messages

This guide shows you how to write message handlers - the methods where all your business logic executes in a Talon microservice.

Note on Consensus Models: This documentation focuses on Event Sourcing, which is Talon's primary consensus model. For State Replication examples and guidance, please refer to the Rumi documentation. Rumi is the next major version (4.x) of the X Platform and provides more robust support for State Replication, including enhanced state modeling capabilities and improved developer tooling. To understand both models conceptually, see Consensus Models.

Overview

Message handlers are annotated methods that process inbound messages. When a message arrives, the AEP Engine dispatches it to the appropriate handler where your business logic executes. The handler reads data from the inbound message, updates the microservice store, and sends outbound messages.

Writing a Message Handler

Here's a canonical message handler that demonstrates the key elements:

@AppHAPolicy(HAPolicy.EventSourcing)
public class OrderProcessor {

    // Injected by the platform
    private AepMessageSender messageSender;

    // Application-owned store (POJOs)
    private Map<String, Order> orders = new HashMap<>();

    @AppInjectionPoint
    public void setMessageSender(AepMessageSender messageSender) {
        this.messageSender = messageSender;
    }

    /**
     * Message handler for new order messages
     */
    @EventHandler
    public void onNewOrder(NewOrderMessage message) {
        // 1. Read data from inbound message
        String orderId = message.getOrderId();
        String symbol = message.getSymbol();
        int quantity = message.getQuantity();

        // 2. Read and update microservice store (POJOs)
        Order order = orders.get(orderId);
        if (order == null) {
            order = new Order();
            order.setOrderId(orderId);
            orders.put(orderId, order);
        }
        order.setSymbol(symbol);
        order.setQuantity(quantity);
        order.setStatus("PENDING");

        // 3. Create and send outbound message
        OrderAckMessage ack = OrderAckMessage.create();
        ack.setOrderId(orderId);
        ack.setStatus("ACCEPTED");
        ack.setTimestamp(System.currentTimeMillis());

        messageSender.sendMessage("order-acks", ack);

        // 4. Handler returns - transaction commits
        // The AEP Engine will:
        //   - Replicate inbound message to backup instances
        //   - Backup replays message to rebuild store
        //   - Establish consensus with cluster members
        //   - Commit the transaction
        //   - Send the outbound message
        //   - Acknowledge the inbound message
    }
}

Handler Signature

A message handler must have this signature:

Key points:

  • Annotated with @EventHandler

  • Must be public

  • Return type must be void

  • Takes exactly one parameter - the inbound message

  • The message parameter type determines which messages this handler processes

Handler Method Names

Method names are not significant - the handler is matched to messages by the parameter type. However, following a naming convention like onMessageType makes code more readable.

Multiple Handlers for Same Message Type

You can have multiple handlers for the same message type:

Both handlers will be invoked for each NewOrderMessage. The order of invocation is deterministic but should not be relied upon - handlers should be independent.

Reading Inbound Messages

Access message fields using the generated getter methods:

Important: Inbound messages are read-only. See Programming Fundamentals for rules about message immutability and lifecycle.

Accessing Microservice Store

With Event Sourcing, your microservice store consists of POJOs that you manage directly. The store is private to your application and transparent to the Talon runtime:

Key points:

  • Store is your own POJOs, not ADM-generated

  • Consensus established by replaying inbound messages on backup instances

  • Store rebuilt on backup by replaying events

  • Your business logic must be deterministic

Store access rules:

  • Store can only be accessed from within a message handler (on the dispatch thread)

  • Store changes must be deterministic - no reliance on external state like system time or random numbers

  • See Programming Fundamentals for threading restrictions

  • See Event Sourcing Template for determinism requirements

Sending Outbound Messages

Create and send messages using the AepMessageSender:

When messages are sent: Outbound messages are not immediately sent when you call sendMessage(). Instead:

  1. The message is queued

  2. The handler returns

  3. The inbound message is replicated to backup instances

  4. Backup instances replay the message to rebuild store

  5. Consensus is established

  6. The transaction commits

  7. Then the outbound message is sent

This ensures that messages are only sent if the transaction succeeds, providing exactly-once semantics.

See Sending Messages for details on message keys, channels, and unsolicited sends.

Transaction Lifecycle

When a message handler executes, it runs within a transaction:

For details on how consensus works, see Cluster Consensus.

For advanced transaction control, see Controlling Transactions.

Common Patterns

Pattern: Lookup or Create

Pattern: Conditional Send

Pattern: Aggregate and Send

Pattern: Forwarding Messages

You cannot resend an inbound message directly. To forward a message, copy it first:

See Programming Fundamentals for details.

Advanced Topics

Using Savepoints

For long handlers or handlers that may fail partway through, you can use savepoints to commit work incrementally:

See Using Savepoints for details.

Zero Garbage Programming

For ultra-low-latency applications, you can eliminate garbage collection pauses using zero-garbage techniques:

See Coding for Zero Garbage for details.

See Also

Last updated