@libp2p/utils 5.4.0-1488a7371 → 5.4.0-4ad63bb79

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.
@@ -0,0 +1,35 @@
1
+ import { type ClearableSignal } from 'any-signal';
2
+ import type { Metrics } from '@libp2p/interface';
3
+ export declare const DEFAULT_TIMEOUT_MULTIPLIER = 1.2;
4
+ export declare const DEFAULT_FAILURE_MULTIPLIER = 2;
5
+ export declare const DEFAULT_MIN_TIMEOUT = 2000;
6
+ export interface AdaptiveTimeoutSignal extends ClearableSignal {
7
+ start: number;
8
+ timeout: number;
9
+ }
10
+ export interface AdaptiveTimeoutInit {
11
+ metricName?: string;
12
+ metrics?: Metrics;
13
+ interval?: number;
14
+ initialValue?: number;
15
+ timeoutMultiplier?: number;
16
+ failureMultiplier?: number;
17
+ minTimeout?: number;
18
+ }
19
+ export interface GetTimeoutSignalOptions {
20
+ timeoutFactor?: number;
21
+ signal?: AbortSignal;
22
+ }
23
+ export declare class AdaptiveTimeout {
24
+ private readonly success;
25
+ private readonly failure;
26
+ private readonly next;
27
+ private readonly metric?;
28
+ private readonly timeoutMultiplier;
29
+ private readonly failureMultiplier;
30
+ private readonly minTimeout;
31
+ constructor(init?: AdaptiveTimeoutInit);
32
+ getTimeoutSignal(options?: GetTimeoutSignalOptions): AdaptiveTimeoutSignal;
33
+ cleanUp(signal: AdaptiveTimeoutSignal): void;
34
+ }
35
+ //# sourceMappingURL=adaptive-timeout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adaptive-timeout.d.ts","sourceRoot":"","sources":["../../src/adaptive-timeout.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,KAAK,eAAe,EAAE,MAAM,YAAY,CAAA;AAE5D,OAAO,KAAK,EAAe,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAE7D,eAAO,MAAM,0BAA0B,MAAM,CAAA;AAC7C,eAAO,MAAM,0BAA0B,IAAI,CAAA;AAC3C,eAAO,MAAM,mBAAmB,OAAO,CAAA;AAEvC,MAAM,WAAW,qBAAsB,SAAQ,eAAe;IAC5D,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,mBAAmB;IAClC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,uBAAuB;IACtC,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAe;IACvC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAe;IACvC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAe;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAa;IACrC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAQ;IAC1C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAQ;IAC1C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAQ;gBAEtB,IAAI,GAAE,mBAAwB;IAa3C,gBAAgB,CAAE,OAAO,GAAE,uBAA4B,GAAG,qBAAqB;IAiB/E,OAAO,CAAE,MAAM,EAAE,qBAAqB,GAAG,IAAI;CAyB9C"}
@@ -0,0 +1,63 @@
1
+ import { setMaxListeners } from '@libp2p/interface';
2
+ import { anySignal } from 'any-signal';
3
+ import { MovingAverage } from './moving-average.js';
4
+ export const DEFAULT_TIMEOUT_MULTIPLIER = 1.2;
5
+ export const DEFAULT_FAILURE_MULTIPLIER = 2;
6
+ export const DEFAULT_MIN_TIMEOUT = 2000;
7
+ export class AdaptiveTimeout {
8
+ success;
9
+ failure;
10
+ next;
11
+ metric;
12
+ timeoutMultiplier;
13
+ failureMultiplier;
14
+ minTimeout;
15
+ constructor(init = {}) {
16
+ this.success = new MovingAverage(init.interval ?? 5000);
17
+ this.failure = new MovingAverage(init.interval ?? 5000);
18
+ this.next = new MovingAverage(init.interval ?? 5000);
19
+ this.failureMultiplier = init.failureMultiplier ?? DEFAULT_FAILURE_MULTIPLIER;
20
+ this.timeoutMultiplier = init.timeoutMultiplier ?? DEFAULT_TIMEOUT_MULTIPLIER;
21
+ this.minTimeout = init.minTimeout ?? DEFAULT_MIN_TIMEOUT;
22
+ if (init.metricName != null) {
23
+ this.metric = init.metrics?.registerMetricGroup(init.metricName);
24
+ }
25
+ }
26
+ getTimeoutSignal(options = {}) {
27
+ // calculate timeout for individual peers based on moving average of
28
+ // previous successful requests
29
+ const timeout = Math.max(Math.round(this.next.movingAverage * (options.timeoutFactor ?? this.timeoutMultiplier)), this.minTimeout);
30
+ const sendTimeout = AbortSignal.timeout(timeout);
31
+ const timeoutSignal = anySignal([options.signal, sendTimeout]);
32
+ setMaxListeners(Infinity, timeoutSignal, sendTimeout);
33
+ timeoutSignal.start = Date.now();
34
+ timeoutSignal.timeout = timeout;
35
+ return timeoutSignal;
36
+ }
37
+ cleanUp(signal) {
38
+ const time = Date.now() - signal.start;
39
+ if (signal.aborted) {
40
+ this.failure.push(time);
41
+ this.next.push(time * this.failureMultiplier);
42
+ this.metric?.update({
43
+ failureMovingAverage: this.failure.movingAverage,
44
+ failureDeviation: this.failure.deviation,
45
+ failureForecast: this.failure.forecast,
46
+ failureVariance: this.failure.variance,
47
+ failure: time
48
+ });
49
+ }
50
+ else {
51
+ this.success.push(time);
52
+ this.next.push(time);
53
+ this.metric?.update({
54
+ successMovingAverage: this.success.movingAverage,
55
+ successDeviation: this.success.deviation,
56
+ successForecast: this.success.forecast,
57
+ successVariance: this.success.variance,
58
+ success: time
59
+ });
60
+ }
61
+ }
62
+ }
63
+ //# sourceMappingURL=adaptive-timeout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adaptive-timeout.js","sourceRoot":"","sources":["../../src/adaptive-timeout.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Implements exponential moving average. Ported from `moving-average`.
3
+ *
4
+ * @see https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
5
+ * @see https://www.npmjs.com/package/moving-average
6
+ */
7
+ export declare class MovingAverage {
8
+ movingAverage: number;
9
+ variance: number;
10
+ deviation: number;
11
+ forecast: number;
12
+ private readonly timespan;
13
+ private previousTime?;
14
+ constructor(timespan: number);
15
+ alpha(t: number, pt: number): number;
16
+ push(value: number, time?: number): void;
17
+ }
18
+ //# sourceMappingURL=moving-average.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"moving-average.d.ts","sourceRoot":"","sources":["../../src/moving-average.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,qBAAa,aAAa;IACjB,aAAa,EAAE,MAAM,CAAA;IACrB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAQ;IACjC,OAAO,CAAC,YAAY,CAAC,CAAQ;gBAEhB,QAAQ,EAAE,MAAM;IAQ7B,KAAK,CAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM;IAIrC,IAAI,CAAE,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,MAAmB,GAAG,IAAI;CAkBtD"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Implements exponential moving average. Ported from `moving-average`.
3
+ *
4
+ * @see https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
5
+ * @see https://www.npmjs.com/package/moving-average
6
+ */
7
+ export class MovingAverage {
8
+ movingAverage;
9
+ variance;
10
+ deviation;
11
+ forecast;
12
+ timespan;
13
+ previousTime;
14
+ constructor(timespan) {
15
+ this.timespan = timespan;
16
+ this.movingAverage = 0;
17
+ this.variance = 0;
18
+ this.deviation = 0;
19
+ this.forecast = 0;
20
+ }
21
+ alpha(t, pt) {
22
+ return 1 - (Math.exp(-(t - pt) / this.timespan));
23
+ }
24
+ push(value, time = Date.now()) {
25
+ if (this.previousTime != null) {
26
+ // calculate moving average
27
+ const a = this.alpha(time, this.previousTime);
28
+ const diff = value - this.movingAverage;
29
+ const incr = a * diff;
30
+ this.movingAverage = a * value + (1 - a) * this.movingAverage;
31
+ // calculate variance & deviation
32
+ this.variance = (1 - a) * (this.variance + diff * incr);
33
+ this.deviation = Math.sqrt(this.variance);
34
+ // calculate forecast
35
+ this.forecast = this.movingAverage + a * diff;
36
+ }
37
+ else {
38
+ this.movingAverage = value;
39
+ }
40
+ this.previousTime = time;
41
+ }
42
+ }
43
+ //# sourceMappingURL=moving-average.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"moving-average.js","sourceRoot":"","sources":["../../src/moving-average.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,OAAO,aAAa;IACjB,aAAa,CAAQ;IACrB,QAAQ,CAAQ;IAChB,SAAS,CAAQ;IACjB,QAAQ,CAAQ;IACN,QAAQ,CAAQ;IACzB,YAAY,CAAS;IAE7B,YAAa,QAAgB;QAC3B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAA;QACxB,IAAI,CAAC,aAAa,GAAG,CAAC,CAAA;QACtB,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAA;QACjB,IAAI,CAAC,SAAS,GAAG,CAAC,CAAA;QAClB,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAA;IACnB,CAAC;IAED,KAAK,CAAE,CAAS,EAAE,EAAU;QAC1B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAA;IAClD,CAAC;IAED,IAAI,CAAE,KAAa,EAAE,OAAe,IAAI,CAAC,GAAG,EAAE;QAC5C,IAAI,IAAI,CAAC,YAAY,IAAI,IAAI,EAAE,CAAC;YAC9B,2BAA2B;YAC3B,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,YAAY,CAAC,CAAA;YAC7C,MAAM,IAAI,GAAG,KAAK,GAAG,IAAI,CAAC,aAAa,CAAA;YACvC,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,CAAA;YACrB,IAAI,CAAC,aAAa,GAAG,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,aAAa,CAAA;YAC7D,iCAAiC;YACjC,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,GAAG,IAAI,GAAG,IAAI,CAAC,CAAA;YACvD,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;YACzC,qBAAqB;YACrB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,aAAa,GAAG,CAAC,GAAG,IAAI,CAAA;QAC/C,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,aAAa,GAAG,KAAK,CAAA;QAC5B,CAAC;QAED,IAAI,CAAC,YAAY,GAAG,IAAI,CAAA;IAC1B,CAAC;CACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@libp2p/utils",
3
- "version": "5.4.0-1488a7371",
3
+ "version": "5.4.0-4ad63bb79",
4
4
  "description": "Package to aggregate shared logic and dependencies for the libp2p ecosystem",
