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:
@@ -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> {
|
||||
|
||||
Reference in New Issue
Block a user