Skip to content

Persistent Subscriptions

Daniel Shearer edited this page Apr 27, 2026 · 1 revision

A PersistentSubscription is an Aeron Archive feature for replaying from a recording, then switching seamlessly to a live stream; useful for late-joining applications. It also provides fallback support for slow consumers by always returning to the replay if kicked off live.

It is an alternative to the existing ReplayMerge feature.

For intended usage, see the Persistent Subscription sample, as well as the system tests for Java, C and C++ Wrapper.

Configuration

To configure a PersistentSubscription, pass a PersistentSubscription.Context to PersistentSubscription#create. A sample with the required configuration options set is shown below.

final PersistentSubscription.Context context = new PersistentSubscription.Context()
    .recordingId(12)
    .startPosition(FROM_START)
    .liveChannel("aeron:ipc")
    .liveStreamId(1000)
    .replayChannel("aeron:ipc")
    .replayStreamId(1001)
    .aeronArchiveContext(archiveContext);

Note: startPosition defaults to PersistentSubscription#FROM_START (the beginning of the recording), but can be set to a specific position in the recording, or PersistentSubscription#FROM_LIVE to immediately join the live stream.

Usage

PersistentSubscription has both PersistentSubscription#poll and PersistentSubscription#controlledPoll for consuming messages. These methods have the same signature as Subscription#poll and Subscription#controlledPoll respectively.

int doWork()
{
  return persistentSubscription.poll(fragmentHandler, fragmentLimit);
}

Note: The fragmentHandler passed to the subscription's polling methods should not be a FragmentAssembler as fragment assembly is handled within the PersistentSubscription.

Checking State

Should an application need to know the current state of the subscription, the API has the following flags:

  • isLive - currently consuming from the live stream.
  • isReplaying - currently replaying from a recording.
  • hasFailed - the subscription has encountered a terminal error and has transitioned to a FAILED state.
  • failureReason - the error that caused the subscription to get into a FAILED state.

There is also a PersistentSubscriptionListener that can be set on the context to receive callbacks on certain state transitions:

  • onLiveJoined - called when the subscription starts consuming from live.
  • onLiveLeft - called when the subscription falls off live.
  • onError(Exception) - called when errors occur. These will not necessarily be terminal failures.

Handling Failure

A PersistentSubscription is quite durable, typically attempting to re-connect to replay or live, should any errors be encountered.

However, there are a few errors that are terminal, requiring the subscription to be re-initialized. These can be detected via the aforementioned hasFailed and failureReason methods.

Observability

PersistentSubscription supports agent logging in Java and publishes four counters in all implementations.

Agent Logs

The two agent logs for a PersistentSubscription are:

  • State Change - logged on each state machine transition, includes old state, new state, recording ID, and channel/stream details.
  • Joined Live - logged when the subscription switches to the live stream, includes recording ID, channel/stream details, live session ID, and join position.
  • Left Live - logged when the subscription drops off the live stream, includes recording ID, channel/stream details, and the latest position on the live stream.

Counters

  • State - current state machine state. See the State enum in io.aeron.archive.client.PersistentSubscription.
  • Join difference - byte gap between replay position and live position when the live image is added. Long.MIN_VALUE when not consuming from live.
  • Live joined count - number of times the subscription has switched to live.
  • Live left count - number of times the subscription has dropped off the live stream.

When to use PersistentSubscriptions over ReplayMerge

As a general recommendation, if you aren't already using ReplayMerge, or you require IPC or Spies support, then use PersistentSubscription. If you are using ReplayMerge and have no IPC or Spies support requirements, then there's no need to switch since ReplayMerge is still actively supported and functional. A table of major differences can be found below.

PersistentSubscription ReplayMerge
IPC / Spy support Yes No
Falls back to replay Yes No
Memory overhead Higher; Uses 2 .logbuffer Lower; Uses 1 .logbuffer
Does fragment assembly Yes No

Clone this wiki locally