@crawlee/core 4.0.0-beta.98 → 4.0.0-rc.0

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.
Files changed (109) hide show
  1. package/autoscaling/autoscaled_pool.d.ts +3 -21
  2. package/autoscaling/autoscaled_pool.js +105 -97
  3. package/autoscaling/concurrency_system.d.ts +7 -22
  4. package/autoscaling/concurrency_system.js +112 -100
  5. package/autoscaling/cpu_load_signal.d.ts +1 -2
  6. package/autoscaling/cpu_load_signal.js +11 -10
  7. package/autoscaling/event_loop_load_signal.d.ts +1 -4
  8. package/autoscaling/event_loop_load_signal.js +18 -18
  9. package/autoscaling/index.d.ts +1 -1
  10. package/autoscaling/index.js +1 -1
  11. package/autoscaling/load_signal.d.ts +8 -7
  12. package/autoscaling/load_signal.js +14 -12
  13. package/autoscaling/memory_load_signal.d.ts +3 -12
  14. package/autoscaling/memory_load_signal.js +41 -41
  15. package/autoscaling/snapshotter.d.ts +7 -10
  16. package/autoscaling/snapshotter.js +18 -18
  17. package/autoscaling/{client_load_signal.d.ts → storage_backend_load_signal.d.ts} +14 -17
  18. package/autoscaling/{client_load_signal.js → storage_backend_load_signal.js} +27 -27
  19. package/autoscaling/system_status.d.ts +9 -11
  20. package/autoscaling/system_status.js +13 -13
  21. package/configuration.d.ts +16 -16
  22. package/configuration.js +6 -6
  23. package/crawlers/context_pipeline.js +6 -6
  24. package/crawlers/crawler_commons.d.ts +14 -116
  25. package/crawlers/crawler_commons.js +1 -107
  26. package/crawlers/error_snapshotter.d.ts +2 -4
  27. package/crawlers/error_snapshotter.js +5 -5
  28. package/crawlers/index.d.ts +1 -1
  29. package/crawlers/index.js +0 -1
  30. package/crawlers/statistics.d.ts +164 -45
  31. package/crawlers/statistics.js +339 -153
  32. package/debug.js +4 -4
  33. package/enqueue_links/enqueue_links.d.ts +51 -159
  34. package/enqueue_links/enqueue_links.js +38 -231
  35. package/enqueue_links/shared.d.ts +34 -24
  36. package/enqueue_links/shared.js +54 -36
  37. package/errors.d.ts +43 -12
  38. package/errors.js +48 -9
  39. package/events/event_manager.d.ts +2 -2
  40. package/events/event_manager.js +7 -6
  41. package/events/local_event_manager.d.ts +1 -1
  42. package/events/local_event_manager.js +5 -5
  43. package/http.d.ts +9 -0
  44. package/http.js +28 -0
  45. package/index.d.ts +1 -1
  46. package/index.js +1 -1
  47. package/log.js +5 -1
  48. package/memory-storage/memory-storage.d.ts +3 -11
  49. package/memory-storage/memory-storage.js +42 -48
  50. package/memory-storage/resource-clients/dataset.d.ts +1 -1
  51. package/memory-storage/resource-clients/dataset.js +8 -13
  52. package/memory-storage/resource-clients/key-value-store.d.ts +1 -1
  53. package/memory-storage/resource-clients/key-value-store.js +36 -38
  54. package/memory-storage/resource-clients/request-queue.d.ts +4 -23
  55. package/memory-storage/resource-clients/request-queue.js +68 -80
  56. package/owned_or_injected.d.ts +1 -3
  57. package/owned_or_injected.js +17 -17
  58. package/package.json +8 -9
  59. package/proxy_configuration.d.ts +1 -3
  60. package/proxy_configuration.js +18 -14
  61. package/recoverable_state.d.ts +80 -45
  62. package/recoverable_state.js +163 -75
  63. package/request.d.ts +3 -4
  64. package/request.js +57 -44
  65. package/router.d.ts +6 -10
  66. package/router.js +23 -23
  67. package/serialization.js +14 -13
  68. package/service_locator.d.ts +19 -10
  69. package/service_locator.js +57 -48
  70. package/session_pool/session.d.ts +3 -14
  71. package/session_pool/session.js +76 -74
  72. package/session_pool/session_pool.d.ts +6 -12
  73. package/session_pool/session_pool.js +85 -77
  74. package/storages/batched_adds.d.ts +37 -0
  75. package/storages/batched_adds.js +73 -0
  76. package/storages/dataset.d.ts +12 -1
  77. package/storages/dataset.js +133 -32
  78. package/storages/index.d.ts +2 -1
  79. package/storages/index.js +2 -1
  80. package/storages/key_value_store.d.ts +19 -4
  81. package/storages/key_value_store.js +204 -75
  82. package/storages/request_dedup_cache.d.ts +1 -2
  83. package/storages/request_dedup_cache.js +9 -9
  84. package/storages/request_list.d.ts +4 -23
  85. package/storages/request_list.js +103 -96
  86. package/storages/request_loader.d.ts +5 -0
  87. package/storages/request_manager_tandem.d.ts +1 -10
  88. package/storages/request_manager_tandem.js +27 -27
  89. package/storages/request_queue.d.ts +21 -21
  90. package/storages/request_queue.js +318 -177
  91. package/storages/sitemap_request_loader.d.ts +22 -70
  92. package/storages/sitemap_request_loader.js +122 -116
  93. package/storages/storage_instance_manager.d.ts +1 -4
  94. package/storages/storage_instance_manager.js +17 -19
  95. package/storages/storage_stats.d.ts +1 -1
  96. package/storages/storage_stats.js +4 -4
  97. package/storages/throttling_request_manager.d.ts +239 -0
  98. package/storages/throttling_request_manager.js +646 -0
  99. package/storages/transaction.d.ts +252 -0
  100. package/storages/transaction.js +251 -0
  101. package/storages/utils.d.ts +6 -3
  102. package/storages/utils.js +11 -2
  103. package/system-info/runtime.js +7 -7
  104. package/url.d.ts +9 -0
  105. package/url.js +11 -0
  106. package/validators.d.ts +22 -25
  107. package/validators.js +13 -25
  108. package/storages/access_checking.d.ts +0 -12
  109. package/storages/access_checking.js +0 -17
