Skip to content

Memory leak: migratePromise() doesn't clean up internal replication state #7786

@OskarD

Description

@OskarD

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 storage
  • masterChangeStream$.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 LINE

This allows cancel() to properly call cancelRxStorageReplication() which emits the cancel event and triggers subscription cleanup.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions