Connection state machine: reconnect + resubscribe #5
Labels
No labels
area:defold
area:docs
area:infra
area:protocol
area:testing
kind:bug
kind:chore
phase:breadth
phase:core
phase:hardening
phase:slice
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
yuris/spacetimedb-defold#5
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
On disconnect the SDK should reconnect with exponential backoff and replay subscriptions (new query_set_ids), dropping stale cache.
on_eventgetsreconnecting/reconnectedtypesPartially delivered in
ee90548:connection.resubscribe_all()tracks live subscriptions, clears the cache (so stale rows cannot survive the gap), and replays every query. Covered by a unit test with a fake transport.What is still missing is the part that needs the engine: nothing currently calls it.
stdb/init.luasurfacesdisconnectedas an event but does not reconnect the websocket or back off.EVENT_DISCONNECTEDwith exponential backoff (double, cap ~30s)resubscribe_all()once the new connection reportsconnectedreconnecting/reconnectedeventsmain/smoke.scriptto kill and restore the server mid-session, proving recovery in-engineClosed in
4c0682a— proven against a real server restartThe binding now reconnects on its own, and the conformance test kills the server to prove it:
tools/conformance.shruns the engine in the background, waits forPHASE1 OK, runsdocker restartunderneath it, and the test only passes if the client comes back and sees the row it inserted before the drop.What landed
connection.backoff_delay(attempt, base, cap)— pure, doubling, clamped. Unit tested at the boundaries (attempt 0, clamp, custom base). Defaults 1s → 30s, configurable viabackoff_base/backoff_cap.IdentityToken, so the client keeps its identity across the gap.IdentityToken, the cache is dropped and every live subscription replayed. Stale rows cannot outlive a disconnect — that was the risk worth protecting.set_send: swapping transport also clearspending, so a callback owed by the dead socket can never fire against the new one. Unit tested.db:close()stops reconnecting;reconnect = falseopts out entirely.disconnected,reconnecting(attempt, delay),reconnected(subscriptions replayed).Known cosmetic wrinkle
extension-websocketfires ERROR before DISCONNECTED, so the log can show "reconnecting attempt 1" just before "server dropped us". The scheduler is idempotent (reconnect_pendingguard), so only one attempt is queued — the ordering is just noisy, not wrong.Deliberately not done
No jitter on the backoff. Fine for a handful of clients; if a server restart ever stampedes a whole player base, spread the retries. Marked with a
ponytail:comment at the call site.