5
5
  "license": "Apache-2.0 OR MIT",
6
6
  "homepage": "https://github.com/libp2p/js-libp2p/tree/main/packages/utils#readme",
@@ -56,6 +56,10 @@
56
56
  "types": "./dist/src/address-sort.d.ts",
57
57
  "import": "./dist/src/address-sort.js"
58
58
  },
59
+ "./adaptive-timeout": {
60
+ "types": "./dist/src/adaptive-timeout.d.ts",
61
+ "import": "./dist/src/adaptive-timeout.js"
62
+ },
59
63
  "./array-equals": {
60
64
  "types": "./dist/src/array-equals.d.ts",
61
65
  "import": "./dist/src/array-equals.js"
@@ -80,6 +84,10 @@
80
84
  "types": "./dist/src/is-promise.d.ts",
81
85
  "import": "./dist/src/is-promise.js"
82
86
  },
87
+ "./moving-average": {
88
+ "types": "./dist/src/moving-average.d.ts",
89
+ "import": "./dist/src/moving-average.js"
90
+ },
83
91
  "./multiaddr/is-loopback": {
84
92
  "types": "./dist/src/multiaddr/is-loopback.d.ts",
85
93
  "import": "./dist/src/multiaddr/is-loopback.js"
@@ -140,9 +148,9 @@
140
148
  },