@@ -4,8 +4,9 @@ import { weightedAvg } from './weighted_avg.js';
4
4
  * and so can yours — it is the only part of their machinery worth reusing.
5
5
  */
6
6
  export class SnapshotStore {
7
- snapshots = [];
7
+ #snapshots = [];
8
8
  /** Retention window in milliseconds. Unbounded until {@link SnapshotStore.useSampleWindow|`useSampleWindow()`}. */
9
+ // kept as TS-private: concurrency_system tests read this retention window directly
9
10
  historyMillis = Infinity;
10
11
  /**
11
12
  * Sizes retention to the window the signal will be sampled over, as handed to it in
@@ -21,30 +22,30 @@ export class SnapshotStore {
21
22
  push(snapshot, now = snapshot.createdAt) {
22
23
  // Inline pruning to avoid private-method transpilation issues
23
24
  let oldCount = 0;
24
- for (let i = 0; i < this.snapshots.length; i++) {
25
- const { createdAt } = this.snapshots[i];
25
+ for (let i = 0; i < this.#snapshots.length; i++) {
26
+ const { createdAt } = this.#snapshots[i];
26
27
  if (now.getTime() - new Date(createdAt).getTime() > this.historyMillis)
27
28
  oldCount++;
28
29
  else
29
30
  break;
30
31
  }
31
32
  if (oldCount)
32
- this.snapshots.splice(0, oldCount);
33
- this.snapshots.push(snapshot);
33
+ this.#snapshots.splice(0, oldCount);
34
+ this.#snapshots.push(snapshot);
34
35
  }
35
36
  /**
36
37
  * Return all snapshots, or only those within the given time window.
37
38
  */
38
39
  getSample(sampleDurationMillis) {
39
40
  if (!sampleDurationMillis)
40
- return this.snapshots;
41
+ return this.#snapshots;
41
42
  const sample = [];
42
- let idx = this.snapshots.length;
43
+ let idx = this.#snapshots.length;
43
44
  if (!idx)
44
45
  return sample;
45
- const latestTime = this.snapshots[idx - 1].createdAt;
46
+ const latestTime = this.#snapshots[idx - 1].createdAt;
46
47
  while (idx--) {
47
- const snapshot = this.snapshots[idx];
48
+ const snapshot = this.#snapshots[idx];
48
49
  if (+latestTime - +snapshot.createdAt <= sampleDurationMillis) {
49
50
  sample.unshift(snapshot);
50
51
  }
@@ -56,10 +57,11 @@ export class SnapshotStore {
56
57
  }
57
58
  /**
58
59
  * Direct, unwindowed access to the underlying array — used by signals whose handler needs the previous snapshot
59
- * to compute a delta (e.g. the event loop and client signals read the last entry to measure change since it).
60
+ * to compute a delta (e.g. the event loop and storage backend signals read the last entry to measure change since
61
+ * it).
60
62
  */
61
63
  getAll() {
62
- return this.snapshots;
64
+ return this.#snapshots;
63
65
  }
64
66
  /**
65
67
  * Discards every retained snapshot. The built-in signals do this when they *start*, so that a session neither
@@ -68,7 +70,7 @@ export class SnapshotStore {
68
70
  * rather than on stop leaves a finished session readable.
69
71
  */
70
72
  clear() {
71
- this.snapshots = [];
73
+ this.#snapshots = [];
72
74
  }
73
75
  }
74
76
  /**
@@ -33,24 +33,15 @@ export interface MemoryLoadSignalOptions {
33
33
  * @category Scaling
34
34
  */
35
35
  export declare class MemoryLoadSignal implements LoadSignal {
36
+ #private;
36
37
  readonly name = "memInfo";
37
38
  readonly overloadedRatio: number;
38
- private readonly store;
39
- private readonly maxUsedRatio;
40
- /** All resolved in `start()`, before anything that reads them can fire. */
41
- private config;
42
- private log;
43
- private maxMemoryBytes;
44
- private events?;
45
- private maxMemoryRatio;
46
- private lastLoggedCriticalMemoryOverloadAt;
47
39
  constructor(options?: MemoryLoadSignalOptions);
48
40
  start(context: LoadSignalStartContext): Promise<void>;
49
41
  stop(): Promise<void>;
50
42
  getSample(sampleDurationMillis?: number): LoadSnapshot[];
51
43
  /** @internal Records a snapshot from a `SYSTEM_INFO` payload. Exposed for tests. */
52
44
  handle(systemInfo: SystemInfo): void;
53
- /** @internal */
54
- _memoryOverloadWarning(systemInfo: SystemInfo, maxMemoryBytes?: number): void;
55
- private _getTotalMemoryBytes;
45
+ private memoryOverloadWarning;
46
+ private getTotalMemoryBytes;
56
47
  }
@@ -1,3 +1,4 @@
1
+ import { EventType } from '../events/event_manager.js';
1
2
  import { serviceLocator } from '../service_locator.js';
2
3
  import { getMemoryInfo } from '../system-info/memory-info.js';
3
4
  import { isContainerized } from '../system-info/runtime.js';
@@ -15,92 +16,91 @@ const CRITICAL_OVERLOAD_RATE_LIMIT_MILLIS = 10_000;
15
16
  export class MemoryLoadSignal {
16
17
  name = 'memInfo';
17
18
  overloadedRatio;
18
- store = new SnapshotStore();
19
- maxUsedRatio;
19
+ #store = new SnapshotStore();
20
+ #maxUsedRatio;
20
21
  /** All resolved in `start()`, before anything that reads them can fire. */
21
- config;
22
- log;
23
- maxMemoryBytes;
24
- events;
25
- maxMemoryRatio;
26
- lastLoggedCriticalMemoryOverloadAt = null;
22
+ #config;
23
+ #log;
24
+ #maxMemoryBytes;
25
+ #events;
26
+ #maxMemoryRatio;
27
+ #lastLoggedCriticalMemoryOverloadAt = null;
27
28
  constructor(options = {}) {
28
- this.maxUsedRatio = options.maxUsedRatio ?? 0.9;
29
+ this.#maxUsedRatio = options.maxUsedRatio ?? 0.9;
29
30
  this.overloadedRatio = options.overloadedRatio ?? 0.2;
30
31
  this.handle = this.handle.bind(this);
31
32
  }
32
33
  async start(context) {
33
- this.store.useSampleWindow(context.maxSampleWindowMillis);
34
+ this.#store.useSampleWindow(context.maxSampleWindowMillis);
34
35
  // A new session starts from a clean slate, so it is not judged on measurements from before the downtime.
35
- this.store.clear();
36
+ this.#store.clear();
36
37
  // Resolved here rather than in the constructor: an instance built ahead of time (to be wrapped, or shared
37
38
  // between systems) must not capture whichever services happened to be registered at that moment.
38
- this.config = serviceLocator.getConfiguration();
39
- this.events = serviceLocator.getEventManager();
40
- this.log = serviceLocator.getLogger().child({ prefix: 'MemoryLoadSignal' });
41
- const memoryMbytes = this.config.memoryMbytes ?? 0;
39
+ this.#config = serviceLocator.getConfiguration();
40
+ this.#events = serviceLocator.getEventManager();
41
+ this.#log = serviceLocator.getLogger().child({ prefix: 'MemoryLoadSignal' });
42
+ const memoryMbytes = this.#config.memoryMbytes ?? 0;
42
43
  if (memoryMbytes > 0) {
43
- this.maxMemoryBytes = memoryMbytes * 1024 * 1024;
44
+ this.#maxMemoryBytes = memoryMbytes * 1024 * 1024;
44
45
  }
45
46
  else {
46
- this.maxMemoryRatio = this.config.availableMemoryRatio;
47
- if (!this.maxMemoryRatio) {
47
+ this.#maxMemoryRatio = this.#config.availableMemoryRatio;
48
+ if (!this.#maxMemoryRatio) {
48
49
  throw new Error('availableMemoryRatio is not set in configuration.');
49
50
  }
50
51
  else {
51
- this.log.debug(`Setting max memory of this run to ${this.maxMemoryRatio * 100} % of available memory. ` +
52
+ this.#log.debug(`Setting max memory of this run to ${this.#maxMemoryRatio * 100} % of available memory. ` +
52
53
  'Use the CRAWLEE_MEMORY_MBYTES or CRAWLEE_AVAILABLE_MEMORY_RATIO environment variable to override it.');
53
54
  }
54
55
  // Fallback memory measurement in case memTotalBytes is missing from SystemInfo.
55
- this.maxMemoryBytes = await this._getTotalMemoryBytes();
56
+ this.#maxMemoryBytes = await this.getTotalMemoryBytes();
56
57
  }
57
- this.events.on("systemInfo" /* EventType.SYSTEM_INFO */, this.handle);
58
+ this.#events.on(EventType.SYSTEM_INFO, this.handle);
58
59
  }
59
60
  async stop() {
60
- this.events?.off("systemInfo" /* EventType.SYSTEM_INFO */, this.handle);
61
- this.events = undefined;
61
+ this.#events?.off(EventType.SYSTEM_INFO, this.handle);
62
+ this.#events = undefined;
62
63
  }
63
64
  getSample(sampleDurationMillis) {
64
- return this.store.getSample(sampleDurationMillis);
65
+ return this.#store.getSample(sampleDurationMillis);
65
66
  }
66
67
  /** @internal Records a snapshot from a `SYSTEM_INFO` payload. Exposed for tests. */
67
68
  handle(systemInfo) {
68
69
  const createdAt = systemInfo.createdAt ? new Date(systemInfo.createdAt) : new Date();
69
70
  const { memCurrentBytes, memTotalBytes } = systemInfo;
70
- let maxMemoryBytes = this.maxMemoryBytes;
71
- if (this.maxMemoryRatio !== undefined && this.maxMemoryRatio > 0) {
72
- maxMemoryBytes = this.maxMemoryRatio * (memTotalBytes ?? this.maxMemoryBytes);
71
+ let maxMemoryBytes = this.#maxMemoryBytes;
72
+ if (this.#maxMemoryRatio !== undefined && this.#maxMemoryRatio > 0) {
73
+ maxMemoryBytes = this.#maxMemoryRatio * (memTotalBytes ?? this.#maxMemoryBytes);
73
74
  }
74
75
  const snapshot = {
75
76
  createdAt,
76
- isOverloaded: memCurrentBytes / maxMemoryBytes > this.maxUsedRatio,
77
+ isOverloaded: memCurrentBytes / maxMemoryBytes > this.#maxUsedRatio,
77
78
  usedBytes: memCurrentBytes,
78
79
  };
79
- this.store.push(snapshot, createdAt);
80
- this._memoryOverloadWarning(systemInfo, maxMemoryBytes);
80
+ this.#store.push(snapshot, createdAt);
81
+ this.memoryOverloadWarning(systemInfo, maxMemoryBytes);
81
82
  }
82
- /** @internal */
83
- _memoryOverloadWarning(systemInfo, maxMemoryBytes) {
84
- const effectiveMax = maxMemoryBytes ?? this.maxMemoryBytes;
83
+ memoryOverloadWarning(systemInfo, maxMemoryBytes) {
84
+ const effectiveMax = maxMemoryBytes ?? this.#maxMemoryBytes;
85
85
  const { memCurrentBytes } = systemInfo;
86
86
  const createdAt = systemInfo.createdAt ? new Date(systemInfo.createdAt) : new Date();
87
- if (this.lastLoggedCriticalMemoryOverloadAt &&
88
- +createdAt < +this.lastLoggedCriticalMemoryOverloadAt + CRITICAL_OVERLOAD_RATE_LIMIT_MILLIS)
87
+ if (this.#lastLoggedCriticalMemoryOverloadAt &&
88
+ +createdAt < +this.#lastLoggedCriticalMemoryOverloadAt + CRITICAL_OVERLOAD_RATE_LIMIT_MILLIS)
89
89
  return;
90
- const maxDesiredMemoryBytes = this.maxUsedRatio * effectiveMax;
91
- const reserveMemory = effectiveMax * (1 - this.maxUsedRatio) * RESERVE_MEMORY_RATIO;
90
+ const maxDesiredMemoryBytes = this.#maxUsedRatio * effectiveMax;
91
+ const reserveMemory = effectiveMax * (1 - this.#maxUsedRatio) * RESERVE_MEMORY_RATIO;
92
92
  const criticalOverloadBytes = maxDesiredMemoryBytes + reserveMemory;
93
93
  const isCriticalOverload = memCurrentBytes > criticalOverloadBytes;
94
94
  if (isCriticalOverload) {
95
95
  const usedPercentage = Math.round((memCurrentBytes / effectiveMax) * 100);
96
96
  const toMb = (bytes) => Math.round(bytes / 1024 ** 2);
97
- this.log.warning('Memory is critically overloaded. ' +
97
+ this.#log.warning('Memory is critically overloaded. ' +
98
98
  `Using ${toMb(memCurrentBytes)} MB of ${toMb(effectiveMax)} MB (${usedPercentage}%). Consider increasing available memory.`);
99
- this.lastLoggedCriticalMemoryOverloadAt = createdAt;
99
+ this.#lastLoggedCriticalMemoryOverloadAt = createdAt;
100
100
  }
101
101
  }
102
- async _getTotalMemoryBytes() {
103
- const containerized = this.config.containerized ?? (await isContainerized());
102
+ async getTotalMemoryBytes() {
103
+ const containerized = this.#config.containerized ?? (await isContainerized());
104
104
  return (await getMemoryInfo({ containerized, logger: serviceLocator.getLogger() })).totalBytes;
105
105
  }
106
106
  }
@@ -1,8 +1,8 @@
1
- import type { ClientLoadSignalOptions } from './client_load_signal.js';
2
1
  import type { CpuLoadSignalOptions } from './cpu_load_signal.js';
3
2
  import type { EventLoopLoadSignalOptions } from './event_loop_load_signal.js';
4
3
  import type { LoadSignal, LoadSignalStartContext } from './load_signal.js';
5
4
  import type { MemoryLoadSignalOptions } from './memory_load_signal.js';
5
+ import type { StorageBackendLoadSignalOptions } from './storage_backend_load_signal.js';
6
6
  /**
7
7
  * The load signals a {@link ConcurrencySystem} watches to decide whether the machine is overloaded.
8
8
  *
@@ -31,11 +31,11 @@ export interface LoadSignalsOptions {
31
31
  */
32
32
  cpu?: CpuLoadSignalOptions | false;
33
33
  /**
34
- * Tuning for the built-in {@link ClientLoadSignal} (snapshot interval + error limit + overload ratio), or
35
- * `false` to switch it off — worth doing when the storage backend reports no rate-limit statistics, since the
34
+ * Tuning for the built-in {@link StorageBackendLoadSignal} (snapshot interval + error limit + overload ratio),
35
+ * or `false` to switch it off — worth doing when the storage backend reports no rate-limit statistics, since the
36
36
  * signal otherwise polls it every second to no purpose.
37
37
  */
38
- client?: ClientLoadSignalOptions | false;
38
+ storageBackend?: StorageBackendLoadSignalOptions | false;
39
39
  /**
40
40
  * Additional {@link LoadSignal} implementations — e.g. navigation timeouts or proxy health — evaluated
41
41
  * alongside the built-in four. If any signal reports overload, the system counts as overloaded. Their lifecycle
@@ -53,18 +53,15 @@ export interface LoadSignalsOptions {
53
53
  export type SnapshotterOptions = Omit<LoadSignalsOptions, 'custom'>;
54
54
  /**
55
55
  * Owns the four built-in {@link LoadSignal} instances — {@link MemoryLoadSignal},
56
- * {@link EventLoopLoadSignal}, {@link CpuLoadSignal} and {@link ClientLoadSignal} — constructing the ones
57
- * that were not switched off and driving their shared lifecycle.
56
+ * {@link EventLoopLoadSignal}, {@link CpuLoadSignal} and {@link StorageBackendLoadSignal} — constructing
57
+ * the ones that were not switched off and driving their shared lifecycle.
58
58
  *
59
59
  * Configured indirectly through {@link ConcurrencySystemOptions.loadSignals|`loadSignals`}, whose per-signal bags
60
60
  * are simply forwarded to the corresponding constructor.
61
61
  * @internal
62
62
  */
63
63
  export declare class Snapshotter {
64
- private readonly memorySignal?;
65
- private readonly eventLoopSignal?;
66
- private readonly cpuSignal?;
67
- private readonly clientSignal?;
64
+ #private;
68
65
  /**
69
66
  * Returns the enabled built-in signals, so `SystemStatus` can iterate them alongside any custom `LoadSignal`
70
67
  * instances. Signals switched off through the options are simply absent — the system status reports them as
@@ -1,22 +1,22 @@
1
- import { ClientLoadSignal } from './client_load_signal.js';
2
1
  import { CpuLoadSignal } from './cpu_load_signal.js';
3
2
  import { EventLoopLoadSignal } from './event_loop_load_signal.js';
4
3
  import { MemoryLoadSignal } from './memory_load_signal.js';
4
+ import { StorageBackendLoadSignal } from './storage_backend_load_signal.js';
5
5
  /**
6
6
  * Owns the four built-in {@link LoadSignal} instances — {@link MemoryLoadSignal},
7
- * {@link EventLoopLoadSignal}, {@link CpuLoadSignal} and {@link ClientLoadSignal} — constructing the ones
8
- * that were not switched off and driving their shared lifecycle.
7
+ * {@link EventLoopLoadSignal}, {@link CpuLoadSignal} and {@link StorageBackendLoadSignal} — constructing
8
+ * the ones that were not switched off and driving their shared lifecycle.
9
9
  *
10
10
  * Configured indirectly through {@link ConcurrencySystemOptions.loadSignals|`loadSignals`}, whose per-signal bags
11
11
  * are simply forwarded to the corresponding constructor.
12
12
  * @internal
13
13
  */
14
14
  export class Snapshotter {
15
- // Absent when switched off through the corresponding option (e.g. `client: false`).
16
- memorySignal;
17
- eventLoopSignal;
18
- cpuSignal;
19
- clientSignal;
15
+ // Absent when switched off through the corresponding option (e.g. `storageBackend: false`).
16
+ #memorySignal;
17
+ #eventLoopSignal;
18
+ #cpuSignal;
19
+ #storageBackendSignal;
20
20
  /**
21
21
  * Returns the enabled built-in signals, so `SystemStatus` can iterate them alongside any custom `LoadSignal`
22
22
  * instances. Signals switched off through the options are simply absent — the system status reports them as
@@ -24,10 +24,10 @@ export class Snapshotter {
24
24
  */
25
25
  getLoadSignals() {
26
26
  const builtin = [
27
- this.memorySignal,
28
- this.eventLoopSignal,
29
- this.cpuSignal,
30
- this.clientSignal,
27
+ this.#memorySignal,
28
+ this.#eventLoopSignal,
29
+ this.#cpuSignal,
30
+ this.#storageBackendSignal,
31
31
  ];
32
32
  return builtin.filter((signal) => signal !== undefined);
33
33
  }
@@ -35,17 +35,17 @@ export class Snapshotter {
35
35
  * @param [options] All `Snapshotter` configuration options.
36
36
  */
37
37
  constructor(options = {}) {
38
- const { memory = {}, eventLoop = {}, cpu = {}, client = {} } = options;
38
+ const { memory = {}, eventLoop = {}, cpu = {}, storageBackend = {} } = options;
39
39
  // Each signal resolves its own ambient dependencies when started, and is told the window it will be sampled
40
40
  // over then too - so there is nothing to thread in here beyond the caller's tuning.
41
41
  if (memory !== false)
42
- this.memorySignal = new MemoryLoadSignal(memory);
42
+ this.#memorySignal = new MemoryLoadSignal(memory);
43
43
  if (eventLoop !== false)
44
- this.eventLoopSignal = new EventLoopLoadSignal(eventLoop);
44
+ this.#eventLoopSignal = new EventLoopLoadSignal(eventLoop);
45
45
  if (cpu !== false)
46
- this.cpuSignal = new CpuLoadSignal(cpu);
47
- if (client !== false)
48
- this.clientSignal = new ClientLoadSignal(client);
46
+ this.#cpuSignal = new CpuLoadSignal(cpu);
47
+ if (storageBackend !== false)
48
+ this.#storageBackendSignal = new StorageBackendLoadSignal(storageBackend);
49
49
  }
50
50
  /**
51
51
  * Starts capturing snapshots at configured intervals. The `context` carries the sample window the signals will
@@ -1,18 +1,19 @@
1
1
  import type { LoadSignal, LoadSignalStartContext, LoadSnapshot } from './load_signal.js';
2
2
  /**
3
- * A snapshot produced by the built-in client (rate-limit) signal.
3
+ * A snapshot produced by the built-in storage backend (rate-limit) signal.
4
4
  * @internal
5
5
  */
6
- export interface ClientSnapshot extends LoadSnapshot {
6
+ export interface StorageBackendSnapshot extends LoadSnapshot {
7
7
  rateLimitErrorCount: number;
8
8
  }
9
9
  /**
10
- * Tuning for the built-in **client** (rate-limit) load signal, as accepted both by {@link ClientLoadSignal} and by
11
- * the {@link LoadSignalsOptions.client|`client`} shorthand on {@link LoadSignalsOptions}.
10
+ * Tuning for the built-in **storage backend** (rate-limit) load signal, as accepted both by
11
+ * {@link StorageBackendLoadSignal} and by the
12
+ * {@link LoadSignalsOptions.storageBackend|`storageBackend`} shorthand on {@link LoadSignalsOptions}.
12
13
  */
13
- export interface ClientLoadSignalOptions {
14
+ export interface StorageBackendLoadSignalOptions {
14
15
  /**
15
- * Defines the interval of checking the current state of the remote API client, in seconds.
16
+ * Defines the interval of checking the current state of the storage backend, in seconds.
16
17
  * @default 1
17
18
  */
18
19
  snapshotIntervalSecs?: number;
@@ -22,7 +23,7 @@ export interface ClientLoadSignalOptions {
22
23
  */
23
24
  maxErrors?: number;
24
25
  /**
25
- * Maximum ratio of overloaded snapshots in a sample before the client counts as overloaded.
26
+ * Maximum ratio of overloaded snapshots in a sample before the storage backend counts as overloaded.
26
27
  * @default 0.3
27
28
  */
28
29
  overloadedRatio?: number;
@@ -33,20 +34,16 @@ export interface ClientLoadSignalOptions {
33
34
  *
34
35
  * Built by default; construct one yourself only to wrap or adapt it — see {@link LoadSignal}.
35
36
  *
36
- * Switch it off entirely ({@link LoadSignalsOptions.client|`client: false`}) if the storage backend reports no
37
- * rate-limit statistics, since it otherwise polls it every second to no purpose.
37
+ * Switch it off entirely ({@link LoadSignalsOptions.storageBackend|`storageBackend: false`}) if the storage backend
38
+ * reports no rate-limit statistics, since it otherwise polls it every second to no purpose.
38
39
  *
39
40
  * @category Scaling
40
41
  */
41
- export declare class ClientLoadSignal implements LoadSignal {
42
- readonly name = "clientInfo";
42
+ export declare class StorageBackendLoadSignal implements LoadSignal {
43
+ #private;
44
+ readonly name = "storageBackendInfo";
43
45
  readonly overloadedRatio: number;
44
- private readonly store;
45
- private readonly intervalMillis;
46
- private readonly maxErrors;
47
- private interval?;
48
- private client?;
49
- constructor(options?: ClientLoadSignalOptions);
46
+ constructor(options?: StorageBackendLoadSignalOptions);
50
47
  start(context: LoadSignalStartContext): Promise<void>;
51
48
  stop(): Promise<void>;
52
49
  getSample(sampleDurationMillis?: number): LoadSnapshot[];
@@ -1,50 +1,50 @@
1
1
  import { betterClearInterval, betterSetInterval } from '@apify/utilities';
2
2
  import { serviceLocator } from '../service_locator.js';
3
3
  import { SnapshotStore } from './load_signal.js';
4
- const CLIENT_RATE_LIMIT_ERROR_RETRY_COUNT = 2;
4
+ const RATE_LIMIT_ERROR_RETRY_COUNT = 2;
5
5
  /**
6
6
  * Periodically checks the storage backend for rate-limit errors (HTTP 429) and reports overload when the error delta
7
7
  * exceeds a threshold.
8
8
  *
9
9
  * Built by default; construct one yourself only to wrap or adapt it — see {@link LoadSignal}.
10
10
  *
11
- * Switch it off entirely ({@link LoadSignalsOptions.client|`client: false`}) if the storage backend reports no
12
- * rate-limit statistics, since it otherwise polls it every second to no purpose.
11
+ * Switch it off entirely ({@link LoadSignalsOptions.storageBackend|`storageBackend: false`}) if the storage backend
12
+ * reports no rate-limit statistics, since it otherwise polls it every second to no purpose.
13
13
  *
14
14
  * @category Scaling
15
15
  */
16
- export class ClientLoadSignal {
17
- name = 'clientInfo';
16
+ export class StorageBackendLoadSignal {
17
+ name = 'storageBackendInfo';
18
18
  overloadedRatio;
19
- store = new SnapshotStore();
20
- intervalMillis;
21
- maxErrors;
22
- interval;
23
- client;
19
+ #store = new SnapshotStore();
20
+ #intervalMillis;
21
+ #maxErrors;
22
+ #interval;
23
+ #storageBackend;
24
24
  constructor(options = {}) {
25
25
  this.overloadedRatio = options.overloadedRatio ?? 0.3;
26
- this.intervalMillis = (options.snapshotIntervalSecs ?? 1) * 1000;
27
- this.maxErrors = options.maxErrors ?? 3;
26
+ this.#intervalMillis = (options.snapshotIntervalSecs ?? 1) * 1000;
27
+ this.#maxErrors = options.maxErrors ?? 3;
28
28
  this.handle = this.handle.bind(this);
29
29
  }
30
30
  async start(context) {
31
- this.store.useSampleWindow(context.maxSampleWindowMillis);
31
+ this.#store.useSampleWindow(context.maxSampleWindowMillis);
32
32
  // A new session starts from a clean slate, or its first measurement diffs the error count against the previous
33
- // session's — possibly against a different backend, since the client is resolved afresh just below.
34
- this.store.clear();
33
+ // session's — possibly against a different backend, since it is resolved afresh just below.
34
+ this.#store.clear();
35
35
  // Resolved here rather than in the constructor, where asking for the backend would instantiate a default one
36
36
  // as a side effect - long before the crawler that owns the run has had a chance to register its own.
37
- this.client = serviceLocator.getStorageBackend();
38
- this.interval = betterSetInterval(this.handle, this.intervalMillis);
37
+ this.#storageBackend = serviceLocator.getStorageBackend();
38
+ this.#interval = betterSetInterval(this.handle, this.#intervalMillis);
39
39
  }
40
40
  async stop() {
41
- if (this.interval)
42
- betterClearInterval(this.interval);
43
- this.interval = undefined;
44
- this.client = undefined;
41
+ if (this.#interval)
42
+ betterClearInterval(this.#interval);
43
+ this.#interval = undefined;
44
+ this.#storageBackend = undefined;
45
45
  }
46
46
  getSample(sampleDurationMillis) {
47
- return this.store.getSample(sampleDurationMillis);
47
+ return this.#store.getSample(sampleDurationMillis);
48
48
  }
49
49
  /**
50
50
  * Records one snapshot, overloaded when rate-limit errors grew by more than the configured limit since the
@@ -53,21 +53,21 @@ export class ClientLoadSignal {
53
53
  */
54
54
  handle(intervalCallback) {
55
55
  const now = new Date();
56
- const allErrorCounts = this.client?.stats?.rateLimitErrors ?? [];
57
- const currentErrCount = allErrorCounts[CLIENT_RATE_LIMIT_ERROR_RETRY_COUNT] || 0;
56
+ const allErrorCounts = this.#storageBackend?.stats?.rateLimitErrors ?? [];
57
+ const currentErrCount = allErrorCounts[RATE_LIMIT_ERROR_RETRY_COUNT] || 0;
58
58
  const snapshot = {
59
59
  createdAt: now,
60
60
  isOverloaded: false,
61
61
  rateLimitErrorCount: currentErrCount,
62
62
  };
63
- const all = this.store.getAll();
63
+ const all = this.#store.getAll();
64
64
  const previousSnapshot = all[all.length - 1];
65
65
  if (previousSnapshot) {
66
66
  const delta = currentErrCount - previousSnapshot.rateLimitErrorCount;
67
- if (delta > this.maxErrors)
67
+ if (delta > this.#maxErrors)
68
68
  snapshot.isOverloaded = true;
69
69
  }
70
- this.store.push(snapshot, now);
70
+ this.#store.push(snapshot, now);
71
71
  intervalCallback();
72
72
  }
73
73
  }
@@ -6,10 +6,10 @@ import type { Snapshotter } from './snapshotter.js';
6
6
  export interface SystemInfo {
7
7
  /** If false, system is being overloaded. */
8
8
  isSystemIdle: boolean;
9
- memInfo: ClientInfo;
10
- eventLoopInfo: ClientInfo;
11
- cpuInfo: ClientInfo;
12
- clientInfo: ClientInfo;
9
+ memInfo: LoadSignalInfo;
10
+ eventLoopInfo: LoadSignalInfo;
11
+ cpuInfo: LoadSignalInfo;
12
+ storageBackendInfo: LoadSignalInfo;
13
13
  memTotalBytes?: number;
14
14
  memCurrentBytes?: number;
15
15
  /**
@@ -31,7 +31,7 @@ export interface SystemInfo {
31
31
  * Status of additional load signals beyond the built-in four.
32
32
  * Keys are `LoadSignal.name` values, values are overload info.
33
33
  */
34
- loadSignalInfo?: Record<string, ClientInfo>;
34
+ loadSignalInfo?: Record<string, LoadSignalInfo>;
35
35
  }
36
36
  /**
37
37
  * How far back the *current* system status looks by default — the window that gates task dispatch.
@@ -69,12 +69,12 @@ export interface SystemStatusOptions {
69
69
  /**
70
70
  * Additional load signals to include in the system status evaluation.
71
71
  * These are evaluated alongside the built-in memory, CPU, event loop,
72
- * and client signals. If any signal reports overload, the system is
73
- * considered overloaded. Each signal carries its own overload ratio.
72
+ * and storage backend signals. If any signal reports overload, the system
73
+ * is considered overloaded. Each signal carries its own overload ratio.
74
74
  */
75
75
  loadSignals?: LoadSignal[];
76
76
  }
77
- export interface ClientInfo {
77
+ export interface LoadSignalInfo {
78
78
  isOverloaded: boolean;
79
79
  limitRatio: number;
80
80
  actualRatio: number;
@@ -105,9 +105,7 @@ export interface FinalStatistics {
105
105
  * @internal
106
106
  */
107
107
  export declare class SystemStatus {
108
- private readonly currentHistoryMillis;
109
- private readonly historyMillis;
110
- private readonly signals;
108
+ #private;
111
109
  constructor(options: SystemStatusOptions);
112
110
  /**
113
111
  * The widest window any signal will be queried with, and therefore exactly how much history the signals are asked
@@ -15,7 +15,7 @@ const BUILTIN_SIGNAL_OPTION_KEYS = {
15
15
  memInfo: 'memory',
16
16
  eventLoopInfo: 'eventLoop',
17
17
  cpuInfo: 'cpu',
18
- clientInfo: 'client',
18
+ storageBackendInfo: 'storageBackend',
19
19
  };
20
20
  const BUILTIN_SIGNAL_NAMES = new Set(Object.keys(BUILTIN_SIGNAL_OPTION_KEYS));
21
21
  /**
@@ -32,14 +32,14 @@ const BUILTIN_SIGNAL_NAMES = new Set(Object.keys(BUILTIN_SIGNAL_OPTION_KEYS));
32
32
  * @internal
33
33
  */
34
34
  export class SystemStatus {
35
- currentHistoryMillis;
36
- historyMillis;
37
- signals;
35
+ #currentHistoryMillis;
36
+ #historyMillis;
37
+ #signals;
38
38
  constructor(options) {
39
39
  const { currentHistorySecs = DEFAULT_CURRENT_HISTORY_SECS, historySecs = DEFAULT_SNAPSHOT_HISTORY_SECS, snapshotter, loadSignals = [], } = options;
40
- this.currentHistoryMillis = currentHistorySecs * 1000;
41
- this.historyMillis = historySecs * 1000;
42
- this.signals = [...snapshotter.getLoadSignals(), ...loadSignals];
40
+ this.#currentHistoryMillis = currentHistorySecs * 1000;
41
+ this.#historyMillis = historySecs * 1000;
42
+ this.#signals = [...snapshotter.getLoadSignals(), ...loadSignals];
43
43
  this.assertUniqueSignalNames();
44
44
  }
45
45
  /**
@@ -48,7 +48,7 @@ export class SystemStatus {
48
48
  * defaults.
49
49
  */
50
50
  get maxSampleWindowMillis() {
51
- return Math.max(this.currentHistoryMillis, this.historyMillis);
51
+ return Math.max(this.#currentHistoryMillis, this.#historyMillis);
52
52
  }
53
53
  /**
54
54
  * Signal names are the keys of the reported {@link SystemInfo}, so a duplicate would leave a status object that
@@ -57,7 +57,7 @@ export class SystemStatus {
57
57
  */
58
58
  assertUniqueSignalNames() {
59
59
  const seen = new Set();
60
- for (const { name } of this.signals) {
60
+ for (const { name } of this.#signals) {
61
61
  if (!seen.has(name)) {
62
62
  seen.add(name);
63
63
  continue;
@@ -85,7 +85,7 @@ export class SystemStatus {
85
85
  * and `true` otherwise.
86
86
  */
87
87
  getCurrentStatus() {
88
- return this.isSystemIdle(this.currentHistoryMillis);
88
+ return this.isSystemIdle(this.#currentHistoryMillis);
89
89
  }
90
90
  /**
91
91
  * Returns an {@link SystemInfo} object with the following structure:
@@ -103,7 +103,7 @@ export class SystemStatus {
103
103
  * `historySecs` seconds and `true` otherwise.
104
104
  */
105
105
  getHistoricalStatus() {
106
- return this.isSystemIdle(this.historyMillis);
106
+ return this.isSystemIdle(this.#historyMillis);
107
107
  }
108
108
  /**
109
109
  * Returns a system status object.
@@ -114,10 +114,10 @@ export class SystemStatus {
114
114
  memInfo: { isOverloaded: false, limitRatio: 0, actualRatio: 0 },
115
115
  eventLoopInfo: { isOverloaded: false, limitRatio: 0, actualRatio: 0 },
116
116
  cpuInfo: { isOverloaded: false, limitRatio: 0, actualRatio: 0 },
117
- clientInfo: { isOverloaded: false, limitRatio: 0, actualRatio: 0 },
117
+ storageBackendInfo: { isOverloaded: false, limitRatio: 0, actualRatio: 0 },
118
118
  };
119
119
  let loadSignalInfo;
120
- for (const signal of this.signals) {
120
+ for (const signal of this.#signals) {
121
121
  const sample = signal.getSample(sampleDurationMillis);
122
122
  const info = evaluateLoadSignalSample(sample, signal.overloadedRatio);
123
123
  if (info.isOverloaded) {