Skip to content

Choose a persistent-stream subscription start position

A rewindable persistent-stream subscription can start with newly published messages or replay messages already retained by its pulling agent. Choose the start position when application behavior creates the subscription, or configure a provider-wide default for subscriptions which omit a position. After delivery begins, sequence-token progress defines the subscription’s position.

For the broader subscription model, see Orleans streaming APIs. Provider durability and rewindability are described in Orleans stream providers.

Pass a StreamSubscriptionStartPosition to SubscribeAsync:

public static Task<StreamSubscriptionHandle<T>> SubscribeFromCacheStart<T>(
IAsyncStream<T> stream,
IAsyncObserver<T> observer) =>
stream.SubscribeAsync(
observer,
StreamSubscriptionStartPosition.EarliestAvailable);

The batch-observer overload accepts the same position:

public static Task<StreamSubscriptionHandle<T>> SubscribeBatchFromCacheStart<T>(
IAsyncBatchObservable<T> stream,
IAsyncBatchObserver<T> observer) =>
stream.SubscribeAsync(
observer,
StreamSubscriptionStartPosition.EarliestAvailable);

The values have these semantics:

PositionInitial delivery
LatestMessages published after the subscription is established. Messages already retained in the queue cache are skipped. This value preserves the default behavior of a tokenless SubscribeAsync call.
EarliestAvailableThe earliest matching message currently retained in the pulling agent’s local queue cache, included in delivery. When the cache has no matching message, delivery begins with the stream’s first future message.

The position applies when the subscription is created. Resume an existing subscription handle to continue from its established progress; resume APIs use sequence tokens rather than a new start position.

Configure InitialSubscriptionStartPosition on the silo-side persistent-stream provider:

public static void ConfigureDefaultStartPosition(
ISiloPersistentStreamConfigurator streams)
{
streams.ConfigurePullingAgent(optionsBuilder =>
optionsBuilder.Configure(options =>
options.InitialSubscriptionStartPosition =
StreamSubscriptionStartPosition.EarliestAvailable));
}

This setting controls initial subscriptions which omit both a concrete sequence token and an explicit start position. It applies to tokenless explicit subscriptions and initial implicit-subscription attachments which have no delivery progress. Latest is the default.

Orleans chooses an initial position in this order:

  1. A concrete StreamSequenceToken supplied by the caller.
  2. An explicit StreamSubscriptionStartPosition supplied to SubscribeAsync.
  3. InitialSubscriptionStartPosition.
  4. Latest.

An explicit choice therefore overrides the provider default in either direction. Explicit Latest skips retained messages when the provider default is EarliestAvailable, and explicit EarliestAvailable replays the local cache when the provider default is Latest. Configure the option consistently on every silo eligible to host a pulling agent for the named provider.

EarliestAvailable searches one pulling agent’s local queue cache for the target StreamId. The cache’s current contents define the available replay window. Cache eviction, memory pressure, queue assignment, silo restarts, and the time since the pulling agent began reading can all move its earliest available position forward.

For Azure Event Hubs, EarliestAvailable keeps the partition receiver and its checkpoint at their current positions. It replays matching messages which the Orleans Event Hubs pulling agent has already read and still retains in its silo-side cache. The local cache window is therefore the replay window for this API, while Event Hubs retention remains the upstream recovery window.

DataMinTimeInCache and DataMaxAgeInCache control time-based eviction, while cache pressure can advance the earliest retained position. Size Event Hubs retention for upstream recovery and size the Orleans cache for the replay interval required by new subscriptions. See Protect a slow Event Hubs consumer for cache eviction and pressure behavior.

The built-in pooled, simple, memory, generator, and Event Hubs queue caches support EarliestAvailable. A custom persistent-stream cache participates by implementing GetCacheCursorAtPosition.

The default queue-cache interface behavior maps Latest to the existing tokenless cursor path and reports NotSupportedException for EarliestAvailable. An explicit subscription which requests the unsupported position receives the error and is faulted. When EarliestAvailable is the provider default for an implicit subscription, Orleans reports the error to the consumer and keeps the implicit subscription live at its current position.

A custom IAsyncObservable<T> implementation receives equivalent extension-overload compatibility: Latest uses its tokenless subscription path, and EarliestAvailable reports NotSupportedException until the observable implements Orleans start-position subscriptions.

For delivery guarantees and sequence-token recovery, continue to Stream delivery, ordering, replay, and recovery.