[y-partyserver] provider.destroy() leaks reconnect timer, hanging Node processes and delaying GC
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 2/5
- Tempo stimato
- 1-3 ore
- Idoneità per principianti
- 65/100
- Tipo di issue
- Bug
- Chiarezza
- Abbastanza chiara
- Stato di attività
- Tranquilla
- Stack tecnologico
- node.js, typescript
- Ambito
- backend, networking
Direzione di ricerca
Inizia dal punto di ingresso y-partyserver/provider e segui WebsocketProvider.destroy() attraverso l’evento di chiusura di WebSocket e la pianificazione della riconnessione. Esegui lo script di riproduzione Node.js dell’issue, quindi verifica che la distruzione del provider non lasci alcun timer di riconnessione a mantenere aperto il ciclo degli eventi e che il processo termini senza il ritardo.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
I maintain YAOS a local-first sync engine running inside Obsidian, built on DOs and y-partyserver. While debugging a CI pipeline that inexplicably took 140 seconds instead of 36 seconds, we discovered that y-partyserver is leaking timers directly into the event loop every time a connection is torn down.
The Bug
When provider.destroy() is called on a WebsocketProvider in y-partyserver, the WebSocket's onclose event listener unconditionally queues a reconnect timer via setTimeout(setupWS, backoff, provider).
While the setupWS callback correctly checks this.shouldConnect and aborts (meaning it doesn't actually reconnect), the setTimeout handle itself is never stored or cleared during the destroy() lifecycle.
The Real-World Impact
-
Delayed Garbage Collection (Electron/SPA): In long-lived environments like ours, the setTimeout closure retains the entire provider instance and its massive associated Y.Doc in memory until the timer naturally fires (up to 2.5s). In environments with flapping networks where connections are repeatedly destroyed and recreated, this creates a massive GC backlog and stutters the UI thread.
-
Node.js / CI Hangs: The Node event loop refuses to exit until the backoff timer fires. In test suites that instantiate and destroy multiple providers, this compounds into massive CI delays, forcing developers to use process.exit(0) hacks just to allow scripts to terminate cleanly.
This issue is inherited DNA from y-websocket. The exact same architectural flaw was reported upstream 6 months ago (see yjs/y-websocket#198), but it has not merged. Since y-partyserver ships its own adapted provider implementation, it needs to be patched here.
Steps to Reproduce
Running this Node.js script proves the event loop is held hostage by the leaked timer, even after the provider is explicitly destroyed:
import YSyncProvider from "y-partyserver/provider";
import WebSocket from "ws";
async function run() {
const ydoc = new Y.Doc();
const provider = new YSyncProvider("ws://127.0.0.1:8787", "room", ydoc, {
WebSocketPolyfill: WebSocket
});
// Wait for initial connection logic
await new Promise(r => setTimeout(r, 100));
console.log("Destroying provider...");
provider.destroy();
// Wait a tick for the ws 'close' event to fire and schedule the leak
await new Promise(r => setTimeout(r, 100));
const handles = process._getActiveHandles().filter(
h => h.constructor.name === 'Timeout' || h.constructor.name === 'TimerWrap'
);
console.log(`Active timers preventing exit: ${handles.length}`);
// Process hangs here for up to 2.5 seconds instead of exiting immediately
}
run();
- Lingua principale
- TypeScript
- Stelle
- 1.3k
- Fork
- 84
- Metriche di merge delle PR
- Nessuna PR unita negli ultimi 30g
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Nessun modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di cloudflare/partykit
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
cloudflare/partykit#425 · 1 commento ·
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
cloudflare/partykit#424 · 1 commento ·
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
cloudflare/partykit#423 ·
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
cloudflare/partykit#419 ·
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 65/100
cloudflare/partykit#411 ·
Tutte le issue di cloudflare/partykit
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
marmelab/react-admin#11431 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 88/100
db-ux-design-system/core-web#8655 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 74/100
I maintainer di solito rispondono entro 1 giorno
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
stripe/react-stripe-js#728 ·
-
[Upload Grade C dataset] the pop-up changes it's size while uploading the document => Jumping effectApertabug
Difficoltà 2/5 1-3 ore Idoneità per principianti 66/100
epam/statgpt-admin-frontend#305 ·
I maintainer di solito rispondono entro 1 giorno