Dynamic dead-lettering
The error log is durable and replayable, which means you can treat it as a powerful, dynamic DLQ.
Here’s how:
- Deploy a special consumer that tracks the error log.
- Use
@Triggerto access and inspect failed messages. - Filter and replay failures based on time, payload type, or originating app.
Example: retrying failed commands
Section titled “Example: retrying failed commands”Let’s assume a bug caused command processing to fail in September 2025. The following setup reprocesses those failed commands:
@Consumer(name = "command-dlq", minIndex = 115126095052800000L, maxIndexExclusive = 115295964364800000L) // 2025-09-01 to 2025-10-01class CommandReplayHandler {
@HandleError void retry(ConsoleError error, @Trigger(messageType = MessageType.COMMAND) MyCommand failedCommand) { Fluxzero.sendCommand(failedCommand); }}@Consumer( name = "command-dlq", minIndex = 115126095052800000L, maxIndexExclusive = 115295964364800000L) // 2025-09-01 to 2025-10-01)class CommandReplayHandler {
@HandleError fun retry(error: ConsoleError, @Trigger(messageType = MessageType.COMMAND) failedCommand: MyCommand) { Fluxzero.sendCommand(failedCommand) }}When to use the Error log
Section titled “When to use the Error log”| Use case | How the Error log helps |
|---|---|
| Fix a bug retroactively | Replay failed commands from the past |
| Validate new handler logic | Test it against real-world errors |
| Retry transient failures | Re-issue requests with retry logic |
| Clean up or suppress errors | Filter out known false-positives |
The error log acts as a time-travel debugger — it gives you full control over how and when to address failures, now or in the future.
Routing with @RoutingKey
Section titled “Routing with @RoutingKey”In Fluxzero, routing is used to assign messages to segments using consistent hashing. This ensures that messages about the same entity — for example, all events for a given OrderId — are always handled by the same consumer, in the correct order.
This is critical when you’re handling messages in parallel, but still want to ensure per-entity consistency.
Declaring the routing key
Section titled “Declaring the routing key”By default, the routing key is derived from the message ID. But you can override this by annotating a field, getter, or method in your payload class with @RoutingKey.
public record ShipOrder(@RoutingKey OrderId orderId) {}@RoutingKey("customer/id")public record OrderPlaced(Customer customer) {}data class ShipOrder( @RoutingKey val orderId: OrderId)@RoutingKey("customer/id")data class OrderPlaced( val customer: Customer)This instructs Fluxzero to extract customer.id and use it as the routing key when publishing or consuming the message.
Handler-level routing keys
Section titled “Handler-level routing keys”In advanced cases, you may want to override routing at the handler level, regardless of how the message was published.
@HandleEvent@RoutingKey("organisationId")void handle(OrganisationUpdate event) { // Will route based on organisationId in metadata or payload}@HandleEvent@RoutingKey("organisationId")fun handle(event: OrganisationUpdate) { // Will route based on organisationId in metadata or payload}@Consumer(ignoreSegment = true)public class OrganisationHandler { ...}@Consumer(ignoreSegment = true)class OrganisationHandler { ...}Metadata-based routing
Section titled “Metadata-based routing”You can also extract routing keys from message metadata. If the metadata key is missing, Fluxzero falls back to the payload.
@RoutingKey("userId")public class AuditLogEntry { ...}@RoutingKey("userId")class AuditLogEntry { ...}Summary
Section titled “Summary”| Placement | Meaning |
|---|---|
| Field/getter | Use the property’s value as routing key |
| Class-level | Use the named property in metadata or payload |
| Handler method | Overrides routing key used during handling (requires ignoreSegment) |
© 2026 Fluxzero