@crawlee/core 4.0.0-beta.104 → 4.0.0-beta.106
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/autoscaling/autoscaled_pool.d.ts +3 -21
- package/autoscaling/autoscaled_pool.js +85 -85
- package/autoscaling/client_load_signal.d.ts +1 -5
- package/autoscaling/client_load_signal.js +20 -20
- package/autoscaling/concurrency_system.d.ts +5 -20
- package/autoscaling/concurrency_system.js +81 -80
- package/autoscaling/cpu_load_signal.d.ts +1 -2
- package/autoscaling/cpu_load_signal.js +10 -10
- package/autoscaling/event_loop_load_signal.d.ts +1 -4
- package/autoscaling/event_loop_load_signal.js +18 -18
- package/autoscaling/load_signal.d.ts +1 -1
- package/autoscaling/load_signal.js +12 -11
- package/autoscaling/memory_load_signal.d.ts +3 -12
- package/autoscaling/memory_load_signal.js +40 -41
- package/autoscaling/snapshotter.d.ts +1 -4
- package/autoscaling/snapshotter.js +12 -12
- package/autoscaling/system_status.d.ts +1 -3
- package/autoscaling/system_status.js +11 -11
- package/configuration.d.ts +1 -1
- package/configuration.js +3 -3
- package/crawlers/context_pipeline.js +6 -6
- package/crawlers/crawler_commons.d.ts +6 -56
- package/crawlers/crawler_commons.js +1 -107
- package/crawlers/index.d.ts +1 -1
- package/crawlers/index.js +0 -1
- package/crawlers/statistics.d.ts +1 -8
- package/crawlers/statistics.js +45 -44
- package/events/event_manager.d.ts +1 -1
- package/events/event_manager.js +3 -3
- package/events/local_event_manager.d.ts +1 -1
- package/events/local_event_manager.js +3 -3
- package/log.js +5 -1
- package/memory-storage/memory-storage.d.ts +1 -5
- package/memory-storage/memory-storage.js +2 -2
- package/memory-storage/resource-clients/dataset.d.ts +1 -1
- package/memory-storage/resource-clients/dataset.js +6 -5
- package/memory-storage/resource-clients/key-value-store.d.ts +1 -1
- package/memory-storage/resource-clients/key-value-store.js +13 -12
- package/memory-storage/resource-clients/request-queue.d.ts +4 -23
- package/memory-storage/resource-clients/request-queue.js +59 -58
- package/owned_or_injected.d.ts +1 -3
- package/owned_or_injected.js +17 -17
- package/package.json +5 -5
- package/proxy_configuration.d.ts +1 -3
- package/proxy_configuration.js +8 -8
- package/recoverable_state.d.ts +1 -10
- package/recoverable_state.js +41 -41
- package/request.d.ts +1 -2
- package/request.js +10 -13
- package/router.d.ts +1 -4
- package/router.js +23 -23
- package/serialization.js +8 -9
- package/service_locator.d.ts +1 -10
- package/service_locator.js +48 -48
- package/session_pool/session.d.ts +1 -12
- package/session_pool/session.js +50 -50
- package/session_pool/session_pool.d.ts +2 -11
- package/session_pool/session_pool.js +59 -58
- package/storages/dataset.d.ts +12 -1
- package/storages/dataset.js +121 -22
- package/storages/index.d.ts +1 -1
- package/storages/index.js +1 -1
- package/storages/key_value_store.d.ts +19 -4
- package/storages/key_value_store.js +174 -48
- package/storages/request_dedup_cache.d.ts +1 -2
- package/storages/request_dedup_cache.js +9 -9
- package/storages/request_list.d.ts +2 -22
- package/storages/request_list.js +74 -73
- package/storages/request_manager_tandem.d.ts +1 -10
- package/storages/request_manager_tandem.js +27 -27
- package/storages/request_queue.d.ts +21 -18
- package/storages/request_queue.js +256 -53
- package/storages/sitemap_request_loader.d.ts +1 -44
- package/storages/sitemap_request_loader.js +87 -87
- package/storages/storage_instance_manager.d.ts +1 -2
- package/storages/storage_instance_manager.js +17 -17
- package/storages/storage_stats.d.ts +1 -1
- package/storages/storage_stats.js +4 -4
- package/storages/transaction.d.ts +252 -0
- package/storages/transaction.js +251 -0
- package/system-info/runtime.js +7 -7
- package/storages/access_checking.d.ts +0 -12
- package/storages/access_checking.js +0 -17
package/crawlers/statistics.js
CHANGED
|
@@ -6,14 +6,14 @@ import { ErrorTracker } from './error_tracker.js';
|
|
|
6
6
|
* @ignore
|
|
7
7
|
*/
|
|
8
8
|
class Job {
|
|
9
|
-
lastRunAt = null;
|
|
10
|
-
durationMillis;
|
|
9
|
+
#lastRunAt = null;
|
|
10
|
+
#durationMillis;
|
|
11
11
|
run() {
|
|
12
|
-
this
|
|
12
|
+
this.#lastRunAt = Date.now();
|
|
13
13
|
}
|
|
14
14
|
finish() {
|
|
15
|
-
this
|
|
16
|
-
return this
|
|
15
|
+
this.#durationMillis = Date.now() - this.#lastRunAt;
|
|
16
|
+
return this.#durationMillis;
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
const errorTrackerConfig = {
|
|
@@ -35,6 +35,7 @@ const errorTrackerConfig = {
|
|
|
35
35
|
* @category Crawlers
|
|
36
36
|
*/
|
|
37
37
|
export class Statistics {
|
|
38
|
+
// kept as TS-private: statistics tests read the static counter directly
|
|
38
39
|
static id = 0;
|
|
39
40
|
/**
|
|
40
41
|
* An error tracker for final retry errors.
|
|
@@ -58,20 +59,20 @@ export class Statistics {
|
|
|
58
59
|
requestRetryHistogram = [];
|
|
59
60
|
keyValueStore = undefined;
|
|
60
61
|
persistStateKey;
|
|
61
|
-
logIntervalMillis;
|
|
62
|
-
logMessage;
|
|
63
|
-
listener;
|
|
64
|
-
requestsInProgress = new Map();
|
|
62
|
+
#logIntervalMillis;
|
|
63
|
+
#logMessage;
|
|
64
|
+
#listener;
|
|
65
|
+
#requestsInProgress = new Map();
|
|
65
66
|
log;
|
|
66
|
-
instanceStart;
|
|
67
|
-
logInterval;
|
|
68
|
-
|
|
69
|
-
persistenceOptions;
|
|
67
|
+
#instanceStart;
|
|
68
|
+
#logInterval;
|
|
69
|
+
#events;
|
|
70
|
+
#persistenceOptions;
|
|
70
71
|
get events() {
|
|
71
|
-
if (!this
|
|
72
|
-
this
|
|
72
|
+
if (!this.#events) {
|
|
73
|
+
this.#events = serviceLocator.getEventManager();
|
|
73
74
|
}
|
|
74
|
-
return this
|
|
75
|
+
return this.#events;
|
|
75
76
|
}
|
|
76
77
|
/**
|
|
77
78
|
* Construct a statistics instance to pass to a crawler via its `statistics` option, e.g. to preconfigure
|
|
@@ -95,11 +96,11 @@ export class Statistics {
|
|
|
95
96
|
this.log = (options.log ?? serviceLocator.getLogger()).child({ prefix: 'Statistics' });
|
|
96
97
|
this.errorTracker = new ErrorTracker({ ...errorTrackerConfig, saveErrorSnapshots });
|
|
97
98
|
this.errorTrackerRetry = new ErrorTracker({ ...errorTrackerConfig, saveErrorSnapshots });
|
|
98
|
-
this
|
|
99
|
-
this
|
|
99
|
+
this.#logIntervalMillis = logIntervalSecs * 1000;
|
|
100
|
+
this.#logMessage = logMessage;
|
|
100
101
|
this.keyValueStore = keyValueStore;
|
|
101
|
-
this
|
|
102
|
-
this
|
|
102
|
+
this.#listener = this.persistState.bind(this);
|
|
103
|
+
this.#persistenceOptions = persistenceOptions;
|
|
103
104
|
// initialize by "resetting"
|
|
104
105
|
this.reset();
|
|
105
106
|
}
|
|
@@ -128,15 +129,15 @@ export class Statistics {
|
|
|
128
129
|
retryErrors: this.errorTrackerRetry.result,
|
|
129
130
|
};
|
|
130
131
|
this.requestRetryHistogram.length = 0;
|
|
131
|
-
this
|
|
132
|
-
this
|
|
132
|
+
this.#requestsInProgress.clear();
|
|
133
|
+
this.#instanceStart = Date.now();
|
|
133
134
|
this.teardown();
|
|
134
135
|
}
|
|
135
136
|
/**
|
|
136
137
|
* @param options - Override the persistence options provided in the constructor
|
|
137
138
|
*/
|
|
138
139
|
async resetStore(options) {
|
|
139
|
-
if (!this
|
|
140
|
+
if (!this.#persistenceOptions.enable && !options?.enable) {
|
|
140
141
|
return;
|
|
141
142
|
}
|
|
142
143
|
if (!this.keyValueStore) {
|
|
@@ -159,18 +160,18 @@ export class Statistics {
|
|
|
159
160
|
* @ignore
|
|
160
161
|
*/
|
|
161
162
|
startJob(id) {
|
|
162
|
-
let job = this
|
|
163
|
+
let job = this.#requestsInProgress.get(id);
|
|
163
164
|
if (!job)
|
|
164
165
|
job = new Job();
|
|
165
166
|
job.run();
|
|
166
|
-
this
|
|
167
|
+
this.#requestsInProgress.set(id, job);
|
|
167
168
|
}
|
|
168
169
|
/**
|
|
169
170
|
* Mark job as finished and sets the state
|
|
170
171
|
* @ignore
|
|
171
172
|
*/
|
|
172
173
|
finishJob(id, retryCount) {
|
|
173
|
-
const job = this
|
|
174
|
+
const job = this.#requestsInProgress.get(id);
|
|
174
175
|
if (!job)
|
|
175
176
|
return;
|
|
176
177
|
const jobDurationMillis = job.finish();
|
|
@@ -181,20 +182,20 @@ export class Statistics {
|
|
|
181
182
|
this.state.requestMinDurationMillis = jobDurationMillis;
|
|
182
183
|
if (jobDurationMillis > this.state.requestMaxDurationMillis)
|
|
183
184
|
this.state.requestMaxDurationMillis = jobDurationMillis;
|
|
184
|
-
this
|
|
185
|
+
this.#requestsInProgress.delete(id);
|
|
185
186
|
}
|
|
186
187
|
/**
|
|
187
188
|
* Mark job as failed and sets the state
|
|
188
189
|
* @ignore
|
|
189
190
|
*/
|
|
190
191
|
failJob(id, retryCount) {
|
|
191
|
-
const job = this
|
|
192
|
+
const job = this.#requestsInProgress.get(id);
|
|
192
193
|
if (!job)
|
|
193
194
|
return;
|
|
194
195
|
this.state.requestTotalFailedDurationMillis += job.finish();
|
|
195
196
|
this.state.requestsFailed++;
|
|
196
197
|
this.saveRetryCountForJob(retryCount);
|
|
197
|
-
this
|
|
198
|
+
this.#requestsInProgress.delete(id);
|
|
198
199
|
}
|
|
199
200
|
/**
|
|
200
201
|
* Discards a started job without affecting the finished/failed counters, e.g. when a request
|
|
@@ -202,14 +203,14 @@ export class Statistics {
|
|
|
202
203
|
* @ignore
|
|
203
204
|
*/
|
|
204
205
|
discardJob(id) {
|
|
205
|
-
this
|
|
206
|
+
this.#requestsInProgress.delete(id);
|
|
206
207
|
}
|
|
207
208
|
/**
|
|
208
209
|
* Calculate the current statistics
|
|
209
210
|
*/
|
|
210
211
|
calculate() {
|
|
211
212
|
const { requestsFailed, requestsFinished, requestTotalFailedDurationMillis, requestTotalFinishedDurationMillis, } = this.state;
|
|
212
|
-
const totalMillis = Date.now() - this
|
|
213
|
+
const totalMillis = Date.now() - this.#instanceStart;
|
|
213
214
|
const totalMinutes = totalMillis / 1000 / 60;
|
|
214
215
|
return {
|
|
215
216
|
requestAvgFailedDurationMillis: Math.round(requestTotalFailedDurationMillis / requestsFailed) || Infinity,
|
|
@@ -228,23 +229,23 @@ export class Statistics {
|
|
|
228
229
|
async startCapturing() {
|
|
229
230
|
// A single instance drives one logging interval and one PERSIST_STATE listener, so a second concurrent
|
|
230
231
|
// capture (e.g. sharing one instance across crawlers running at once) would orphan the first. Fail loudly.
|
|
231
|
-
if (this
|
|
232
|
+
if (this.#logInterval) {
|
|
232
233
|
throw new Error('Statistics.startCapturing() was already called - this instance is already capturing.');
|
|
233
234
|
}
|
|
234
235
|
this.keyValueStore ??= await KeyValueStore.open(null, { configuration: serviceLocator.getConfiguration() });
|
|
235
236
|
if (this.state.crawlerStartedAt === null) {
|
|
236
237
|
this.state.crawlerStartedAt = new Date();
|
|
237
238
|
}
|
|
238
|
-
if (this
|
|
239
|
+
if (this.#persistenceOptions.enable) {
|
|
239
240
|
await this.maybeLoadStatistics();
|
|
240
|
-
this.events.on("persistState" /* EventType.PERSIST_STATE */, this
|
|
241
|
+
this.events.on("persistState" /* EventType.PERSIST_STATE */, this.#listener);
|
|
241
242
|
}
|
|
242
|
-
this
|
|
243
|
-
this.log.info(this
|
|
243
|
+
this.#logInterval = setInterval(() => {
|
|
244
|
+
this.log.info(this.#logMessage, {
|
|
244
245
|
...this.calculate(),
|
|
245
246
|
retryHistogram: this.requestRetryHistogram,
|
|
246
247
|
});
|
|
247
|
-
}, this
|
|
248
|
+
}, this.#logIntervalMillis);
|
|
248
249
|
}
|
|
249
250
|
/**
|
|
250
251
|
* Stops logging and remove event listeners, then persist
|
|
@@ -265,7 +266,7 @@ export class Statistics {
|
|
|
265
266
|
* @param options - Override the persistence options provided in the constructor
|
|
266
267
|
*/
|
|
267
268
|
async persistState(options) {
|
|
268
|
-
if (!this
|
|
269
|
+
if (!this.#persistenceOptions.enable && !options?.enable) {
|
|
269
270
|
return;
|
|
270
271
|
}
|
|
271
272
|
// this might be called before startCapturing was called without using await, should not crash
|
|
@@ -312,17 +313,17 @@ export class Statistics {
|
|
|
312
313
|
this.state.crawlerStartedAt = savedState.crawlerStartedAt ? new Date(savedState.crawlerStartedAt) : null;
|
|
313
314
|
this.state.statsPersistedAt = savedState.statsPersistedAt ? new Date(savedState.statsPersistedAt) : null;
|
|
314
315
|
this.state.crawlerRuntimeMillis = savedState.crawlerRuntimeMillis;
|
|
315
|
-
this
|
|
316
|
+
this.#instanceStart = Date.now() - (+this.state.statsPersistedAt - savedState.crawlerLastStartTimestamp);
|
|
316
317
|
this.log.debug('Loaded from KeyValueStore');
|
|
317
318
|
}
|
|
318
319
|
teardown() {
|
|
319
320
|
// this can be called before a call to startCapturing happens (or in a 'finally' block)
|
|
320
321
|
// Only unsubscribe if event manager was already resolved — avoid eagerly resolving it
|
|
321
322
|
// (e.g. during the constructor's reset() call, which would capture the wrong context)
|
|
322
|
-
this
|
|
323
|
-
if (this
|
|
324
|
-
clearInterval(this
|
|
325
|
-
this
|
|
323
|
+
this.#events?.off("persistState" /* EventType.PERSIST_STATE */, this.#listener);
|
|
324
|
+
if (this.#logInterval) {
|
|
325
|
+
clearInterval(this.#logInterval);
|
|
326
|
+
this.#logInterval = null;
|
|
326
327
|
}
|
|
327
328
|
}
|
|
328
329
|
/**
|
|
@@ -335,7 +336,7 @@ export class Statistics {
|
|
|
335
336
|
// omit duplicated information
|
|
336
337
|
const result = {
|
|
337
338
|
...this.state,
|
|
338
|
-
crawlerLastStartTimestamp: this
|
|
339
|
+
crawlerLastStartTimestamp: this.#instanceStart,
|
|
339
340
|
crawlerFinishedAt: this.state.crawlerFinishedAt
|
|
340
341
|
? new Date(this.state.crawlerFinishedAt).toISOString()
|
|
341
342
|
: null,
|
|
@@ -41,12 +41,12 @@ interface Intervals {
|
|
|
41
41
|
systemInfo?: BetterIntervalID;
|
|
42
42
|
}
|
|
43
43
|
export declare abstract class EventManager {
|
|
44
|
+
#private;
|
|
44
45
|
protected events: AsyncEventEmitter<{}>;
|
|
45
46
|
protected initialized: boolean;
|
|
46
47
|
protected intervals: Intervals;
|
|
47
48
|
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
48
49
|
protected log: import("@crawlee/types").CrawleeLogger;
|
|
49
|
-
private persistStateIntervalMillis;
|
|
50
50
|
constructor(options: EventManagerOptions);
|
|
51
51
|
/**
|
|
52
52
|
* Initializes the event manager by starting the `persistState` event interval.
|
package/events/event_manager.js
CHANGED
|
@@ -15,9 +15,9 @@ export class EventManager {
|
|
|
15
15
|
initialized = false;
|
|
16
16
|
intervals = {};
|
|
17
17
|
log = serviceLocator.getLogger().child({ prefix: 'Events' });
|
|
18
|
-
persistStateIntervalMillis;
|
|
18
|
+
#persistStateIntervalMillis;
|
|
19
19
|
constructor(options) {
|
|
20
|
-
this
|
|
20
|
+
this.#persistStateIntervalMillis = options.persistStateIntervalMillis;
|
|
21
21
|
this.events.setMaxListeners(50);
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
@@ -31,7 +31,7 @@ export class EventManager {
|
|
|
31
31
|
this.intervals.persistState = betterSetInterval((intervalCallback) => {
|
|
32
32
|
this.emit("persistState" /* EventType.PERSIST_STATE */, { isMigrating: false });
|
|
33
33
|
intervalCallback();
|
|
34
|
-
}, this
|
|
34
|
+
}, this.#persistStateIntervalMillis);
|
|
35
35
|
this.initialized = true;
|
|
36
36
|
}
|
|
37
37
|
/**
|
|
@@ -5,7 +5,7 @@ export interface LocalEventManagerOptions extends EventManagerOptions {
|
|
|
5
5
|
systemInfoIntervalMillis: number;
|
|
6
6
|
}
|
|
7
7
|
export declare class LocalEventManager extends EventManager {
|
|
8
|
-
private
|
|
8
|
+
#private;
|
|
9
9
|
constructor(options: LocalEventManagerOptions);
|
|
10
10
|
/**
|
|
11
11
|
* Creates a new `LocalEventManager` based on the provided `Configuration`.
|
|
@@ -2,10 +2,10 @@ import { betterClearInterval, betterSetInterval } from '@apify/utilities';
|
|
|
2
2
|
import { serviceLocator } from '../service_locator.js';
|
|
3
3
|
import { EventManager } from './event_manager.js';
|
|
4
4
|
export class LocalEventManager extends EventManager {
|
|
5
|
-
systemInfoIntervalMillis;
|
|
5
|
+
#systemInfoIntervalMillis;
|
|
6
6
|
constructor(options) {
|
|
7
7
|
super(options);
|
|
8
|
-
this
|
|
8
|
+
this.#systemInfoIntervalMillis = options.systemInfoIntervalMillis;
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
11
|
* Creates a new `LocalEventManager` based on the provided `Configuration`.
|
|
@@ -28,7 +28,7 @@ export class LocalEventManager extends EventManager {
|
|
|
28
28
|
}
|
|
29
29
|
await super.init();
|
|
30
30
|
this.emitSystemInfoEvent = this.emitSystemInfoEvent.bind(this);
|
|
31
|
-
this.intervals.systemInfo = betterSetInterval(this.emitSystemInfoEvent.bind(this), this
|
|
31
|
+
this.intervals.systemInfo = betterSetInterval(this.emitSystemInfoEvent.bind(this), this.#systemInfoIntervalMillis);
|
|
32
32
|
}
|
|
33
33
|
/**
|
|
34
34
|
* @inheritDoc
|
package/log.js
CHANGED
|
@@ -30,6 +30,8 @@ import log, { Log, Logger, LoggerJson, LoggerText, LogLevel } from '@apify/log';
|
|
|
30
30
|
* ```
|
|
31
31
|
*/
|
|
32
32
|
export class BaseCrawleeLogger {
|
|
33
|
+
// kept as TS-private: the adaptive crawler's log proxy calls non-intercepted methods with `this === proxy`,
|
|
34
|
+
// where `#`-field access would throw at runtime
|
|
33
35
|
options;
|
|
34
36
|
warningsLogged = new Set();
|
|
35
37
|
constructor(options = {}) {
|
|
@@ -87,7 +89,9 @@ export class BaseCrawleeLogger {
|
|
|
87
89
|
*/
|
|
88
90
|
export class ApifyLogAdapter extends BaseCrawleeLogger {
|
|
89
91
|
apifyLog;
|
|
90
|
-
constructor(
|
|
92
|
+
constructor(
|
|
93
|
+
// kept as a TS-private parameter property: reached through the adaptive crawler's log proxy, see above
|
|
94
|
+
apifyLog, options) {
|
|
91
95
|
super(options ?? {});
|
|
92
96
|
this.apifyLog = apifyLog;
|
|
93
97
|
}
|
|
@@ -10,12 +10,8 @@ export interface MemoryStorageOptions {
|
|
|
10
10
|
logger?: CrawleeLogger;
|
|
11
11
|
}
|
|
12
12
|
export declare class MemoryStorageBackend implements storage.StorageBackend {
|
|
13
|
+
#private;
|
|
13
14
|
readonly logger?: CrawleeLogger;
|
|
14
|
-
/**
|
|
15
|
-
* Unique per-instance cache partition key. Mirrors the way `FileSystemStorageBackend` partitions its
|
|
16
|
-
* cache by storage directory: two distinct `MemoryStorageBackend` instances must not share cached backends.
|
|
17
|
-
*/
|
|
18
|
-
private readonly instanceCacheKey;
|
|
19
15
|
readonly keyValueStoreBackendCache: KeyValueStoreBackend[];
|
|
20
16
|
readonly datasetBackendCache: DatasetBackend[];
|
|
21
17
|
readonly requestQueueBackendCache: RequestQueueBackend[];
|
|
@@ -8,7 +8,7 @@ export class MemoryStorageBackend {
|
|
|
8
8
|
* Unique per-instance cache partition key. Mirrors the way `FileSystemStorageBackend` partitions its
|
|
9
9
|
* cache by storage directory: two distinct `MemoryStorageBackend` instances must not share cached backends.
|
|
10
10
|
*/
|
|
11
|
-
instanceCacheKey = `MemoryStorageBackend:${randomUUID()}`;
|
|
11
|
+
#instanceCacheKey = `MemoryStorageBackend:${randomUUID()}`;
|
|
12
12
|
keyValueStoreBackendCache = [];
|
|
13
13
|
datasetBackendCache = [];
|
|
14
14
|
requestQueueBackendCache = [];
|
|
@@ -20,7 +20,7 @@ export class MemoryStorageBackend {
|
|
|
20
20
|
* cache partitions in the storage backend cache.
|
|
21
21
|
*/
|
|
22
22
|
getStorageBackendCacheKey() {
|
|
23
|
-
return this
|
|
23
|
+
return this.#instanceCacheKey;
|
|
24
24
|
}
|
|
25
25
|
static resolveStorageKey(options) {
|
|
26
26
|
const isAlias = 'alias' in options && !!options.alias;
|
|
@@ -14,6 +14,7 @@ export interface DatasetBackendOptions {
|
|
|
14
14
|
storageBackend: MemoryStorageBackend;
|
|
15
15
|
}
|
|
16
16
|
export declare class DatasetBackend<Data extends Dictionary = Dictionary> extends BaseClient implements storage.DatasetBackend<Data> {
|
|
17
|
+
#private;
|
|
17
18
|
name?: string;
|
|
18
19
|
/**
|
|
19
20
|
* The key used for cache lookup. For named storages, this equals the name. For alias (unnamed)
|
|
@@ -24,7 +25,6 @@ export declare class DatasetBackend<Data extends Dictionary = Dictionary> extend
|
|
|
24
25
|
accessedAt: Date;
|
|
25
26
|
modifiedAt: Date;
|
|
26
27
|
itemCount: number;
|
|
27
|
-
private readonly datasetEntries;
|
|
28
28
|
private readonly storageBackend;
|
|
29
29
|
constructor(options: DatasetBackendOptions);
|
|
30
30
|
getMetadata(): Promise<storage.DatasetInfo>;
|
|
@@ -22,7 +22,8 @@ export class DatasetBackend extends BaseClient {
|
|
|
22
22
|
accessedAt = new Date();
|
|
23
23
|
modifiedAt = new Date();
|
|
24
24
|
itemCount = 0;
|
|
25
|
-
datasetEntries = new Map();
|
|
25
|
+
#datasetEntries = new Map();
|
|
26
|
+
// kept as TS-private: storage-backend tests read this field at runtime
|
|
26
27
|
storageBackend;
|
|
27
28
|
constructor(options) {
|
|
28
29
|
super(options.id ?? randomUUID());
|
|
@@ -39,12 +40,12 @@ export class DatasetBackend extends BaseClient {
|
|
|
39
40
|
if (storeIndex !== -1) {
|
|
40
41
|
const [oldBackend] = this.storageBackend.datasetBackendCache.splice(storeIndex, 1);
|
|
41
42
|
oldBackend.itemCount = 0;
|
|
42
|
-
oldBackend
|
|
43
|
+
oldBackend.#datasetEntries.clear();
|
|
43
44
|
}
|
|
44
45
|
}
|
|
45
46
|
async purge() {
|
|
46
47
|
this.itemCount = 0;
|
|
47
|
-
this
|
|
48
|
+
this.#datasetEntries.clear();
|
|
48
49
|
this.updateTimestamps(true);
|
|
49
50
|
}
|
|
50
51
|
getData(options = {}) {
|
|
@@ -67,7 +68,7 @@ export class DatasetBackend extends BaseClient {
|
|
|
67
68
|
const items = [];
|
|
68
69
|
for (let idx = start; idx < end; idx++) {
|
|
69
70
|
const entryNumber = this.generateLocalEntryName(idx);
|
|
70
|
-
items.push(this
|
|
71
|
+
items.push(this.#datasetEntries.get(entryNumber));
|
|
71
72
|
}
|
|
72
73
|
this.updateTimestamps(false);
|
|
73
74
|
return {
|
|
@@ -82,7 +83,7 @@ export class DatasetBackend extends BaseClient {
|
|
|
82
83
|
async pushData(items) {
|
|
83
84
|
for (const entry of items) {
|
|
84
85
|
const idx = this.generateLocalEntryName(++this.itemCount);
|
|
85
|
-
this
|
|
86
|
+
this.#datasetEntries.set(idx, JSON.parse(JSON.stringify(entry)));
|
|
86
87
|
}
|
|
87
88
|
this.updateTimestamps(true);
|
|
88
89
|
}
|
|
@@ -19,6 +19,7 @@ export interface InternalKeyRecord {
|
|
|
19
19
|
extension: string;
|
|
20
20
|
}
|
|
21
21
|
export declare class KeyValueStoreBackend extends BaseClient implements storage.KeyValueStoreBackend {
|
|
22
|
+
#private;
|
|
22
23
|
name?: string;
|
|
23
24
|
/**
|
|
24
25
|
* The key used for cache lookup. For named storages, this equals the name. For alias (unnamed)
|
|
@@ -28,7 +29,6 @@ export declare class KeyValueStoreBackend extends BaseClient implements storage.
|
|
|
28
29
|
createdAt: Date;
|
|
29
30
|
accessedAt: Date;
|
|
30
31
|
modifiedAt: Date;
|
|
31
|
-
private readonly keyValueEntries;
|
|
32
32
|
private readonly storageBackend;
|
|
33
33
|
constructor(options: KeyValueStoreBackendOptions);
|
|
34
34
|
getMetadata(): Promise<storage.KeyValueStoreInfo>;
|
|
@@ -19,7 +19,8 @@ export class KeyValueStoreBackend extends BaseClient {
|
|
|
19
19
|
createdAt = new Date();
|
|
20
20
|
accessedAt = new Date();
|
|
21
21
|
modifiedAt = new Date();
|
|
22
|
-
keyValueEntries = new Map();
|
|
22
|
+
#keyValueEntries = new Map();
|
|
23
|
+
// kept as TS-private: storage-backend tests read this field at runtime
|
|
23
24
|
storageBackend;
|
|
24
25
|
constructor(options) {
|
|
25
26
|
super(options.id ?? randomUUID());
|
|
@@ -35,11 +36,11 @@ export class KeyValueStoreBackend extends BaseClient {
|
|
|
35
36
|
const storeIndex = this.storageBackend.keyValueStoreBackendCache.findIndex((store) => store.id === this.id);
|
|
36
37
|
if (storeIndex !== -1) {
|
|
37
38
|
const [oldBackend] = this.storageBackend.keyValueStoreBackendCache.splice(storeIndex, 1);
|
|
38
|
-
oldBackend
|
|
39
|
+
oldBackend.#keyValueEntries.clear();
|
|
39
40
|
}
|
|
40
41
|
}
|
|
41
42
|
async purge() {
|
|
42
|
-
this
|
|
43
|
+
this.#keyValueEntries.clear();
|
|
43
44
|
this.updateTimestamps(true);
|
|
44
45
|
}
|
|
45
46
|
/**
|
|
@@ -49,9 +50,9 @@ export class KeyValueStoreBackend extends BaseClient {
|
|
|
49
50
|
* preserve the bare `INPUT` key only.
|
|
50
51
|
*/
|
|
51
52
|
async purgeExceptInput() {
|
|
52
|
-
for (const key of this
|
|
53
|
+
for (const key of this.#keyValueEntries.keys()) {
|
|
53
54
|
if (key !== KEY_VALUE_STORE_INPUT_KEY) {
|
|
54
|
-
this
|
|
55
|
+
this.#keyValueEntries.delete(key);
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
58
|
this.updateTimestamps(true);
|
|
@@ -65,7 +66,7 @@ export class KeyValueStoreBackend extends BaseClient {
|
|
|
65
66
|
})
|
|
66
67
|
.parse(options);
|
|
67
68
|
const items = [];
|
|
68
|
-
for (const record of this
|
|
69
|
+
for (const record of this.#keyValueEntries.values()) {
|
|
69
70
|
const size = Buffer.byteLength(record.value);
|
|
70
71
|
items.push({
|
|
71
72
|
key: record.key,
|
|
@@ -114,11 +115,11 @@ export class KeyValueStoreBackend extends BaseClient {
|
|
|
114
115
|
*/
|
|
115
116
|
async recordExists(key) {
|
|
116
117
|
s.string().parse(key);
|
|
117
|
-
return this
|
|
118
|
+
return this.#keyValueEntries.has(key);
|
|
118
119
|
}
|
|
119
120
|
async getValue(key) {
|
|
120
121
|
s.string().parse(key);
|
|
121
|
-
const entry = this
|
|
122
|
+
const entry = this.#keyValueEntries.get(key);
|
|
122
123
|
if (!entry) {
|
|
123
124
|
return undefined;
|
|
124
125
|
}
|
|
@@ -169,19 +170,19 @@ export class KeyValueStoreBackend extends BaseClient {
|
|
|
169
170
|
const normalizedValue = typeof value === 'string'
|
|
170
171
|
? Buffer.from(value, 'utf-8')
|
|
171
172
|
: toBuffer(value);
|
|
172
|
-
const
|
|
173
|
+
const normalizedRecord = {
|
|
173
174
|
extension,
|
|
174
175
|
key,
|
|
175
176
|
value: normalizedValue,
|
|
176
177
|
contentType,
|
|
177
178
|
};
|
|
178
|
-
this
|
|
179
|
+
this.#keyValueEntries.set(key, normalizedRecord);
|
|
179
180
|
this.updateTimestamps(true);
|
|
180
181
|
}
|
|
181
182
|
async deleteValue(key) {
|
|
182
183
|
s.string().parse(key);
|
|
183
|
-
if (this
|
|
184
|
-
this
|
|
184
|
+
if (this.#keyValueEntries.has(key)) {
|
|
185
|
+
this.#keyValueEntries.delete(key);
|
|
185
186
|
this.updateTimestamps(true);
|
|
186
187
|
}
|
|
187
188
|
}
|
|
@@ -22,6 +22,7 @@ export interface InternalRequest {
|
|
|
22
22
|
json: string;
|
|
23
23
|
}
|
|
24
24
|
export declare class RequestQueueBackend extends BaseClient implements storage.RequestQueueBackend {
|
|
25
|
+
#private;
|
|
25
26
|
name?: string;
|
|
26
27
|
/**
|
|
27
28
|
* The key used for cache lookup. For named storages, this equals the name. For alias (unnamed)
|
|
@@ -33,26 +34,6 @@ export declare class RequestQueueBackend extends BaseClient implements storage.R
|
|
|
33
34
|
modifiedAt: Date;
|
|
34
35
|
handledRequestCount: number;
|
|
35
36
|
pendingRequestCount: number;
|
|
36
|
-
/**
|
|
37
|
-
* Serializes every operation that reads-then-writes this backend's shared queue state — the
|
|
38
|
-
* `requests` map, the `forefrontRequestIds` array, the `inProgressRequestIds` set and the request
|
|
39
|
-
* counts. Those mutations span `await` points, so without this mutex a concurrent operation could
|
|
40
|
-
* interleave and corrupt them (e.g. a head scan pruning `forefrontRequestIds` while
|
|
41
|
-
* `addBatchOfRequests` pushes to it). Held by every mutating method as well as by `isEmpty`/
|
|
42
|
-
* `isFinished`, whose head scan also prunes `forefrontRequestIds`.
|
|
43
|
-
*/
|
|
44
|
-
private readonly queueStateMutex;
|
|
45
|
-
private forefrontRequestIds;
|
|
46
|
-
/**
|
|
47
|
-
* IDs of requests currently fetched but not yet handled or reclaimed. A request in this set is
|
|
48
|
-
* "in progress" and will not be handed out again by {@link fetchNextRequest}.
|
|
49
|
-
*
|
|
50
|
-
* Unlike the file-system / platform clients, the in-memory queue lives entirely within a single
|
|
51
|
-
* process and is never shared with another consumer, so there is no need for an expiring,
|
|
52
|
-
* cross-process-visible lock — tracking in-progress requests in this set is enough.
|
|
53
|
-
*/
|
|
54
|
-
private readonly inProgressRequestIds;
|
|
55
|
-
private readonly requests;
|
|
56
37
|
private readonly storageBackend;
|
|
57
38
|
constructor(options: RequestQueueBackendOptions);
|
|
58
39
|
getMetadata(): Promise<storage.RequestQueueInfo>;
|
|
@@ -90,7 +71,7 @@ export declare class RequestQueueBackend extends BaseClient implements storage.R
|
|
|
90
71
|
listItems(): Promise<storage.UpdateRequestSchema[]>;
|
|
91
72
|
toRequestQueueInfo(): storage.RequestQueueInfo;
|
|
92
73
|
private updateTimestamps;
|
|
93
|
-
private
|
|
94
|
-
private
|
|
95
|
-
private
|
|
74
|
+
private jsonToRequest;
|
|
75
|
+
private createInternalRequest;
|
|
76
|
+
private calculateOrderNo;
|
|
96
77
|
}
|