-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Description
Summary
Calling collection.migratePromise() causes a memory leak because the internal replication state is never properly cleaned up after migration completes.
Root Cause
In rx-migration-state.ts, the migrateStorage() method creates a replicationState via replicateRxStorageInstance(), but this local variable is never assigned to this.replicationState:
const replicationState = replicateRxStorageInstance({...}); // Line 375
// ... migration happens ...
await this.cancel(); // Line 474 - but this.replicationState is undefined!When cancel() runs, it checks if (this.replicationState) which is always undefined, so cancelRxStorageReplication() is never called.
Consequence
The subscriptions created in upstream.ts are never unsubscribed:
forkInstance.changeStream().subscribe(...)keeps a reference to the old storagemasterChangeStream$.subscribe(...)also remains active
These subscriptions are only cleaned up when state.events.canceled emits true, which only happens when cancelRxStorageReplication() is called.
Heap Snapshot Evidence
The retainer path shows:
sessionId → docData → previousDocumentData → events → task → Array → openTasks → Context → oldStorage → RxStorageInstanceDexie → changes$ → Subject
This confirms the openTasks array in upstream.ts and the changes$ Subject are being retained.
Environment
- RxDB version: 16.21.1+
- Storage: Dexie (IndexedDB)
- Plugins: RxDBMigrationSchemaPlugin, RxDBLeaderElectionPlugin
Fix
Add one line after creating the replication state in migrateStorage():
const replicationState = replicateRxStorageInstance({...});
this.replicationState = replicationState; // ADD THIS LINEThis allows cancel() to properly call cancelRxStorageReplication() which emits the cancel event and triggers subscription cleanup.