@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.resolve();
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.reject(new error_js_1.ChainHeadError('Cannot recover from stop event!'));
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
- // @ts-ignore a trick internally to check whether a provider is using smoldot connection
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
- pullQueue.enqueue(() => pull(block)).catch(console.error);
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.resolve();
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.reject(new ChainHeadError('Cannot recover from stop event!'));
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
- // @ts-ignore a trick internally to check whether a provider is using smoldot connection
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.11b0ca8e.3+11b0ca8",
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.11b0ca8e.3+11b0ca8",
17
- "@dedot/providers": "0.9.5-next.11b0ca8e.3+11b0ca8",
18
- "@dedot/runtime-specs": "0.9.5-next.11b0ca8e.3+11b0ca8",
19
- "@dedot/shape": "0.9.5-next.11b0ca8e.3+11b0ca8",
20
- "@dedot/storage": "0.9.5-next.11b0ca8e.3+11b0ca8",
21
- "@dedot/types": "0.9.5-next.11b0ca8e.3+11b0ca8",
22
- "@dedot/utils": "0.9.5-next.11b0ca8e.3+11b0ca8"
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": "11b0ca8e2df2c59e12259dc2ba0c61ec91e181cf",
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
- pullQueue.enqueue(() => pull(block)).catch(console.error);
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();