From 0660d0cf159852ced81bb4d5b3b4e9948f9059fd Mon Sep 17 00:00:00 2001 From: r0t0r-r0t0r Date: Sat, 29 Jan 2022 13:41:02 +0300 Subject: [PATCH] Fix memory leak on raw rpc call (#4505) * Fix memory leak on raw rpc call While processing a raw rpc call an observable is removed from the wrong cache. It causes a memory leak. * Fix memory leak on raw rpc subscription, fix types Fixed similar memory leak on raw rpc subscription. Also added proper type declaration for the return value of the _memomize function to address build error. --- packages/rpc-core/src/bundle.ts | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/packages/rpc-core/src/bundle.ts b/packages/rpc-core/src/bundle.ts index f4688e4ccc..812468b668 100644 --- a/packages/rpc-core/src/bundle.ts +++ b/packages/rpc-core/src/bundle.ts @@ -24,6 +24,11 @@ interface StorageChangeSetJSON { changes: [string, string | null][]; } +type MemoizedRpcInterfaceMethod = Memoized & { + raw: Memoized; + meta: DefinitionRpc; +} + const l = logger('rpc-core'); const EMPTY_META = { @@ -174,14 +179,14 @@ export class RpcCore { } } - private _memomize (creator: (isScale: boolean) => (...values: unknown[]) => Observable, def: DefinitionRpc): Memoized { + private _memomize (creator: (isScale: boolean) => (...values: unknown[]) => Observable, def: DefinitionRpc): MemoizedRpcInterfaceMethod { const memoOpts = { getInstanceId: () => this.#instanceId }; const memoized = memoize(creator(true) as RpcInterfaceMethod, memoOpts); memoized.raw = memoize(creator(false), memoOpts); memoized.meta = def; - return memoized; + return memoized as MemoizedRpcInterfaceMethod; } private _formatResult (isScale: boolean, registry: Registry, blockHash: string | Uint8Array | null | undefined, method: string, def: DefinitionRpc, params: Codec[], result: unknown): T { @@ -193,7 +198,7 @@ export class RpcCore { private _createMethodSend (section: string, method: string, def: DefinitionRpc): RpcInterfaceMethod { const rpcName = def.endpoint || `${section}_${method}`; const hashIndex = def.params.findIndex(({ isHistoric }) => isHistoric); - let memoized: null | Memoized = null; + let memoized: null | MemoizedRpcInterfaceMethod = null; // execute the RPC call, doing a registry swap for historic as applicable const callWithRegistry = async (isScale: boolean, values: unknown[]): Promise => { @@ -229,7 +234,11 @@ export class RpcCore { return (): void => { // delete old results from cache - memoized?.unmemoize(...values); + if (isScale) { + memoized?.unmemoize(...values); + } else { + memoized?.raw.unmemoize(...values); + } }; }).pipe( publishReplay(1), // create a Replay(1) @@ -262,7 +271,7 @@ export class RpcCore { const subName = `${section}_${subMethod}`; const unsubName = `${section}_${unsubMethod}`; const subType = `${section}_${updateType}`; - let memoized: null | Memoized = null; + let memoized: null | MemoizedRpcInterfaceMethod = null; const creator = (isScale: boolean) => (...values: unknown[]): Observable => { return new Observable((observer: Observer): () => void => { @@ -302,7 +311,11 @@ export class RpcCore { // Teardown logic return (): void => { // Delete from cache, so old results don't hang around - memoized?.unmemoize(...values); + if (isScale) { + memoized?.unmemoize(...values); + } else { + memoized?.raw.unmemoize(...values); + } // Unsubscribe from provider subscriptionPromise