@dedot/api 0.9.5-next.11b0ca8e.3 → 0.9.5-next.889fe7cd.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -22,6 +22,7 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
|
|
|
22
22
|
#recovering;
|
|
23
23
|
#blockUsage;
|
|
24
24
|
#cache;
|
|
25
|
+
#operationQueue;
|
|
25
26
|
constructor(client, options) {
|
|
26
27
|
super(client, { prefix: 'chainHead', supportedVersions: ['unstable', 'v1'], ...options });
|
|
27
28
|
this.#handlers = {};
|
|
@@ -32,6 +33,7 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
|
|
|
32
33
|
this.#retryQueue = new utils_1.AsyncQueue();
|
|
33
34
|
this.#blockUsage = new BlockUsage_js_1.BlockUsage();
|
|
34
35
|
this.#cache = new Map();
|
|
36
|
+
this.#operationQueue = new utils_1.ThrottleQueue(this.#__unsafe__isSmoldot() ? 30 : 250);
|
|
35
37
|
}
|
|
36
38
|
async runtimeVersion() {
|
|
37
39
|
await this.#ensureFollowed();
|
|
@@ -236,7 +238,7 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
|
|
|
236
238
|
.then(() => {
|
|
237
239
|
// 3. Resolve the recovering promise
|
|
238
240
|
// This means to continue all pending requests while the chainHead started recovering mode at step 1.
|
|
239
|
-
this.#recovering
|
|
241
|
+
this.#recovering?.resolve();
|
|
240
242
|
// 4. Recover stale operations
|
|
241
243
|
// 4.1. Operations that's going on & waiting to receiving its operationId
|
|
242
244
|
// will eventually get an `limitedReached` error for using a stale followSubscriptionId
|
|
@@ -252,7 +254,7 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
|
|
|
252
254
|
.catch((e) => {
|
|
253
255
|
console.error(e);
|
|
254
256
|
// TODO we should retry a few attempts
|
|
255
|
-
this.#recovering
|
|
257
|
+
this.#recovering?.reject(new error_js_1.ChainHeadError('Cannot recover from stop event!'));
|
|
256
258
|
Object.values(this.#handlers).forEach(({ defer, operationId }) => {
|
|
257
259
|
defer.reject(new error_js_1.ChainHeadError('Cannot recover from stop event!'));
|
|
258
260
|
delete this.#handlers[operationId];
|
|
@@ -400,6 +402,7 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
|
|
|
400
402
|
this.#retryQueue.clear();
|
|
401
403
|
this.#blockUsage.clear();
|
|
402
404
|
this.#cache.clear();
|
|
405
|
+
this.#operationQueue.cancel();
|
|
403
406
|
}
|
|
404
407
|
async #ensureFollowed() {
|
|
405
408
|
if (this.#recovering) {
|
|
@@ -485,7 +488,7 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
|
|
|
485
488
|
const resp = await this.send('body', this.#subscriptionId, hash);
|
|
486
489
|
return this.#awaitOperation(resp, hash);
|
|
487
490
|
};
|
|
488
|
-
const resp = await this.#performOperationWithRetry(operation, atHash);
|
|
491
|
+
const resp = await this.#operationQueue.add(() => this.#performOperationWithRetry(operation, atHash));
|
|
489
492
|
this.#cache.set(cacheKey, resp);
|
|
490
493
|
return resp;
|
|
491
494
|
}
|
|
@@ -514,7 +517,7 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
|
|
|
514
517
|
const resp = await this.send('call', this.#subscriptionId, hash, func, params);
|
|
515
518
|
return this.#awaitOperation(resp, hash);
|
|
516
519
|
};
|
|
517
|
-
const resp = await this.#performOperationWithRetry(operation, atHash);
|
|
520
|
+
const resp = await this.#operationQueue.add(() => this.#performOperationWithRetry(operation, atHash));
|
|
518
521
|
this.#cache.set(cacheKey, resp);
|
|
519
522
|
return resp;
|
|
520
523
|
}
|
|
@@ -557,9 +560,7 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
|
|
|
557
560
|
}
|
|
558
561
|
this.#blockUsage.use(hash);
|
|
559
562
|
let results = [];
|
|
560
|
-
|
|
561
|
-
const isSmoldot = typeof this.client.provider['chain'] === 'function';
|
|
562
|
-
if (isSmoldot) {
|
|
563
|
+
if (this.#__unsafe__isSmoldot()) {
|
|
563
564
|
const fetchItem = async (item) => {
|
|
564
565
|
const [batch, newDiscardedItems] = await this.#getStorage([item], childTrie ?? null, hash);
|
|
565
566
|
if (newDiscardedItems.length > 0) {
|
|
@@ -592,7 +593,7 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
|
|
|
592
593
|
}
|
|
593
594
|
async #getStorage(items, childTrie, at) {
|
|
594
595
|
const operation = () => this.#getStorageOperation(items, childTrie, this.#ensurePinnedHash(at));
|
|
595
|
-
return this.#performOperationWithRetry(operation, at);
|
|
596
|
+
return this.#operationQueue.add(() => this.#performOperationWithRetry(operation, at));
|
|
596
597
|
}
|
|
597
598
|
async #getStorageOperation(items, childTrie, at) {
|
|
598
599
|
await this.#ensureFollowed();
|
|
@@ -629,5 +630,9 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
|
|
|
629
630
|
return;
|
|
630
631
|
await this.send('unpin', this.#subscriptionId, hashes);
|
|
631
632
|
}
|
|
633
|
+
#__unsafe__isSmoldot() {
|
|
634
|
+
// @ts-ignore a trick internally to check whether a provider is using smoldot connection
|
|
635
|
+
return typeof this.client.provider['chain'] === 'function';
|
|
636
|
+
}
|
|
632
637
|
}
|
|
633
638
|
exports.ChainHead = ChainHead;
|
|
@@ -83,7 +83,13 @@ class NewStorageQuery extends BaseStorageQuery_js_1.BaseStorageQuery {
|
|
|
83
83
|
await pull(best);
|
|
84
84
|
// Subscribe to best block events
|
|
85
85
|
const unsub = this.client.on('bestBlock', (block) => {
|
|
86
|
-
|
|
86
|
+
// Here we're handling each pull one by one,
|
|
87
|
+
// If the queue get too long, it might take a long time for us to get the fresh & latest data
|
|
88
|
+
// This is a precaution in such case, if the queue size >= 3 we skip all the pending pull and jump to the latest pull
|
|
89
|
+
if (pullQueue.size >= 3)
|
|
90
|
+
pullQueue.clear();
|
|
91
|
+
// TODO timing out for a pull to prevent it took too long to fetch
|
|
92
|
+
pullQueue.enqueue(() => pull(block)).catch(utils_1.noop);
|
|
87
93
|
});
|
|
88
94
|
return async () => {
|
|
89
95
|
unsub();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { $Header } from '@dedot/codecs';
|
|
2
|
-
import { assert, AsyncQueue, deferred, ensurePresence, noop, waitFor } from '@dedot/utils';
|
|
2
|
+
import { assert, AsyncQueue, deferred, ensurePresence, noop, ThrottleQueue, waitFor, } from '@dedot/utils';
|
|
3
3
|
import { JsonRpcGroup } from '../JsonRpcGroup.js';
|
|
4
4
|
import { BlockUsage } from './BlockUsage.js';
|
|
5
5
|
import { ChainHeadBlockNotPinnedError, ChainHeadBlockPrunedError, ChainHeadError, ChainHeadLimitReachedError, ChainHeadOperationError, ChainHeadOperationInaccessibleError, ChainHeadStopError, RetryStrategy, } from './error.js';
|
|
@@ -19,6 +19,7 @@ export class ChainHead extends JsonRpcGroup {
|
|
|
19
19
|
#recovering;
|
|
20
20
|
#blockUsage;
|
|
21
21
|
#cache;
|
|
22
|
+
#operationQueue;
|
|
22
23
|
constructor(client, options) {
|
|
23
24
|
super(client, { prefix: 'chainHead', supportedVersions: ['unstable', 'v1'], ...options });
|
|
24
25
|
this.#handlers = {};
|
|
@@ -29,6 +30,7 @@ export class ChainHead extends JsonRpcGroup {
|
|
|
29
30
|
this.#retryQueue = new AsyncQueue();
|
|
30
31
|
this.#blockUsage = new BlockUsage();
|
|
31
32
|
this.#cache = new Map();
|
|
33
|
+
this.#operationQueue = new ThrottleQueue(this.#__unsafe__isSmoldot() ? 30 : 250);
|
|
32
34
|
}
|
|
33
35
|
async runtimeVersion() {
|
|
34
36
|
await this.#ensureFollowed();
|
|
@@ -233,7 +235,7 @@ export class ChainHead extends JsonRpcGroup {
|
|
|
233
235
|
.then(() => {
|
|
234
236
|
// 3. Resolve the recovering promise
|
|
235
237
|
// This means to continue all pending requests while the chainHead started recovering mode at step 1.
|
|
236
|
-
this.#recovering
|
|
238
|
+
this.#recovering?.resolve();
|
|
237
239
|
// 4. Recover stale operations
|
|
238
240
|
// 4.1. Operations that's going on & waiting to receiving its operationId
|
|
239
241
|
// will eventually get an `limitedReached` error for using a stale followSubscriptionId
|
|
@@ -249,7 +251,7 @@ export class ChainHead extends JsonRpcGroup {
|
|
|
249
251
|
.catch((e) => {
|
|
250
252
|
console.error(e);
|
|
251
253
|
// TODO we should retry a few attempts
|
|
252
|
-
this.#recovering
|
|
254
|
+
this.#recovering?.reject(new ChainHeadError('Cannot recover from stop event!'));
|
|
253
255
|
Object.values(this.#handlers).forEach(({ defer, operationId }) => {
|
|
254
256
|
defer.reject(new ChainHeadError('Cannot recover from stop event!'));
|
|
255
257
|
delete this.#handlers[operationId];
|
|
@@ -397,6 +399,7 @@ export class ChainHead extends JsonRpcGroup {
|
|
|
397
399
|
this.#retryQueue.clear();
|
|
398
400
|
this.#blockUsage.clear();
|
|
399
401
|
this.#cache.clear();
|
|
402
|
+
this.#operationQueue.cancel();
|
|
400
403
|
}
|
|
401
404
|
async #ensureFollowed() {
|
|
402
405
|
if (this.#recovering) {
|
|
@@ -482,7 +485,7 @@ export class ChainHead extends JsonRpcGroup {
|
|
|
482
485
|
const resp = await this.send('body', this.#subscriptionId, hash);
|
|
483
486
|
return this.#awaitOperation(resp, hash);
|
|
484
487
|
};
|
|
485
|
-
const resp = await this.#performOperationWithRetry(operation, atHash);
|
|
488
|
+
const resp = await this.#operationQueue.add(() => this.#performOperationWithRetry(operation, atHash));
|
|
486
489
|
this.#cache.set(cacheKey, resp);
|
|
487
490
|
return resp;
|
|
488
491
|
}
|
|
@@ -511,7 +514,7 @@ export class ChainHead extends JsonRpcGroup {
|
|
|
511
514
|
const resp = await this.send('call', this.#subscriptionId, hash, func, params);
|
|
512
515
|
return this.#awaitOperation(resp, hash);
|
|
513
516
|
};
|
|
514
|
-
const resp = await this.#performOperationWithRetry(operation, atHash);
|
|
517
|
+
const resp = await this.#operationQueue.add(() => this.#performOperationWithRetry(operation, atHash));
|
|
515
518
|
this.#cache.set(cacheKey, resp);
|
|
516
519
|
return resp;
|
|
517
520
|
}
|
|
@@ -554,9 +557,7 @@ export class ChainHead extends JsonRpcGroup {
|
|
|
554
557
|
}
|
|
555
558
|
this.#blockUsage.use(hash);
|
|
556
559
|
let results = [];
|
|
557
|
-
|
|
558
|
-
const isSmoldot = typeof this.client.provider['chain'] === 'function';
|
|
559
|
-
if (isSmoldot) {
|
|
560
|
+
if (this.#__unsafe__isSmoldot()) {
|
|
560
561
|
const fetchItem = async (item) => {
|
|
561
562
|
const [batch, newDiscardedItems] = await this.#getStorage([item], childTrie ?? null, hash);
|
|
562
563
|
if (newDiscardedItems.length > 0) {
|
|
@@ -589,7 +590,7 @@ export class ChainHead extends JsonRpcGroup {
|
|
|
589
590
|
}
|
|
590
591
|
async #getStorage(items, childTrie, at) {
|
|
591
592
|
const operation = () => this.#getStorageOperation(items, childTrie, this.#ensurePinnedHash(at));
|
|
592
|
-
return this.#performOperationWithRetry(operation, at);
|
|
593
|
+
return this.#operationQueue.add(() => this.#performOperationWithRetry(operation, at));
|
|
593
594
|
}
|
|
594
595
|
async #getStorageOperation(items, childTrie, at) {
|
|
595
596
|
await this.#ensureFollowed();
|
|
@@ -626,4 +627,8 @@ export class ChainHead extends JsonRpcGroup {
|
|
|
626
627
|
return;
|
|
627
628
|
await this.send('unpin', this.#subscriptionId, hashes);
|
|
628
629
|
}
|
|
630
|
+
#__unsafe__isSmoldot() {
|
|
631
|
+
// @ts-ignore a trick internally to check whether a provider is using smoldot connection
|
|
632
|
+
return typeof this.client.provider['chain'] === 'function';
|
|
633
|
+
}
|
|
629
634
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dedot/api",
|
|
3
|
-
"version": "0.9.5-next.
|
|
3
|
+
"version": "0.9.5-next.889fe7cd.3+889fe7c",
|
|
4
4
|
"description": "A delightful JavaScript/TypeScript client for Polkadot & Substrate",
|
|
5
5
|
"author": "Thang X. Vu <thang@dedot.dev>",
|
|
6
6
|
"homepage": "https://dedot.dev",
|
|
@@ -13,13 +13,13 @@
|
|
|
13
13
|
"type": "module",
|
|
14
14
|
"sideEffects": false,
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@dedot/codecs": "0.9.5-next.
|
|
17
|
-
"@dedot/providers": "0.9.5-next.
|
|
18
|
-
"@dedot/runtime-specs": "0.9.5-next.
|
|
19
|
-
"@dedot/shape": "0.9.5-next.
|
|
20
|
-
"@dedot/storage": "0.9.5-next.
|
|
21
|
-
"@dedot/types": "0.9.5-next.
|
|
22
|
-
"@dedot/utils": "0.9.5-next.
|
|
16
|
+
"@dedot/codecs": "0.9.5-next.889fe7cd.3+889fe7c",
|
|
17
|
+
"@dedot/providers": "0.9.5-next.889fe7cd.3+889fe7c",
|
|
18
|
+
"@dedot/runtime-specs": "0.9.5-next.889fe7cd.3+889fe7c",
|
|
19
|
+
"@dedot/shape": "0.9.5-next.889fe7cd.3+889fe7c",
|
|
20
|
+
"@dedot/storage": "0.9.5-next.889fe7cd.3+889fe7c",
|
|
21
|
+
"@dedot/types": "0.9.5-next.889fe7cd.3+889fe7c",
|
|
22
|
+
"@dedot/utils": "0.9.5-next.889fe7cd.3+889fe7c"
|
|
23
23
|
},
|
|
24
24
|
"scripts": {
|
|
25
25
|
"build": "tsc --project tsconfig.build.json && tsc --project tsconfig.build.cjs.json",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"node": ">=18"
|
|
49
49
|
},
|
|
50
50
|
"license": "Apache-2.0",
|
|
51
|
-
"gitHead": "
|
|
51
|
+
"gitHead": "889fe7cd88f69202d4634cfe9fd25900b41a8935",
|
|
52
52
|
"module": "./index.js",
|
|
53
53
|
"types": "./index.d.ts"
|
|
54
54
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AsyncQueue } from '@dedot/utils';
|
|
1
|
+
import { AsyncQueue, noop } from '@dedot/utils';
|
|
2
2
|
import { BaseStorageQuery } from './BaseStorageQuery.js';
|
|
3
3
|
/**
|
|
4
4
|
* @name NewStorageQuery
|
|
@@ -80,7 +80,13 @@ export class NewStorageQuery extends BaseStorageQuery {
|
|
|
80
80
|
await pull(best);
|
|
81
81
|
// Subscribe to best block events
|
|
82
82
|
const unsub = this.client.on('bestBlock', (block) => {
|
|
83
|
-
|
|
83
|
+
// Here we're handling each pull one by one,
|
|
84
|
+
// If the queue get too long, it might take a long time for us to get the fresh & latest data
|
|
85
|
+
// This is a precaution in such case, if the queue size >= 3 we skip all the pending pull and jump to the latest pull
|
|
86
|
+
if (pullQueue.size >= 3)
|
|
87
|
+
pullQueue.clear();
|
|
88
|
+
// TODO timing out for a pull to prevent it took too long to fetch
|
|
89
|
+
pullQueue.enqueue(() => pull(block)).catch(noop);
|
|
84
90
|
});
|
|
85
91
|
return async () => {
|
|
86
92
|
unsub();
|