@crawlee/core 4.0.0-beta.8 → 4.0.0-beta.80
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.
- package/README.md +17 -13
- package/autoscaling/autoscaled_pool.d.ts +5 -6
- package/autoscaling/autoscaled_pool.js +12 -11
- package/autoscaling/client_load_signal.d.ts +25 -0
- package/autoscaling/client_load_signal.js +36 -0
- package/autoscaling/cpu_load_signal.d.ts +28 -0
- package/autoscaling/cpu_load_signal.js +24 -0
- package/autoscaling/event_loop_load_signal.d.ts +23 -0
- package/autoscaling/event_loop_load_signal.js +35 -0
- package/autoscaling/index.d.ts +5 -1
- package/autoscaling/index.js +5 -1
- package/autoscaling/load_signal.d.ts +99 -0
- package/autoscaling/load_signal.js +135 -0
- package/autoscaling/memory_load_signal.d.ts +43 -0
- package/autoscaling/memory_load_signal.js +102 -0
- package/autoscaling/snapshotter.d.ts +35 -72
- package/autoscaling/snapshotter.js +78 -181
- package/autoscaling/system_status.d.ts +21 -36
- package/autoscaling/system_status.js +46 -84
- package/configuration.d.ts +88 -228
- package/configuration.js +162 -224
- package/cookie_utils.d.ts +4 -3
- package/cookie_utils.js +20 -13
- package/crawlers/context_pipeline.d.ts +70 -0
- package/crawlers/context_pipeline.js +122 -0
- package/crawlers/crawler_commons.d.ts +20 -30
- package/crawlers/crawler_commons.js +12 -21
- package/crawlers/crawler_utils.d.ts +2 -3
- package/crawlers/crawler_utils.js +1 -2
- package/crawlers/error_snapshotter.d.ts +3 -3
- package/crawlers/error_snapshotter.js +2 -3
- package/crawlers/error_tracker.d.ts +2 -2
- package/crawlers/error_tracker.js +0 -1
- package/crawlers/index.d.ts +1 -2
- package/crawlers/index.js +1 -2
- package/crawlers/internals/types.d.ts +7 -0
- package/crawlers/internals/types.js +1 -0
- package/crawlers/statistics.d.ts +22 -17
- package/crawlers/statistics.js +30 -26
- package/enqueue_links/enqueue_links.d.ts +44 -22
- package/enqueue_links/enqueue_links.js +51 -30
- package/enqueue_links/index.d.ts +0 -1
- package/enqueue_links/index.js +0 -1
- package/enqueue_links/shared.d.ts +25 -9
- package/enqueue_links/shared.js +69 -38
- package/errors.d.ts +53 -4
- package/errors.js +70 -5
- package/events/event_manager.d.ts +34 -8
- package/events/event_manager.js +8 -10
- package/events/index.d.ts +0 -1
- package/events/index.js +0 -1
- package/events/local_event_manager.d.ts +15 -5
- package/events/local_event_manager.js +37 -40
- package/index.d.ts +4 -3
- package/index.js +3 -2
- package/log.d.ts +82 -3
- package/log.js +102 -1
- package/memory-storage/consts.d.ts +4 -0
- package/memory-storage/consts.js +4 -0
- package/memory-storage/index.d.ts +1 -0
- package/memory-storage/index.js +1 -0
- package/memory-storage/memory-storage.d.ts +46 -0
- package/memory-storage/memory-storage.js +136 -0
- package/memory-storage/resource-clients/common/base-client.d.ts +4 -0
- package/memory-storage/resource-clients/common/base-client.js +6 -0
- package/memory-storage/resource-clients/dataset.d.ts +40 -0
- package/memory-storage/resource-clients/dataset.js +113 -0
- package/memory-storage/resource-clients/key-value-store.d.ts +63 -0
- package/memory-storage/resource-clients/key-value-store.js +203 -0
- package/memory-storage/resource-clients/request-queue.d.ts +96 -0
- package/memory-storage/resource-clients/request-queue.js +421 -0
- package/memory-storage/utils.d.ts +17 -0
- package/memory-storage/utils.js +42 -0
- package/package.json +12 -10
- package/proxy_configuration.d.ts +14 -149
- package/proxy_configuration.js +19 -168
- package/recoverable_state.d.ts +120 -0
- package/recoverable_state.js +141 -0
- package/request.d.ts +84 -13
- package/request.js +107 -28
- package/router.d.ts +117 -15
- package/router.js +124 -30
- package/serialization.d.ts +0 -1
- package/serialization.js +1 -2
- package/service_locator.d.ts +156 -0
- package/service_locator.js +238 -0
- package/session_pool/consts.d.ts +1 -2
- package/session_pool/consts.js +1 -2
- package/session_pool/errors.d.ts +0 -1
- package/session_pool/errors.js +0 -1
- package/session_pool/fingerprint.d.ts +9 -0
- package/session_pool/fingerprint.js +30 -0
- package/session_pool/index.d.ts +0 -2
- package/session_pool/index.js +0 -2
- package/session_pool/session.d.ts +35 -73
- package/session_pool/session.js +46 -99
- package/session_pool/session_pool.d.ts +71 -72
- package/session_pool/session_pool.js +111 -100
- package/storages/access_checking.d.ts +0 -1
- package/storages/access_checking.js +5 -2
- package/storages/dataset.d.ts +101 -51
- package/storages/dataset.js +168 -126
- package/storages/index.d.ts +8 -7
- package/storages/index.js +6 -7
- package/storages/key_value_store.d.ts +165 -36
- package/storages/key_value_store.js +269 -122
- package/storages/key_value_store_codec.d.ts +32 -0
- package/storages/key_value_store_codec.js +113 -0
- package/storages/request_dedup_cache.d.ts +23 -0
- package/storages/request_dedup_cache.js +48 -0
- package/storages/request_list.d.ts +38 -82
- package/storages/request_list.js +65 -41
- package/storages/request_loader.d.ts +96 -0
- package/storages/request_loader.js +1 -0
- package/storages/request_manager.d.ts +33 -0
- package/storages/request_manager.js +1 -0
- package/storages/request_manager_tandem.d.ts +106 -0
- package/storages/request_manager_tandem.js +197 -0
- package/storages/request_queue.d.ts +303 -46
- package/storages/request_queue.js +629 -214
- package/storages/{sitemap_request_list.d.ts → sitemap_request_loader.d.ts} +28 -24
- package/storages/{sitemap_request_list.js → sitemap_request_loader.js} +49 -46
- package/storages/storage_instance_manager.d.ts +91 -0
- package/storages/storage_instance_manager.js +258 -0
- package/storages/storage_stats.d.ts +48 -0
- package/storages/storage_stats.js +29 -0
- package/storages/utils.d.ts +51 -6
- package/storages/utils.js +60 -9
- package/system-info/cpu-info.d.ts +67 -0
- package/system-info/cpu-info.js +216 -0
- package/system-info/memory-info.d.ts +31 -0
- package/system-info/memory-info.js +115 -0
- package/system-info/ps-tree.d.ts +17 -0
- package/system-info/ps-tree.js +144 -0
- package/system-info/runtime.d.ts +14 -0
- package/system-info/runtime.js +80 -0
- package/typedefs.d.ts +1 -2
- package/typedefs.js +0 -1
- package/validators.d.ts +8 -1
- package/validators.js +8 -1
- package/autoscaling/autoscaled_pool.d.ts.map +0 -1
- package/autoscaling/autoscaled_pool.js.map +0 -1
- package/autoscaling/index.d.ts.map +0 -1
- package/autoscaling/index.js.map +0 -1
- package/autoscaling/snapshotter.d.ts.map +0 -1
- package/autoscaling/snapshotter.js.map +0 -1
- package/autoscaling/system_status.d.ts.map +0 -1
- package/autoscaling/system_status.js.map +0 -1
- package/configuration.d.ts.map +0 -1
- package/configuration.js.map +0 -1
- package/cookie_utils.d.ts.map +0 -1
- package/cookie_utils.js.map +0 -1
- package/crawlers/crawler_commons.d.ts.map +0 -1
- package/crawlers/crawler_commons.js.map +0 -1
- package/crawlers/crawler_extension.d.ts +0 -12
- package/crawlers/crawler_extension.d.ts.map +0 -1
- package/crawlers/crawler_extension.js +0 -14
- package/crawlers/crawler_extension.js.map +0 -1
- package/crawlers/crawler_utils.d.ts.map +0 -1
- package/crawlers/crawler_utils.js.map +0 -1
- package/crawlers/error_snapshotter.d.ts.map +0 -1
- package/crawlers/error_snapshotter.js.map +0 -1
- package/crawlers/error_tracker.d.ts.map +0 -1
- package/crawlers/error_tracker.js.map +0 -1
- package/crawlers/index.d.ts.map +0 -1
- package/crawlers/index.js.map +0 -1
- package/crawlers/statistics.d.ts.map +0 -1
- package/crawlers/statistics.js.map +0 -1
- package/enqueue_links/enqueue_links.d.ts.map +0 -1
- package/enqueue_links/enqueue_links.js.map +0 -1
- package/enqueue_links/index.d.ts.map +0 -1
- package/enqueue_links/index.js.map +0 -1
- package/enqueue_links/shared.d.ts.map +0 -1
- package/enqueue_links/shared.js.map +0 -1
- package/errors.d.ts.map +0 -1
- package/errors.js.map +0 -1
- package/events/event_manager.d.ts.map +0 -1
- package/events/event_manager.js.map +0 -1
- package/events/index.d.ts.map +0 -1
- package/events/index.js.map +0 -1
- package/events/local_event_manager.d.ts.map +0 -1
- package/events/local_event_manager.js.map +0 -1
- package/http_clients/base-http-client.d.ts +0 -134
- package/http_clients/base-http-client.d.ts.map +0 -1
- package/http_clients/base-http-client.js +0 -33
- package/http_clients/base-http-client.js.map +0 -1
- package/http_clients/form-data-like.d.ts +0 -67
- package/http_clients/form-data-like.d.ts.map +0 -1
- package/http_clients/form-data-like.js +0 -5
- package/http_clients/form-data-like.js.map +0 -1
- package/http_clients/got-scraping-http-client.d.ts +0 -15
- package/http_clients/got-scraping-http-client.d.ts.map +0 -1
- package/http_clients/got-scraping-http-client.js +0 -69
- package/http_clients/got-scraping-http-client.js.map +0 -1
- package/http_clients/index.d.ts +0 -3
- package/http_clients/index.d.ts.map +0 -1
- package/http_clients/index.js +0 -3
- package/http_clients/index.js.map +0 -1
- package/index.d.ts.map +0 -1
- package/index.js.map +0 -1
- package/log.d.ts.map +0 -1
- package/log.js.map +0 -1
- package/proxy_configuration.d.ts.map +0 -1
- package/proxy_configuration.js.map +0 -1
- package/request.d.ts.map +0 -1
- package/request.js.map +0 -1
- package/router.d.ts.map +0 -1
- package/router.js.map +0 -1
- package/serialization.d.ts.map +0 -1
- package/serialization.js.map +0 -1
- package/session_pool/consts.d.ts.map +0 -1
- package/session_pool/consts.js.map +0 -1
- package/session_pool/errors.d.ts.map +0 -1
- package/session_pool/errors.js.map +0 -1
- package/session_pool/events.d.ts +0 -3
- package/session_pool/events.d.ts.map +0 -1
- package/session_pool/events.js +0 -3
- package/session_pool/events.js.map +0 -1
- package/session_pool/index.d.ts.map +0 -1
- package/session_pool/index.js.map +0 -1
- package/session_pool/session.d.ts.map +0 -1
- package/session_pool/session.js.map +0 -1
- package/session_pool/session_pool.d.ts.map +0 -1
- package/session_pool/session_pool.js.map +0 -1
- package/storages/access_checking.d.ts.map +0 -1
- package/storages/access_checking.js.map +0 -1
- package/storages/dataset.d.ts.map +0 -1
- package/storages/dataset.js.map +0 -1
- package/storages/index.d.ts.map +0 -1
- package/storages/index.js.map +0 -1
- package/storages/key_value_store.d.ts.map +0 -1
- package/storages/key_value_store.js.map +0 -1
- package/storages/request_list.d.ts.map +0 -1
- package/storages/request_list.js.map +0 -1
- package/storages/request_provider.d.ts +0 -307
- package/storages/request_provider.d.ts.map +0 -1
- package/storages/request_provider.js +0 -555
- package/storages/request_provider.js.map +0 -1
- package/storages/request_queue.d.ts.map +0 -1
- package/storages/request_queue.js.map +0 -1
- package/storages/request_queue_v2.d.ts +0 -87
- package/storages/request_queue_v2.d.ts.map +0 -1
- package/storages/request_queue_v2.js +0 -438
- package/storages/request_queue_v2.js.map +0 -1
- package/storages/sitemap_request_list.d.ts.map +0 -1
- package/storages/sitemap_request_list.js.map +0 -1
- package/storages/storage_manager.d.ts +0 -58
- package/storages/storage_manager.d.ts.map +0 -1
- package/storages/storage_manager.js +0 -105
- package/storages/storage_manager.js.map +0 -1
- package/storages/utils.d.ts.map +0 -1
- package/storages/utils.js.map +0 -1
- package/tsconfig.build.tsbuildinfo +0 -1
- package/typedefs.d.ts.map +0 -1
- package/typedefs.js.map +0 -1
- package/validators.d.ts.map +0 -1
- package/validators.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
<h1 align="center">
|
|
2
2
|
<a href="https://crawlee.dev">
|
|
3
3
|
<picture>
|
|
4
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/apify/crawlee/master/website/static/img/crawlee-dark.svg?sanitize=true"
|
|
5
|
-
<img alt="Crawlee" src="https://raw.githubusercontent.com/apify/crawlee/master/website/static/img/crawlee-light.svg?sanitize=true" width="500"
|
|
4
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/apify/crawlee/master/website/static/img/crawlee-dark.svg?sanitize=true" />
|
|
5
|
+
<img alt="Crawlee" src="https://raw.githubusercontent.com/apify/crawlee/master/website/static/img/crawlee-light.svg?sanitize=true" width="500" />
|
|
6
6
|
</picture>
|
|
7
7
|
</a>
|
|
8
|
-
<br
|
|
8
|
+
<br />
|
|
9
9
|
<small>A web scraping and browser automation library</small>
|
|
10
10
|
</h1>
|
|
11
11
|
|
|
12
|
-
<p align=center>
|
|
13
|
-
<a href="https://
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
<p align="center">
|
|
13
|
+
<a href="https://trendshift.io/repositories/5179" target="_blank"><img src="https://trendshift.io/api/badge/repositories/5179" alt="apify%2Fcrawlee | Trendshift" width="250" height="55"/></a>
|
|
14
|
+
</p>
|
|
15
|
+
|
|
16
|
+
<p align="center">
|
|
17
|
+
<a href="https://www.npmjs.com/package/@crawlee/core" rel="nofollow"><img src="https://img.shields.io/npm/v/@crawlee/core.svg" alt="NPM latest version" data-canonical-src="https://img.shields.io/npm/v/@crawlee/core/next.svg" /></a>
|
|
18
|
+
<a href="https://www.npmjs.com/package/@crawlee/core" rel="nofollow"><img src="https://img.shields.io/npm/dm/@crawlee/core.svg" alt="Downloads" data-canonical-src="https://img.shields.io/npm/dm/@crawlee/core.svg" /></a>
|
|
19
|
+
<a href="https://discord.gg/jyEM2PRvMU" rel="nofollow"><img src="https://img.shields.io/discord/801163717915574323?label=discord" alt="Chat on discord" data-canonical-src="https://img.shields.io/discord/801163717915574323?label=discord" /></a>
|
|
20
|
+
<a href="https://github.com/apify/crawlee/actions/workflows/test-ci.yml"><img src="https://github.com/apify/crawlee/actions/workflows/test-ci.yml/badge.svg?branch=master" alt="Build Status" /></a>
|
|
17
21
|
</p>
|
|
18
22
|
|
|
19
23
|
Crawlee covers your crawling and scraping end-to-end and **helps you build reliable scrapers. Fast.**
|
|
@@ -24,7 +28,7 @@ Crawlee is available as the [`crawlee`](https://www.npmjs.com/package/crawlee) N
|
|
|
24
28
|
|
|
25
29
|
> 👉 **View full documentation, guides and examples on the [Crawlee project website](https://crawlee.dev)** 👈
|
|
26
30
|
|
|
27
|
-
>
|
|
31
|
+
> Do you prefer 🐍 Python instead of JavaScript? [👉 Checkout Crawlee for Python 👈](https://github.com/apify/crawlee-python).
|
|
28
32
|
|
|
29
33
|
## Installation
|
|
30
34
|
|
|
@@ -85,7 +89,7 @@ By default, Crawlee stores data to `./storage` in the current working directory.
|
|
|
85
89
|
We provide automated beta builds for every merged code change in Crawlee. You can find them in the npm [list of releases](https://www.npmjs.com/package/crawlee?activeTab=versions). If you want to test new features or bug fixes before we release them, feel free to install a beta build like this:
|
|
86
90
|
|
|
87
91
|
```bash
|
|
88
|
-
npm install crawlee@
|
|
92
|
+
npm install crawlee@next
|
|
89
93
|
```
|
|
90
94
|
|
|
91
95
|
If you also use the [Apify SDK](https://github.com/apify/apify-sdk-js), you need to specify dependency overrides in your `package.json` file so that you don't end up with multiple versions of Crawlee installed:
|
|
@@ -94,9 +98,9 @@ If you also use the [Apify SDK](https://github.com/apify/apify-sdk-js), you need
|
|
|
94
98
|
{
|
|
95
99
|
"overrides": {
|
|
96
100
|
"apify": {
|
|
97
|
-
"@crawlee/core": "
|
|
98
|
-
"@crawlee/types": "
|
|
99
|
-
"@crawlee/utils": "
|
|
101
|
+
"@crawlee/core": "$crawlee",
|
|
102
|
+
"@crawlee/types": "$crawlee",
|
|
103
|
+
"@crawlee/utils": "$crawlee"
|
|
100
104
|
}
|
|
101
105
|
}
|
|
102
106
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import { Configuration } from '../configuration.js';
|
|
1
|
+
import type { CrawleeLogger } from '../log.js';
|
|
3
2
|
import type { SnapshotterOptions } from './snapshotter.js';
|
|
4
3
|
import type { SystemInfo, SystemStatusOptions } from './system_status.js';
|
|
5
4
|
export interface AutoscaledPoolOptions {
|
|
@@ -99,7 +98,7 @@ export interface AutoscaledPoolOptions {
|
|
|
99
98
|
* By default, this is set to `Infinity`, but you can pass any positive, non-zero integer.
|
|
100
99
|
*/
|
|
101
100
|
maxTasksPerMinute?: number;
|
|
102
|
-
log?:
|
|
101
|
+
log?: CrawleeLogger;
|
|
103
102
|
}
|
|
104
103
|
/**
|
|
105
104
|
* Manages a pool of asynchronous resource-intensive tasks that are executed in parallel.
|
|
@@ -150,7 +149,6 @@ export interface AutoscaledPoolOptions {
|
|
|
150
149
|
* @category Scaling
|
|
151
150
|
*/
|
|
152
151
|
export declare class AutoscaledPool {
|
|
153
|
-
private readonly config;
|
|
154
152
|
private readonly log;
|
|
155
153
|
private readonly desiredConcurrencyRatio;
|
|
156
154
|
private readonly scaleUpStepRatio;
|
|
@@ -172,6 +170,8 @@ export declare class AutoscaledPool {
|
|
|
172
170
|
private resolve;
|
|
173
171
|
private reject;
|
|
174
172
|
private snapshotter;
|
|
173
|
+
/** Additional SystemStatus loadSignals - tracked here for initialization and cleanup */
|
|
174
|
+
private loadSignals;
|
|
175
175
|
private systemStatus;
|
|
176
176
|
private autoscaleInterval;
|
|
177
177
|
private maybeRunInterval;
|
|
@@ -179,7 +179,7 @@ export declare class AutoscaledPool {
|
|
|
179
179
|
private queryingIsFinished;
|
|
180
180
|
private tasksDonePerSecondInterval?;
|
|
181
181
|
private _tasksPerMinute;
|
|
182
|
-
constructor(options: AutoscaledPoolOptions
|
|
182
|
+
constructor(options: AutoscaledPoolOptions);
|
|
183
183
|
/**
|
|
184
184
|
* Gets the minimum number of tasks running in parallel.
|
|
185
185
|
*/
|
|
@@ -297,4 +297,3 @@ export declare class AutoscaledPool {
|
|
|
297
297
|
protected _incrementTasksDonePerSecond(intervalCallback: () => void): void;
|
|
298
298
|
protected get _isOverMaxRequestLimit(): boolean;
|
|
299
299
|
}
|
|
300
|
-
//# sourceMappingURL=autoscaled_pool.d.ts.map
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import ow from 'ow';
|
|
2
2
|
import { addTimeoutToPromise } from '@apify/timeout';
|
|
3
3
|
import { betterClearInterval, betterSetInterval } from '@apify/utilities';
|
|
4
|
-
import { Configuration } from '../configuration.js';
|
|
5
4
|
import { CriticalError } from '../errors.js';
|
|
6
|
-
import {
|
|
5
|
+
import { serviceLocator } from '../service_locator.js';
|
|
7
6
|
import { Snapshotter } from './snapshotter.js';
|
|
8
7
|
import { SystemStatus } from './system_status.js';
|
|
9
8
|
/**
|
|
@@ -55,7 +54,6 @@ import { SystemStatus } from './system_status.js';
|
|
|
55
54
|
* @category Scaling
|
|
56
55
|
*/
|
|
57
56
|
export class AutoscaledPool {
|
|
58
|
-
config;
|
|
59
57
|
log;
|
|
60
58
|
// Configurable properties.
|
|
61
59
|
desiredConcurrencyRatio;
|
|
@@ -79,6 +77,8 @@ export class AutoscaledPool {
|
|
|
79
77
|
resolve = null;
|
|
80
78
|
reject = null;
|
|
81
79
|
snapshotter;
|
|
80
|
+
/** Additional SystemStatus loadSignals - tracked here for initialization and cleanup */
|
|
81
|
+
loadSignals;
|
|
82
82
|
systemStatus;
|
|
83
83
|
autoscaleInterval;
|
|
84
84
|
maybeRunInterval;
|
|
@@ -86,8 +86,7 @@ export class AutoscaledPool {
|
|
|
86
86
|
queryingIsFinished;
|
|
87
87
|
tasksDonePerSecondInterval;
|
|
88
88
|
_tasksPerMinute = Array.from({ length: 60 }, () => 0);
|
|
89
|
-
constructor(options
|
|
90
|
-
this.config = config;
|
|
89
|
+
constructor(options) {
|
|
91
90
|
ow(options, ow.object.exactShape({
|
|
92
91
|
runTaskFunction: ow.function,
|
|
93
92
|
isFinishedFunction: ow.function,
|
|
@@ -107,7 +106,7 @@ export class AutoscaledPool {
|
|
|
107
106
|
log: ow.optional.object,
|
|
108
107
|
maxTasksPerMinute: ow.optional.number.integerOrInfinite.greaterThanOrEqual(1),
|
|
109
108
|
}));
|
|
110
|
-
const { runTaskFunction, isFinishedFunction, isTaskReadyFunction, maxConcurrency = 200, minConcurrency = 1, desiredConcurrency, desiredConcurrencyRatio = 0.9, scaleUpStepRatio = 0.05, scaleDownStepRatio = 0.05, maybeRunIntervalSecs = 0.5, loggingIntervalSecs = 60, taskTimeoutSecs = 0, autoscaleIntervalSecs = 10, systemStatusOptions, snapshotterOptions, log =
|
|
109
|
+
const { runTaskFunction, isFinishedFunction, isTaskReadyFunction, maxConcurrency = 200, minConcurrency = 1, desiredConcurrency, desiredConcurrencyRatio = 0.9, scaleUpStepRatio = 0.05, scaleDownStepRatio = 0.05, maybeRunIntervalSecs = 0.5, loggingIntervalSecs = 60, taskTimeoutSecs = 0, autoscaleIntervalSecs = 10, systemStatusOptions, snapshotterOptions, log = serviceLocator.getLogger(), maxTasksPerMinute = Infinity, } = options;
|
|
111
110
|
this.log = log.child({ prefix: 'AutoscaledPool' });
|
|
112
111
|
// Configurable properties.
|
|
113
112
|
this.desiredConcurrencyRatio = desiredConcurrencyRatio;
|
|
@@ -137,11 +136,9 @@ export class AutoscaledPool {
|
|
|
137
136
|
ssoCopy.snapshotter ??= new Snapshotter({
|
|
138
137
|
...snapshotterOptions,
|
|
139
138
|
log: this.log,
|
|
140
|
-
config: this.config,
|
|
141
|
-
client: this.config.getStorageClient(),
|
|
142
139
|
});
|
|
143
|
-
ssoCopy.config ??= this.config;
|
|
144
140
|
this.snapshotter = ssoCopy.snapshotter;
|
|
141
|
+
this.loadSignals = ssoCopy.loadSignals ?? [];
|
|
145
142
|
this.systemStatus = new SystemStatus(ssoCopy);
|
|
146
143
|
}
|
|
147
144
|
/**
|
|
@@ -204,6 +201,7 @@ export class AutoscaledPool {
|
|
|
204
201
|
this.reject = reject;
|
|
205
202
|
});
|
|
206
203
|
await this.snapshotter.start();
|
|
204
|
+
await Promise.all(this.loadSignals.map((s) => s.start()));
|
|
207
205
|
// This interval checks the system status and updates the desired concurrency accordingly.
|
|
208
206
|
this.autoscaleInterval = betterSetInterval(this._autoscale, this.autoscaleIntervalMillis);
|
|
209
207
|
// This is here because if we scale down to let's say 1, then after each promise is finished
|
|
@@ -258,14 +256,17 @@ export class AutoscaledPool {
|
|
|
258
256
|
this.isStopped = true;
|
|
259
257
|
await new Promise((resolve, reject) => {
|
|
260
258
|
let timeout;
|
|
259
|
+
let interval;
|
|
261
260
|
if (timeoutSecs) {
|
|
262
261
|
timeout = setTimeout(() => {
|
|
262
|
+
// Clean up the polling interval to prevent it from leaking on timeout.
|
|
263
|
+
clearInterval(interval);
|
|
263
264
|
const err = new Error("The pool's running tasks did not finish" +
|
|
264
265
|
`in ${timeoutSecs} secs after pool.pause() invocation.`);
|
|
265
266
|
reject(err);
|
|
266
267
|
}, timeoutSecs);
|
|
267
268
|
}
|
|
268
|
-
|
|
269
|
+
interval = setInterval(() => {
|
|
269
270
|
if (this._currentConcurrency <= 0) {
|
|
270
271
|
// Clean up timeout and interval to prevent process hanging.
|
|
271
272
|
if (timeout)
|
|
@@ -517,6 +518,7 @@ export class AutoscaledPool {
|
|
|
517
518
|
betterClearInterval(this.tasksDonePerSecondInterval);
|
|
518
519
|
if (this.snapshotter)
|
|
519
520
|
await this.snapshotter.stop();
|
|
521
|
+
await Promise.all(this.loadSignals.map((s) => s.stop()));
|
|
520
522
|
}
|
|
521
523
|
_incrementTasksDonePerSecond(intervalCallback) {
|
|
522
524
|
this._tasksPerMinute.unshift(0);
|
|
@@ -530,4 +532,3 @@ export class AutoscaledPool {
|
|
|
530
532
|
return this._tasksPerMinute.reduce((acc, curr) => acc + curr, 0) >= this.maxTasksPerMinute;
|
|
531
533
|
}
|
|
532
534
|
}
|
|
533
|
-
//# sourceMappingURL=autoscaled_pool.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { StorageBackend } from '@crawlee/types';
|
|
2
|
+
import type { LoadSnapshot } from './load_signal.js';
|
|
3
|
+
import { SnapshotStore } from './load_signal.js';
|
|
4
|
+
export interface ClientSnapshot extends LoadSnapshot {
|
|
5
|
+
rateLimitErrorCount: number;
|
|
6
|
+
}
|
|
7
|
+
export interface ClientLoadSignalOptions {
|
|
8
|
+
client: StorageBackend;
|
|
9
|
+
clientSnapshotIntervalSecs?: number;
|
|
10
|
+
maxClientErrors?: number;
|
|
11
|
+
overloadedRatio?: number;
|
|
12
|
+
snapshotHistoryMillis?: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Periodically checks the storage client for rate-limit errors (HTTP 429)
|
|
16
|
+
* and reports overload when the error delta exceeds a threshold.
|
|
17
|
+
*/
|
|
18
|
+
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
19
|
+
export declare function createClientLoadSignal(options: ClientLoadSignalOptions): Omit<import("./load_signal.js").LoadSignal, "getSample"> & {
|
|
20
|
+
store: SnapshotStore<ClientSnapshot>;
|
|
21
|
+
handle: (cb: () => unknown) => void;
|
|
22
|
+
getSample(sampleDurationMillis?: number): ClientSnapshot[];
|
|
23
|
+
};
|
|
24
|
+
/** @internal Return type for backward compat in Snapshotter facade */
|
|
25
|
+
export type ClientLoadSignal = ReturnType<typeof createClientLoadSignal>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { SnapshotStore } from './load_signal.js';
|
|
2
|
+
const CLIENT_RATE_LIMIT_ERROR_RETRY_COUNT = 2;
|
|
3
|
+
/**
|
|
4
|
+
* Periodically checks the storage client for rate-limit errors (HTTP 429)
|
|
5
|
+
* and reports overload when the error delta exceeds a threshold.
|
|
6
|
+
*/
|
|
7
|
+
export function createClientLoadSignal(options) {
|
|
8
|
+
const maxClientErrors = options.maxClientErrors ?? 3;
|
|
9
|
+
const signal = SnapshotStore.fromInterval({
|
|
10
|
+
name: 'clientInfo',
|
|
11
|
+
overloadedRatio: options.overloadedRatio ?? 0.3,
|
|
12
|
+
intervalMillis: (options.clientSnapshotIntervalSecs ?? 1) * 1000,
|
|
13
|
+
snapshotHistoryMillis: options.snapshotHistoryMillis,
|
|
14
|
+
handler(store, intervalCallback) {
|
|
15
|
+
const now = new Date();
|
|
16
|
+
const allErrorCounts = options.client.stats?.rateLimitErrors ?? [];
|
|
17
|
+
const currentErrCount = allErrorCounts[CLIENT_RATE_LIMIT_ERROR_RETRY_COUNT] || 0;
|
|
18
|
+
const snapshot = {
|
|
19
|
+
createdAt: now,
|
|
20
|
+
isOverloaded: false,
|
|
21
|
+
rateLimitErrorCount: currentErrCount,
|
|
22
|
+
};
|
|
23
|
+
const all = store.getAll();
|
|
24
|
+
const previousSnapshot = all[all.length - 1];
|
|
25
|
+
if (previousSnapshot) {
|
|
26
|
+
const { rateLimitErrorCount } = previousSnapshot;
|
|
27
|
+
const delta = currentErrCount - rateLimitErrorCount;
|
|
28
|
+
if (delta > maxClientErrors)
|
|
29
|
+
snapshot.isOverloaded = true;
|
|
30
|
+
}
|
|
31
|
+
store.push(snapshot, now);
|
|
32
|
+
intervalCallback();
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
return signal;
|
|
36
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Configuration } from '../configuration.js';
|
|
2
|
+
import type { LoadSnapshot } from './load_signal.js';
|
|
3
|
+
import { SnapshotStore } from './load_signal.js';
|
|
4
|
+
import type { SystemInfo } from './system_status.js';
|
|
5
|
+
export interface CpuSnapshot extends LoadSnapshot {
|
|
6
|
+
usedRatio: number;
|
|
7
|
+
ticks?: {
|
|
8
|
+
idle: number;
|
|
9
|
+
total: number;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export interface CpuLoadSignalOptions {
|
|
13
|
+
overloadedRatio?: number;
|
|
14
|
+
snapshotHistoryMillis?: number;
|
|
15
|
+
config: Configuration;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Tracks CPU usage via `SYSTEM_INFO` events and reports overload when
|
|
19
|
+
* the platform or local OS metrics indicate the CPU is overloaded.
|
|
20
|
+
*/
|
|
21
|
+
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
22
|
+
export declare function createCpuLoadSignal(options: CpuLoadSignalOptions): Omit<import("./load_signal.js").LoadSignal, "getSample"> & {
|
|
23
|
+
store: SnapshotStore<CpuSnapshot>;
|
|
24
|
+
handle: (payload: SystemInfo) => void;
|
|
25
|
+
getSample(sampleDurationMillis?: number): CpuSnapshot[];
|
|
26
|
+
};
|
|
27
|
+
/** @internal Return type for backward compat in Snapshotter facade */
|
|
28
|
+
export type CpuLoadSignal = ReturnType<typeof createCpuLoadSignal>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { serviceLocator } from '../service_locator.js';
|
|
2
|
+
import { SnapshotStore } from './load_signal.js';
|
|
3
|
+
/**
|
|
4
|
+
* Tracks CPU usage via `SYSTEM_INFO` events and reports overload when
|
|
5
|
+
* the platform or local OS metrics indicate the CPU is overloaded.
|
|
6
|
+
*/
|
|
7
|
+
export function createCpuLoadSignal(options) {
|
|
8
|
+
return SnapshotStore.fromEvent({
|
|
9
|
+
name: 'cpuInfo',
|
|
10
|
+
overloadedRatio: options.overloadedRatio ?? 0.4,
|
|
11
|
+
events: serviceLocator.getEventManager(),
|
|
12
|
+
event: "systemInfo" /* EventType.SYSTEM_INFO */,
|
|
13
|
+
snapshotHistoryMillis: options.snapshotHistoryMillis,
|
|
14
|
+
handler(store, systemInfo) {
|
|
15
|
+
const { cpuCurrentUsage, isCpuOverloaded } = systemInfo;
|
|
16
|
+
const createdAt = systemInfo.createdAt ? new Date(systemInfo.createdAt) : new Date();
|
|
17
|
+
store.push({
|
|
18
|
+
createdAt,
|
|
19
|
+
isOverloaded: isCpuOverloaded,
|
|
20
|
+
usedRatio: Math.ceil(cpuCurrentUsage / 100),
|
|
21
|
+
}, createdAt);
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { LoadSnapshot } from './load_signal.js';
|
|
2
|
+
import { SnapshotStore } from './load_signal.js';
|
|
3
|
+
export interface EventLoopSnapshot extends LoadSnapshot {
|
|
4
|
+
exceededMillis: number;
|
|
5
|
+
}
|
|
6
|
+
export interface EventLoopLoadSignalOptions {
|
|
7
|
+
eventLoopSnapshotIntervalSecs?: number;
|
|
8
|
+
maxBlockedMillis?: number;
|
|
9
|
+
overloadedRatio?: number;
|
|
10
|
+
snapshotHistoryMillis?: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Periodically measures event loop delay and reports overload when the
|
|
14
|
+
* delay exceeds a configured threshold.
|
|
15
|
+
*/
|
|
16
|
+
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
17
|
+
export declare function createEventLoopLoadSignal(options?: EventLoopLoadSignalOptions): Omit<import("./load_signal.js").LoadSignal, "getSample"> & {
|
|
18
|
+
store: SnapshotStore<EventLoopSnapshot>;
|
|
19
|
+
handle: (cb: () => unknown) => void;
|
|
20
|
+
getSample(sampleDurationMillis?: number): EventLoopSnapshot[];
|
|
21
|
+
};
|
|
22
|
+
/** @internal Return type for backward compat in Snapshotter facade */
|
|
23
|
+
export type EventLoopLoadSignal = ReturnType<typeof createEventLoopLoadSignal>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { SnapshotStore } from './load_signal.js';
|
|
2
|
+
/**
|
|
3
|
+
* Periodically measures event loop delay and reports overload when the
|
|
4
|
+
* delay exceeds a configured threshold.
|
|
5
|
+
*/
|
|
6
|
+
export function createEventLoopLoadSignal(options = {}) {
|
|
7
|
+
const intervalMillis = (options.eventLoopSnapshotIntervalSecs ?? 0.5) * 1000;
|
|
8
|
+
const maxBlockedMillis = options.maxBlockedMillis ?? 50;
|
|
9
|
+
const signal = SnapshotStore.fromInterval({
|
|
10
|
+
name: 'eventLoopInfo',
|
|
11
|
+
overloadedRatio: options.overloadedRatio ?? 0.6,
|
|
12
|
+
intervalMillis,
|
|
13
|
+
snapshotHistoryMillis: options.snapshotHistoryMillis,
|
|
14
|
+
handler(store, intervalCallback) {
|
|
15
|
+
const now = new Date();
|
|
16
|
+
const snapshot = {
|
|
17
|
+
createdAt: now,
|
|
18
|
+
isOverloaded: false,
|
|
19
|
+
exceededMillis: 0,
|
|
20
|
+
};
|
|
21
|
+
const all = store.getAll();
|
|
22
|
+
const previousSnapshot = all[all.length - 1];
|
|
23
|
+
if (previousSnapshot) {
|
|
24
|
+
const { createdAt } = previousSnapshot;
|
|
25
|
+
const delta = now.getTime() - +createdAt - intervalMillis;
|
|
26
|
+
if (delta > maxBlockedMillis)
|
|
27
|
+
snapshot.isOverloaded = true;
|
|
28
|
+
snapshot.exceededMillis = Math.max(delta - maxBlockedMillis, 0);
|
|
29
|
+
}
|
|
30
|
+
store.push(snapshot, now);
|
|
31
|
+
intervalCallback();
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
return signal;
|
|
35
|
+
}
|
package/autoscaling/index.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
export * from './autoscaled_pool.js';
|
|
2
|
+
export * from './client_load_signal.js';
|
|
3
|
+
export * from './cpu_load_signal.js';
|
|
4
|
+
export * from './event_loop_load_signal.js';
|
|
5
|
+
export * from './load_signal.js';
|
|
6
|
+
export * from './memory_load_signal.js';
|
|
2
7
|
export * from './snapshotter.js';
|
|
3
8
|
export * from './system_status.js';
|
|
4
|
-
//# sourceMappingURL=index.d.ts.map
|
package/autoscaling/index.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
export * from './autoscaled_pool.js';
|
|
2
|
+
export * from './client_load_signal.js';
|
|
3
|
+
export * from './cpu_load_signal.js';
|
|
4
|
+
export * from './event_loop_load_signal.js';
|
|
5
|
+
export * from './load_signal.js';
|
|
6
|
+
export * from './memory_load_signal.js';
|
|
2
7
|
export * from './snapshotter.js';
|
|
3
8
|
export * from './system_status.js';
|
|
4
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { EventManager, EventTypeName } from '../events/event_manager.js';
|
|
2
|
+
import type { ClientInfo } from './system_status.js';
|
|
3
|
+
/**
|
|
4
|
+
* A snapshot of a resource's overload state at a point in time.
|
|
5
|
+
*/
|
|
6
|
+
export interface LoadSnapshot {
|
|
7
|
+
createdAt: Date;
|
|
8
|
+
isOverloaded: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* A signal that reports whether a particular resource is overloaded.
|
|
12
|
+
*
|
|
13
|
+
* `SystemStatus` aggregates multiple `LoadSignal` instances to determine
|
|
14
|
+
* overall system health. The built-in signals cover memory, CPU, event loop,
|
|
15
|
+
* and API client rate limits. You can implement this interface to add
|
|
16
|
+
* custom overload signals (e.g. navigation timeouts, proxy health).
|
|
17
|
+
*/
|
|
18
|
+
export interface LoadSignal {
|
|
19
|
+
/** Human-readable name used in logging and `SystemInfo` keys. */
|
|
20
|
+
readonly name: string;
|
|
21
|
+
/**
|
|
22
|
+
* Maximum ratio of overloaded snapshots in a sample before the signal
|
|
23
|
+
* is considered overloaded. For example, `0.2` means the signal fires
|
|
24
|
+
* when more than 20% of the sample window is overloaded.
|
|
25
|
+
*/
|
|
26
|
+
readonly overloadedRatio: number;
|
|
27
|
+
/** Start collecting snapshots. Called when the pool starts. */
|
|
28
|
+
start(): Promise<void>;
|
|
29
|
+
/** Stop collecting snapshots. Called when the pool shuts down. */
|
|
30
|
+
stop(): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Return snapshots for a recent time window (used for "current" status).
|
|
33
|
+
* @param sampleDurationMillis How far back to look, in milliseconds.
|
|
34
|
+
*/
|
|
35
|
+
getSample(sampleDurationMillis?: number): LoadSnapshot[];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* A time-pruning, time-windowed store for `LoadSnapshot` values.
|
|
39
|
+
* Signals compose with this instead of inheriting from a base class.
|
|
40
|
+
*/
|
|
41
|
+
export declare class SnapshotStore<T extends LoadSnapshot = LoadSnapshot> {
|
|
42
|
+
private snapshots;
|
|
43
|
+
private readonly historyMillis;
|
|
44
|
+
constructor(historyMillis?: number);
|
|
45
|
+
/**
|
|
46
|
+
* Add a snapshot and prune entries older than the history window.
|
|
47
|
+
*/
|
|
48
|
+
push(snapshot: T, now?: Date): void;
|
|
49
|
+
/**
|
|
50
|
+
* Return all snapshots, or only those within the given time window.
|
|
51
|
+
*/
|
|
52
|
+
getSample(sampleDurationMillis?: number): T[];
|
|
53
|
+
/**
|
|
54
|
+
* Direct access to the underlying array (for backward-compat getters).
|
|
55
|
+
*/
|
|
56
|
+
getAll(): T[];
|
|
57
|
+
/**
|
|
58
|
+
* Create a `LoadSignal` that snapshots on a `betterSetInterval` tick.
|
|
59
|
+
*
|
|
60
|
+
* The `handler` receives the store (to read previous snapshots) and the
|
|
61
|
+
* interval callback (which it **must** call when done). It should call
|
|
62
|
+
* `store.push()` to record a snapshot.
|
|
63
|
+
*/
|
|
64
|
+
static fromInterval<T extends LoadSnapshot>(options: {
|
|
65
|
+
name: string;
|
|
66
|
+
overloadedRatio: number;
|
|
67
|
+
intervalMillis: number;
|
|
68
|
+
snapshotHistoryMillis?: number;
|
|
69
|
+
handler: (store: SnapshotStore<T>, intervalCallback: () => unknown) => void;
|
|
70
|
+
}): Omit<LoadSignal, 'getSample'> & {
|
|
71
|
+
store: SnapshotStore<T>;
|
|
72
|
+
handle: (cb: () => unknown) => void;
|
|
73
|
+
getSample(sampleDurationMillis?: number): T[];
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Create a `LoadSignal` that snapshots in response to an `EventManager` event.
|
|
77
|
+
*
|
|
78
|
+
* The `handler` receives the event payload and the store. It should call
|
|
79
|
+
* `store.push()` to record a snapshot.
|
|
80
|
+
*/
|
|
81
|
+
static fromEvent<T extends LoadSnapshot, E>(options: {
|
|
82
|
+
name: string;
|
|
83
|
+
overloadedRatio: number;
|
|
84
|
+
events: EventManager;
|
|
85
|
+
event: EventTypeName;
|
|
86
|
+
snapshotHistoryMillis?: number;
|
|
87
|
+
handler: (store: SnapshotStore<T>, payload: E) => void;
|
|
88
|
+
}): Omit<LoadSignal, 'getSample'> & {
|
|
89
|
+
store: SnapshotStore<T>;
|
|
90
|
+
handle: (payload: E) => void;
|
|
91
|
+
getSample(sampleDurationMillis?: number): T[];
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Evaluate whether a sample of `LoadSnapshot` values exceeds the given
|
|
96
|
+
* overloaded ratio, using a time-weighted average. This is the shared
|
|
97
|
+
* evaluation logic used by `SystemStatus` for all signal types.
|
|
98
|
+
*/
|
|
99
|
+
export declare function evaluateLoadSignalSample(sample: LoadSnapshot[], overloadedRatio: number): ClientInfo;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { weightedAvg } from '@crawlee/utils';
|
|
2
|
+
import { betterClearInterval, betterSetInterval } from '@apify/utilities';
|
|
3
|
+
/**
|
|
4
|
+
* A time-pruning, time-windowed store for `LoadSnapshot` values.
|
|
5
|
+
* Signals compose with this instead of inheriting from a base class.
|
|
6
|
+
*/
|
|
7
|
+
export class SnapshotStore {
|
|
8
|
+
snapshots = [];
|
|
9
|
+
historyMillis;
|
|
10
|
+
constructor(historyMillis = 30_000) {
|
|
11
|
+
this.historyMillis = historyMillis;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Add a snapshot and prune entries older than the history window.
|
|
15
|
+
*/
|
|
16
|
+
push(snapshot, now = snapshot.createdAt) {
|
|
17
|
+
// Inline pruning to avoid private-method transpilation issues
|
|
18
|
+
let oldCount = 0;
|
|
19
|
+
for (let i = 0; i < this.snapshots.length; i++) {
|
|
20
|
+
const { createdAt } = this.snapshots[i];
|
|
21
|
+
if (now.getTime() - new Date(createdAt).getTime() > this.historyMillis)
|
|
22
|
+
oldCount++;
|
|
23
|
+
else
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
if (oldCount)
|
|
27
|
+
this.snapshots.splice(0, oldCount);
|
|
28
|
+
this.snapshots.push(snapshot);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Return all snapshots, or only those within the given time window.
|
|
32
|
+
*/
|
|
33
|
+
getSample(sampleDurationMillis) {
|
|
34
|
+
if (!sampleDurationMillis)
|
|
35
|
+
return this.snapshots;
|
|
36
|
+
const sample = [];
|
|
37
|
+
let idx = this.snapshots.length;
|
|
38
|
+
if (!idx)
|
|
39
|
+
return sample;
|
|
40
|
+
const latestTime = this.snapshots[idx - 1].createdAt;
|
|
41
|
+
while (idx--) {
|
|
42
|
+
const snapshot = this.snapshots[idx];
|
|
43
|
+
if (+latestTime - +snapshot.createdAt <= sampleDurationMillis) {
|
|
44
|
+
sample.unshift(snapshot);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return sample;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Direct access to the underlying array (for backward-compat getters).
|
|
54
|
+
*/
|
|
55
|
+
getAll() {
|
|
56
|
+
return this.snapshots;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Create a `LoadSignal` that snapshots on a `betterSetInterval` tick.
|
|
60
|
+
*
|
|
61
|
+
* The `handler` receives the store (to read previous snapshots) and the
|
|
62
|
+
* interval callback (which it **must** call when done). It should call
|
|
63
|
+
* `store.push()` to record a snapshot.
|
|
64
|
+
*/
|
|
65
|
+
static fromInterval(options) {
|
|
66
|
+
const store = new SnapshotStore(options.snapshotHistoryMillis);
|
|
67
|
+
let interval = null;
|
|
68
|
+
const handle = (cb) => options.handler(store, cb);
|
|
69
|
+
return {
|
|
70
|
+
name: options.name,
|
|
71
|
+
overloadedRatio: options.overloadedRatio,
|
|
72
|
+
store,
|
|
73
|
+
handle,
|
|
74
|
+
getSample: (ms) => store.getSample(ms),
|
|
75
|
+
async start() {
|
|
76
|
+
interval = betterSetInterval(handle, options.intervalMillis);
|
|
77
|
+
},
|
|
78
|
+
async stop() {
|
|
79
|
+
betterClearInterval(interval);
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Create a `LoadSignal` that snapshots in response to an `EventManager` event.
|
|
85
|
+
*
|
|
86
|
+
* The `handler` receives the event payload and the store. It should call
|
|
87
|
+
* `store.push()` to record a snapshot.
|
|
88
|
+
*/
|
|
89
|
+
static fromEvent(options) {
|
|
90
|
+
const store = new SnapshotStore(options.snapshotHistoryMillis);
|
|
91
|
+
const handle = (payload) => options.handler(store, payload);
|
|
92
|
+
return {
|
|
93
|
+
name: options.name,
|
|
94
|
+
overloadedRatio: options.overloadedRatio,
|
|
95
|
+
store,
|
|
96
|
+
handle,
|
|
97
|
+
getSample: (ms) => store.getSample(ms),
|
|
98
|
+
async start() {
|
|
99
|
+
options.events.on(options.event, handle);
|
|
100
|
+
},
|
|
101
|
+
async stop() {
|
|
102
|
+
options.events.off(options.event, handle);
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Evaluate whether a sample of `LoadSnapshot` values exceeds the given
|
|
109
|
+
* overloaded ratio, using a time-weighted average. This is the shared
|
|
110
|
+
* evaluation logic used by `SystemStatus` for all signal types.
|
|
111
|
+
*/
|
|
112
|
+
export function evaluateLoadSignalSample(sample, overloadedRatio) {
|
|
113
|
+
if (sample.length === 0) {
|
|
114
|
+
return {
|
|
115
|
+
isOverloaded: false,
|
|
116
|
+
limitRatio: overloadedRatio,
|
|
117
|
+
actualRatio: 0,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
const weights = [];
|
|
121
|
+
const values = [];
|
|
122
|
+
for (let i = 1; i < sample.length; i++) {
|
|
123
|
+
const previous = sample[i - 1];
|
|
124
|
+
const current = sample[i];
|
|
125
|
+
const weight = +current.createdAt - +previous.createdAt;
|
|
126
|
+
weights.push(weight || 1); // Prevent errors from 0ms long intervals (sync) between snapshots.
|
|
127
|
+
values.push(+current.isOverloaded);
|
|
128
|
+
}
|
|
129
|
+
const wAvg = sample.length === 1 ? +sample[0].isOverloaded : weightedAvg(values, weights);
|
|
130
|
+
return {
|
|
131
|
+
isOverloaded: wAvg > overloadedRatio,
|
|
132
|
+
limitRatio: overloadedRatio,
|
|
133
|
+
actualRatio: Math.round(wAvg * 1000) / 1000,
|
|
134
|
+
};
|
|
135
|
+
}
|