Bootstrap
When you subscribe with bootstrap: true, the server sends a complete snapshot of all existing entities before transitioning to live notifications. This ensures the subscriber starts from a consistent and complete view.
Server guarantees
Section titled “Server guarantees”The server uses a subscribe-before-scan approach:
- The subscription is registered for live notifications.
- The server begins scanning all entities of the subscribed kind.
- Scanned entities are sent as
Bootstrapnotifications. - When the scan is complete, the server sends a
BOOTSTRAP_ENDmarker.
Because the subscription is registered before the scan begins:
- No gaps. Every entity that exists at subscribe time, or is created/updated after, is delivered at least once. You will never miss a change.
- Duplicates are possible. An entity that changes during the scan window may arrive both as a
Bootstrapnotification and as a liveCreatedorUpdatednotification. This is by design. Gaps would mean stale data, while duplicates are easily resolved.
Notification interleaving during bootstrap
Section titled “Notification interleaving during bootstrap”During the bootstrap window, you may receive Bootstrap and live notifications interleaved. The server sends them on the same TCP connection in order, but bootstrap frames from different partitions may interleave with live notifications from the flush cycle.
Do not assume that all Bootstrap notifications arrive before any live notifications. Process all notifications the same way regardless of type.
Resolving duplicates: version wins
Section titled “Resolving duplicates: version wins”Every notification carries a Version that is monotonically increasing per entity. The correct merge rule is: highest version wins, regardless of notification type or arrival order.
If you receive:
- A
Bootstrapfor entity X at version 3, then a liveUpdatedfor entity X at version 4: apply version 4. - A live
Updatedfor entity X at version 4, then aBootstrapfor entity X at version 3: ignore the bootstrap (you already have newer data).
Recommended pattern
Section titled “Recommended pattern”var state = new Dictionary<ReadOnlyMemory<byte>, (ulong Version, Player Entity), EntityIdComparer.Instance>();
await foreach (var change in players.SubscribeAsync(bootstrap: true, ct: ct)){ switch (change.Type) { case NotificationType.Bootstrap: case NotificationType.Created: case NotificationType.Updated: { if (change.Entity is not { } entity) break;
// Apply only if this is newer than what we have. if (state.TryGetValue(change.EntityId, out var existing) && existing.Version >= change.Version) break; // Stale duplicate, ignore.
state[change.EntityId] = (change.Version, entity); break; } case NotificationType.Deleted: { // Deleted wins over same-version bootstrap/created. if (state.TryGetValue(change.EntityId, out var existing) && existing.Version > change.Version) break; // We have a newer resurrection, ignore delete.
state.Remove(change.EntityId); break; } }}Detecting bootstrap completion
Section titled “Detecting bootstrap completion”The SubscriptionHandle exposes a BootstrapStatus property that transitions through:
NotRequested -> InProgress -> Complete
var sub = await players.SubscribeAsync(bootstrap: true, ct: ct);
sub.BootstrapStatusChanged += status =>{ if (status == BootstrapState.Complete) Console.WriteLine("Bootstrap complete. Local state is now current.");};
await foreach (var change in sub.ReadAllAsync(ct)){ ApplyChange(state, change); // same version-wins logic}Important: Do not use BootstrapStatus to filter notifications. Process all notifications the same way (version-wins merge) regardless of whether bootstrap is in progress. The status is informational: it tells you when you have a complete snapshot, not when to start processing differently.
One-shot bootstrap
Section titled “One-shot bootstrap”If you need a point-in-time snapshot without maintaining a live subscription, use BootstrapSnapshotAsync:
IReadOnlyList<Player> snapshot = await players.BootstrapSnapshotAsync();This subscribes with bootstrap: true, waits for bootstrap to complete, collects all entities, and unsubscribes. It is useful for periodic reconciliation tasks.
Correctness rules summary
Section titled “Correctness rules summary”- Always apply version-wins.
Bootstrap,Created, andUpdatedall carry entity data and a version. Highest version wins, regardless of notification type or arrival order. - Never ignore live notifications during bootstrap. They may carry newer state than the scan.
- Treat
BootstrapandCreatedidentically for merge purposes. Both mean “here is the current state of this entity.” Deletedremoves the entity unless you already have a higher version (which would mean the entity was re-created after the delete).BootstrapStatus == Completemeans the server has finished scanning. After this point, your local state is fully caught up (assuming you applied all notifications).