141
149
  "dependencies": {
142
150
  "@chainsafe/is-ip": "^2.0.2",
143
- "@libp2p/crypto": "4.1.1-1488a7371",
144
- "@libp2p/interface": "1.3.1-1488a7371",
145
- "@libp2p/logger": "4.0.12-1488a7371",
151
+ "@libp2p/crypto": "4.1.1-4ad63bb79",
152
+ "@libp2p/interface": "1.3.1-4ad63bb79",
153
+ "@libp2p/logger": "4.0.12-4ad63bb79",
146
154
  "@multiformats/multiaddr": "^12.2.1",
147
155
  "@multiformats/multiaddr-matcher": "^1.2.0",
148
156
  "@sindresorhus/fnv1a": "^3.1.0",
@@ -162,7 +170,7 @@
162
170
  "uint8arrays": "^5.0.3"
163
171
  },
164
172
  "devDependencies": {
165
- "@libp2p/peer-id-factory": "4.1.1-1488a7371",
173
+ "@libp2p/peer-id-factory": "4.1.1-4ad63bb79",
166
174
  "@types/netmask": "^2.0.5",
167
175
  "aegir": "^42.2.5",
168
176
  "delay": "^6.0.0",
@@ -0,0 +1,94 @@
1
+ import { setMaxListeners } from '@libp2p/interface'
2
+ import { anySignal, type ClearableSignal } from 'any-signal'
3
+ import { MovingAverage } from './moving-average.js'
4
+ import type { MetricGroup, Metrics } from '@libp2p/interface'
5
+
6
+ export const DEFAULT_TIMEOUT_MULTIPLIER = 1.2
7
+ export const DEFAULT_FAILURE_MULTIPLIER = 2
8
+ export const DEFAULT_MIN_TIMEOUT = 2000
9
+
10
+ export interface AdaptiveTimeoutSignal extends ClearableSignal {
11
+ start: number
12
+ timeout: number
13
+ }
14
+
15
+ export interface AdaptiveTimeoutInit {
16
+ metricName?: string
17
+ metrics?: Metrics
18
+ interval?: number
19
+ initialValue?: number
20
+ timeoutMultiplier?: number
21
+ failureMultiplier?: number
22
+ minTimeout?: number
23
+ }
24
+
25
+ export interface GetTimeoutSignalOptions {
26
+ timeoutFactor?: number
27
+ signal?: AbortSignal
28
+ }
29
+
30
+ export class AdaptiveTimeout {
31
+ private readonly success: MovingAverage
32
+ private readonly failure: MovingAverage
33
+ private readonly next: MovingAverage
34
+ private readonly metric?: MetricGroup
35
+ private readonly timeoutMultiplier: number
36
+ private readonly failureMultiplier: number
37
+ private readonly minTimeout: number
38
+
39
+ constructor (init: AdaptiveTimeoutInit = {}) {
40
+ this.success = new MovingAverage(init.interval ?? 5000)
41
+ this.failure = new MovingAverage(init.interval ?? 5000)
42
+ this.next = new MovingAverage(init.interval ?? 5000)
43
+ this.failureMultiplier = init.failureMultiplier ?? DEFAULT_FAILURE_MULTIPLIER
44
+ this.timeoutMultiplier = init.timeoutMultiplier ?? DEFAULT_TIMEOUT_MULTIPLIER
45
+ this.minTimeout = init.minTimeout ?? DEFAULT_MIN_TIMEOUT
46
+
47
+ if (init.metricName != null) {
48
+ this.metric = init.metrics?.registerMetricGroup(init.metricName)
49
+ }
50
+ }
51
+
52
+ getTimeoutSignal (options: GetTimeoutSignalOptions = {}): AdaptiveTimeoutSignal {
53
+ // calculate timeout for individual peers based on moving average of
54
+ // previous successful requests
55
+ const timeout = Math.max(
56
+ Math.round(this.next.movingAverage * (options.timeoutFactor ?? this.timeoutMultiplier)),
57
+ this.minTimeout
58
+ )
59
+ const sendTimeout = AbortSignal.timeout(timeout)
60
+ const timeoutSignal = anySignal([options.signal, sendTimeout]) as AdaptiveTimeoutSignal
61
+ setMaxListeners(Infinity, timeoutSignal, sendTimeout)
62
+
63
+ timeoutSignal.start = Date.now()
64
+ timeoutSignal.timeout = timeout
65
+
66
+ return timeoutSignal
67
+ }
68
+
69
+ cleanUp (signal: AdaptiveTimeoutSignal): void {
70
+ const time = Date.now() - signal.start
71
+
72
+ if (signal.aborted) {
73
+ this.failure.push(time)
74
+ this.next.push(time * this.failureMultiplier)
75
+ this.metric?.update({
76
+ failureMovingAverage: this.failure.movingAverage,
77
+ failureDeviation: this.failure.deviation,
78
+ failureForecast: this.failure.forecast,
79
+ failureVariance: this.failure.variance,
80
+ failure: time
81
+ })
82
+ } else {
83
+ this.success.push(time)
84
+ this.next.push(time)
85
+ this.metric?.update({
86
+ successMovingAverage: this.success.movingAverage,
87
+ successDeviation: this.success.deviation,
88
+ successForecast: this.success.forecast,
89
+ successVariance: this.success.variance,
90
+ success: time
91
+ })
92
+ }
93
+ }
94
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Implements exponential moving average. Ported from `moving-average`.
3
+ *
4
+ * @see https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
5
+ * @see https://www.npmjs.com/package/moving-average
6
+ */
7
+ export class MovingAverage {
8
+ public movingAverage: number
9
+ public variance: number
10
+ public deviation: number
11
+ public forecast: number
12
+ private readonly timespan: number
13
+ private previousTime?: number
14
+
15
+ constructor (timespan: number) {
16
+ this.timespan = timespan
17
+ this.movingAverage = 0
18
+ this.variance = 0
19
+ this.deviation = 0
20
+ this.forecast = 0
21
+ }
22
+
23
+ alpha (t: number, pt: number): number {
24
+ return 1 - (Math.exp(-(t - pt) / this.timespan))
25
+ }
26
+
27
+ push (value: number, time: number = Date.now()): void {
28
+ if (this.previousTime != null) {
29
+ // calculate moving average
30
+ const a = this.alpha(time, this.previousTime)
31
+ const diff = value - this.movingAverage
32
+ const incr = a * diff
33
+ this.movingAverage = a * value + (1 - a) * this.movingAverage
34
+ // calculate variance & deviation
35
+ this.variance = (1 - a) * (this.variance + diff * incr)
36
+ this.deviation = Math.sqrt(this.variance)
37
+ // calculate forecast
38
+ this.forecast = this.movingAverage + a * diff
39
+ } else {
40
+ this.movingAverage = value
41
+ }
42
+
43
+ this.previousTime = time
44
+ }
45
+ }