Skip to content

Subscriptions

Subscriptions deliver a real-time stream of change notifications for all entities of a given kind. They are the primary way to observe state changes in ConvergeDB.

await foreach (var change in players.SubscribeAsync(bootstrap: true))
{
Console.WriteLine($"{change.Type}: {change.Entity?.Name} (v{change.Version})");
}

The SubscribeAsync method returns an IAsyncEnumerable<EntityChange<T>> that yields notifications as they arrive.

ParameterDefaultDescription
bootstrapfalseIf true, the server sends a snapshot of all existing entities before live notifications. See Bootstrap.
includePreviousfalseIf true, Updated notifications include the previous entity state. See Change Tracking.
reconnectModeLiveOnlyControls what happens on reconnection. See Reconnection.
ctdefaultCancellation token. Cancelling unsubscribes and stops the stream.

Each notification has a Type indicating what happened:

TypeMeaning
CreatedA new entity was created (or re-created from a tombstone).
UpdatedAn existing entity’s field data changed.
DeletedAn entity was tombstoned (all sources retracted).
BootstrapAn entity snapshot from the bootstrap scan. See Bootstrap.

Every notification carries:

PropertyDescription
TypeThe notification type (see above).
EntityThe current entity state (typed as T). Null for Deleted.
EntityIdThe 32-byte entity ID.
VersionThe entity’s current version (monotonically increasing).
SourceSetBitmask of which sources assert this entity.
ChangedFieldsBitmask indicating which fields changed. See Change Tracking.
PreviousEntityThe previous entity state, if includePrevious was true and the type is Updated.
MetadataOpaque metadata from the write that triggered this notification. See Metadata.

In v1, subscriptions filter by entity kind only. You subscribe to all entities of a given kind and receive all changes. Field-level filtering (subscribing only to entities where a specific field matches a predicate) is planned for v2.

If you need to filter on the client side, check the notification’s fields in your processing loop:

await foreach (var change in players.SubscribeAsync(bootstrap: false))
{
if (change.Entity?.Health > 0)
{
// Process only living players
}
}

Each subscription has a bounded notification buffer (default: 4,096 notifications). If the subscriber falls behind and the buffer fills, the subscriber is disconnected by the server.

On reconnection, the subscriber must re-subscribe. If ReconnectBootstrapMode.Full is configured, the subscriber automatically re-bootstraps to recover missed state. See Reconnection.

To avoid buffer overflow:

  • Process notifications promptly in the await foreach loop.
  • Offload expensive work to a background queue rather than processing synchronously in the notification loop.
  • Increase SubscriberBufferSize in ConvergenceOptions if your workload produces large notification bursts.

The subscription is active for the lifetime of the await foreach loop. When the loop exits (via cancellation, break, or exception), the SDK sends an UNSUBSCRIBE frame to the server and releases the subscription resources.

You can also manage subscriptions manually via SubscriptionHandle. See Building Correct State for advanced patterns.

Subscriptions deliver coalesced state: multiple writes within a coalescing window are merged into a single notification. If you need to observe every individual ASSERT, PATCH, or RETRACT operation with its source identity and a total ordering, see Event Streams.