chore: recover from backend restarts via backend-owned project lifecycle (tracking)

Aperta
#247 2 commenti 0 reazioni 2 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

@RangerMauve ci sta già lavorando.

Dal 31/8/2026.

Valutazione

Questa issue non è ancora stata valutata.

Descrizione

The problem

The :ComapeoCore foreground service runs in its own process, so Android can kill and restart it (low-memory killer, watchdog) while the app process lives on. On current released code that leaves the frontend permanently stuck: the data socket has no reconnect and no state signal into JS, in-flight RPC calls hang until a 30 s timeout, every event subscription goes permanently deaf (server-side rpc-reflector state is per-connection), and the control socket's drop latches the app into a fatal-error screen — which unmounts the very tree whose activity would have triggered a reconnect.

Underneath that sits a structural issue in @comapeo/ipc v9: project instance lifecycle is client-visible, via per-open instance ids minted from a per-connection counter. Five successive fix PRs (digidem/comapeo-ipc#47, digidem/comapeo-ipc#49, digidem/comapeo-ipc#61, digidem/comapeo-ipc#74, digidem/comapeo-ipc#83/digidem/comapeo-ipc#84) each patched a race the previous one introduced, and the design is unsound across a restart — a restarted (or merely reconnected) backend can remint the exact id a cached wrapper is bound to, so revalidation falsely passes. It also produced the leave/re-invite bugs (digidem/comapeo-mobile#2041, digidem/comapeo-mobile#2042).

The direction

Make the backend own instance lifecycle entirely. Per-project channels are keyed by the stable projectPublicId; client references are permanent; the backend closes and re-opens MapeoProject instances behind them invisibly (leave/re-join today, memory-pressure eviction later). With no client-visible instance identity, the whole class of stale-reference races disappears, and restart recovery reduces to two phases: reject pending calls at drop time, resubscribe once reconnected — no cache invalidation of references, no generation guards. The one client-visible lifecycle state is "you left this project", surfaced as a typed PROJECT_LEFT error.

The pieces

  • digidem/rpc-reflector#55 — late-bound handlers: a server's handler becomes a replaceable plug-in behind a durable subscription registry (createServer accepts a factory; detachHandler/ensureHandler). This is the mechanism that lets instances swap behind a stable channel without the IPC layer inspecting wire frames.
  • digidem/comapeo-ipc#88 — v10: stable channels, permanent references, thin server host on late-bound handlers, PROJECT_LEFT semantics, and the notifyTransportReset/resubscribe recovery helpers (supersedes digidem/comapeo-ipc#87/digidem/comapeo-ipc#89).
  • #226 (this repo) — Kotlin socket reconnect with backoff and epoch-scoped teardown, transportStateChange into JS, the two-phase recovery wiring, subscribeToBackendRestart, and a boot nonce in the control channel's ready frame so a reconnect to a surviving backend is distinguished from a genuine restart (supersedes #225; carries its commits unchanged).
  • digidem/comapeo-core-react#202 — the hook layer: reset/refetch query caches on the restart signal, refresh sync stores (v10's permanent references removed the accidental recovery path), bounded query-only retry on the transport error code, survivable map-share streams.
  • digidem/comapeo-core#1319 — core-level lifecycle hardening (tracking): the typed PROJECT_LEFT error that lets the IPC layer drop its interim leave logic, plus getProject races, close-event fixes, and related robustness issues (digidem/comapeo-core#1313–digidem/comapeo-core#1318).

Remaining work

comapeo-mobile must stop treating the transport drop as fatal (keep the tree mounted with a reconnecting affordance; wire subscribeToBackendRestart to the query reset and re-derive its module-scope captures). On-device e2e needs the published packages and COMAPEO_DEBUG_LIFECYCLE plumbed into the native start path. Out of scope for now, pending memory measurements and the hypercore 11 migration: boot-time re-opening of projects (including left projects whose leave record hasn't propagated) and the eviction policy.

Merge order is forced by dependencies: digidem/rpc-reflector#55 → release → digidem/comapeo-ipc#88 lockfile + merge → release → #226 and digidem/comapeo-core-react#202 lockfile bumps. Until the upstream releases, npm ci fails on the downstream branches by design.

Lingua principale
Kotlin
Stelle
1
Fork
0
Merge medio
1g 6h
PR unite (30g)
5

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di digidem/comapeo-core-react-native

Tutte le issue di digidem/comapeo-core-react-native

Issue simili

Altre issue su Kotlin

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.