Promise cache for metadata from version (improves parallel) (#4708)

* Cache for metadata from version (improves parallel)

* Ordering

* Single waiting placeholder

* Dedupe

* Sprinkle in await

* Tighten up with HexString

* CHANGELOG
This commit is contained in:
Jaco
2022-03-31 10:46:46 +03:00
committed by GitHub
parent a98e9543b8
commit df4bda1fc8
2 changed files with 56 additions and 51 deletions
+55 -51
View File
@@ -4,7 +4,7 @@
import type { Observable, Subscription } from 'rxjs';
import type { Text } from '@polkadot/types';
import type { ExtDef } from '@polkadot/types/extrinsic/signedExtensions/types';
import type { ChainProperties, Hash, RuntimeVersion, RuntimeVersionPartial } from '@polkadot/types/interfaces';
import type { ChainProperties, Hash, HeaderPartial, RuntimeVersion, RuntimeVersionPartial } from '@polkadot/types/interfaces';
import type { Registry } from '@polkadot/types/types';
import type { BN } from '@polkadot/util';
import type { HexString } from '@polkadot/util/types';
@@ -35,7 +35,7 @@ export abstract class Init<ApiType extends ApiTypes> extends Decorate<ApiType> {
#updateSub?: Subscription | null = null;
#waitingRegistries: Record<string, Promise<VersionedRegistry<ApiType>>> = {};
#waitingRegistries: Record<HexString, Promise<VersionedRegistry<ApiType>>> = {};
constructor (options: ApiOptions, type: ApiTypes, decorateMethod: DecorateMethod<ApiType>) {
super(options, type, decorateMethod);
@@ -120,6 +120,44 @@ export abstract class Init<ApiType extends ApiTypes> extends Decorate<ApiType> {
return this._createDecorated(registry, true, null, u8aHash).decoratedApi;
}
private async _createBlockRegistry (blockHash: Uint8Array, header: HeaderPartial, version: RuntimeVersionPartial): Promise<VersionedRegistry<ApiType>> {
const registry = new TypeRegistry(blockHash);
const metadata = new Metadata(registry,
await firstValueFrom(this._rpcCore.state.getMetadata.raw<HexString>(header.parentHash))
);
this._initRegistry(registry, this._runtimeChain as Text, version, metadata);
// add our new registry
const result = { lastBlockHash: blockHash, metadata, registry, specName: version.specName, specVersion: version.specVersion };
this.#registries.push(result);
return result;
}
private _cacheBlockRegistryProgress (key: HexString, creator: () => Promise<VersionedRegistry<ApiType>>): Promise<VersionedRegistry<ApiType>> {
// look for waiting resolves
let waiting = this.#waitingRegistries[key];
if (isUndefined(waiting)) {
// nothing waiting, construct new
waiting = this.#waitingRegistries[key] = new Promise<VersionedRegistry<ApiType>>((resolve, reject): void => {
creator()
.then((registry): void => {
delete this.#waitingRegistries[key];
resolve(registry);
})
.catch((error): void => {
delete this.#waitingRegistries[key];
reject(error);
});
});
}
return waiting;
}
private _getBlockRegistryViaVersion (blockHash: Uint8Array, version?: RuntimeVersionPartial): VersionedRegistry<ApiType> | null {
if (version) {
// check for pre-existing registries. We also check specName, e.g. it
@@ -139,7 +177,7 @@ export abstract class Init<ApiType extends ApiTypes> extends Decorate<ApiType> {
return null;
}
private async _getBlockRegistry (blockHash: Uint8Array): Promise<VersionedRegistry<ApiType>> {
private async _getBlockRegistryViaHash (blockHash: Uint8Array): Promise<VersionedRegistry<ApiType>> {
// ensure we have everything required
assert(this._genesisHash && this._runtimeVersion, 'Cannot retrieve data on an uninitialized chain');
@@ -161,62 +199,28 @@ export abstract class Init<ApiType extends ApiTypes> extends Decorate<ApiType> {
: await firstValueFrom(this._rpcCore.state.getRuntimeVersion.raw(header.parentHash))
);
const existingViaVersion = this._getBlockRegistryViaVersion(blockHash, version);
if (existingViaVersion) {
return existingViaVersion;
}
// nothing has been found, construct new
const registry = new TypeRegistry(blockHash);
const metadata = new Metadata(registry,
await firstValueFrom(this._rpcCore.state.getMetadata.raw<HexString>(header.parentHash))
return (
// try to find via version
this._getBlockRegistryViaVersion(blockHash, version) ||
// return new or in-flight result
await this._cacheBlockRegistryProgress(version.toHex(), () => this._createBlockRegistry(blockHash, header, version))
);
this._initRegistry(registry, this._runtimeChain as Text, version, metadata);
// add our new registry
const result = { lastBlockHash: blockHash, metadata, registry, specName: version.specName, specVersion: version.specVersion };
this.#registries.push(result);
return result;
}
/**
* @description Sets up a registry based on the block hash defined
*/
public async getBlockRegistry (blockHash: Uint8Array, knownVersion?: RuntimeVersion): Promise<VersionedRegistry<ApiType>> {
const existingViaHash = this.#registries.find(({ lastBlockHash }) =>
lastBlockHash && u8aEq(lastBlockHash, blockHash)
return (
// try to find via blockHash
this.#registries.find(({ lastBlockHash }) =>
lastBlockHash && u8aEq(lastBlockHash, blockHash)
) ||
// try to find via version
this._getBlockRegistryViaVersion(blockHash, knownVersion) ||
// return new or in-flight result
await this._cacheBlockRegistryProgress(u8aToHex(blockHash), () => this._getBlockRegistryViaHash(blockHash))
);
if (existingViaHash) {
return existingViaHash;
}
const existingViaVersion = this._getBlockRegistryViaVersion(blockHash, knownVersion);
if (existingViaVersion) {
return existingViaVersion;
}
const blockHashHex = u8aToHex(blockHash);
let waiting = this.#waitingRegistries[blockHashHex];
if (isUndefined(waiting)) {
waiting = this._getBlockRegistry(blockHash);
this.#waitingRegistries[blockHashHex] = waiting;
// when we have resolved, remove this from the waiting list
waiting
.then((): void => {
delete this.#waitingRegistries[blockHashHex];
})
.catch(() => undefined);
}
return waiting;
}
protected async _loadMeta (): Promise<boolean> {