@crawlee/core 4.0.0-beta.17 → 4.0.0-beta.170
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 +14 -14
- package/autoscaling/autoscaled_pool.d.ts +73 -177
- package/autoscaling/autoscaled_pool.js +182 -329
- package/autoscaling/concurrency_system.d.ts +268 -0
- package/autoscaling/concurrency_system.js +364 -0
- package/autoscaling/cpu_load_signal.d.ts +43 -0
- package/autoscaling/cpu_load_signal.js +47 -0
- package/autoscaling/event_loop_load_signal.d.ts +51 -0
- package/autoscaling/event_loop_load_signal.js +60 -0
- package/autoscaling/index.d.ts +6 -1
- package/autoscaling/index.js +6 -1
- package/autoscaling/load_signal.d.ts +98 -0
- package/autoscaling/load_signal.js +103 -0
- package/autoscaling/memory_load_signal.d.ts +47 -0
- package/autoscaling/memory_load_signal.js +106 -0
- package/autoscaling/snapshotter.d.ts +58 -163
- package/autoscaling/snapshotter.js +45 -263
- package/autoscaling/storage_backend_load_signal.d.ts +56 -0
- package/autoscaling/storage_backend_load_signal.js +73 -0
- package/autoscaling/system_status.d.ts +70 -92
- package/autoscaling/system_status.js +92 -122
- package/autoscaling/weighted_avg.d.ts +5 -0
- package/autoscaling/weighted_avg.js +14 -0
- package/byte_utils.d.ts +17 -0
- package/byte_utils.js +42 -0
- package/configuration.d.ts +96 -223
- package/configuration.js +170 -222
- package/cookie_utils.d.ts +3 -2
- package/cookie_utils.js +18 -7
- package/crawlers/context_pipeline.d.ts +10 -1
- package/crawlers/context_pipeline.js +31 -8
- package/crawlers/crawler_commons.d.ts +128 -124
- package/crawlers/crawler_commons.js +1 -108
- package/crawlers/error_snapshotter.d.ts +2 -5
- package/crawlers/error_snapshotter.js +5 -6
- package/crawlers/error_tracker.d.ts +0 -1
- package/crawlers/error_tracker.js +0 -1
- package/crawlers/index.d.ts +1 -3
- package/crawlers/index.js +0 -3
- package/crawlers/internals/types.d.ts +0 -1
- package/crawlers/internals/types.js +0 -1
- package/crawlers/statistics.d.ts +194 -79
- package/crawlers/statistics.js +382 -201
- package/debug.d.ts +36 -0
- package/debug.js +70 -0
- package/enqueue_links/enqueue_links.d.ts +61 -154
- package/enqueue_links/enqueue_links.js +40 -232
- package/enqueue_links/index.d.ts +0 -1
- package/enqueue_links/index.js +0 -1
- package/enqueue_links/shared.d.ts +55 -28
- package/enqueue_links/shared.js +116 -69
- package/errors.d.ts +82 -4
- package/errors.js +101 -5
- package/events/event_manager.d.ts +35 -9
- package/events/event_manager.js +12 -13
- package/events/index.d.ts +0 -1
- package/events/index.js +0 -1
- package/events/local_event_manager.d.ts +15 -3
- package/events/local_event_manager.js +39 -13
- package/http.d.ts +9 -0
- package/http.js +28 -0
- package/index.d.ts +7 -5
- package/index.js +8 -4
- package/iterables.d.ts +79 -0
- package/iterables.js +134 -0
- package/log.d.ts +77 -1
- package/log.js +134 -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 +44 -0
- package/memory-storage/memory-storage.js +160 -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 +106 -0
- package/memory-storage/resource-clients/key-value-store.d.ts +63 -0
- package/memory-storage/resource-clients/key-value-store.js +199 -0
- package/memory-storage/resource-clients/request-queue.d.ts +77 -0
- package/memory-storage/resource-clients/request-queue.js +414 -0
- package/memory-storage/utils.d.ts +16 -0
- package/memory-storage/utils.js +41 -0
- package/owned_or_injected.d.ts +58 -0
- package/owned_or_injected.js +98 -0
- package/package.json +17 -16
- package/proxy_configuration.d.ts +22 -128
- package/proxy_configuration.js +32 -144
- package/recoverable_state.d.ts +84 -51
- package/recoverable_state.js +175 -72
- package/request.d.ts +57 -16
- package/request.js +130 -69
- package/router.d.ts +193 -21
- package/router.js +188 -43
- package/serialization.d.ts +0 -1
- package/serialization.js +15 -15
- package/service_locator.d.ts +165 -0
- package/service_locator.js +253 -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 +40 -90
- package/session_pool/session.js +103 -160
- package/session_pool/session_pool.d.ts +85 -101
- package/session_pool/session_pool.js +254 -210
- package/storages/batched_adds.d.ts +37 -0
- package/storages/batched_adds.js +73 -0
- package/storages/dataset.d.ts +114 -56
- package/storages/dataset.js +292 -149
- package/storages/index.d.ts +9 -9
- package/storages/index.js +7 -9
- package/storages/key_value_store.d.ts +186 -48
- package/storages/key_value_store.js +452 -170
- 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 +22 -0
- package/storages/request_dedup_cache.js +48 -0
- package/storages/request_list.d.ts +54 -129
- package/storages/request_list.js +272 -245
- package/storages/request_loader.d.ts +133 -0
- package/storages/request_loader.js +36 -0
- package/storages/request_manager.d.ts +109 -0
- package/storages/request_manager.js +1 -0
- package/storages/request_manager_tandem.d.ts +55 -23
- package/storages/request_manager_tandem.js +139 -57
- package/storages/request_queue.d.ts +286 -48
- package/storages/request_queue.js +757 -218
- package/storages/{sitemap_request_list.d.ts → sitemap_request_loader.d.ts} +43 -91
- package/storages/sitemap_request_loader.js +439 -0
- package/storages/storage_instance_manager.d.ts +87 -0
- package/storages/storage_instance_manager.js +256 -0
- package/storages/storage_stats.d.ts +48 -0
- package/storages/storage_stats.js +29 -0
- package/storages/throttling_request_manager.d.ts +223 -0
- package/storages/throttling_request_manager.js +835 -0
- package/storages/transaction.d.ts +260 -0
- package/storages/transaction.js +287 -0
- package/storages/utils.d.ts +59 -11
- package/storages/utils.js +75 -15
- 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 +0 -6
- package/typedefs.js +0 -1
- package/url.d.ts +9 -0
- package/url.js +11 -0
- package/validators.d.ts +23 -18
- package/validators.js +14 -18
- 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/context_pipeline.d.ts.map +0 -1
- package/crawlers/context_pipeline.js.map +0 -1
- package/crawlers/crawler_commons.d.ts.map +0 -1
- package/crawlers/crawler_commons.js.map +0 -1
- package/crawlers/crawler_utils.d.ts +0 -10
- package/crawlers/crawler_utils.d.ts.map +0 -1
- package/crawlers/crawler_utils.js +0 -12
- 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/internals/types.d.ts.map +0 -1
- package/crawlers/internals/types.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 -140
- package/http_clients/base-http-client.d.ts.map +0 -1
- package/http_clients/base-http-client.js +0 -40
- 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 -20
- package/http_clients/got-scraping-http-client.d.ts.map +0 -1
- package/http_clients/got-scraping-http-client.js +0 -85
- 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/recoverable_state.d.ts.map +0 -1
- package/recoverable_state.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 +0 -13
- package/storages/access_checking.d.ts.map +0 -1
- package/storages/access_checking.js +0 -14
- 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_list_adapter.d.ts +0 -58
- package/storages/request_list_adapter.d.ts.map +0 -1
- package/storages/request_list_adapter.js +0 -81
- package/storages/request_list_adapter.js.map +0 -1
- package/storages/request_manager_tandem.d.ts.map +0 -1
- package/storages/request_manager_tandem.js.map +0 -1
- package/storages/request_provider.d.ts +0 -371
- package/storages/request_provider.d.ts.map +0 -1
- package/storages/request_provider.js +0 -585
- 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 +0 -430
- 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/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
|
@@ -1,31 +1,74 @@
|
|
|
1
|
-
import { EventEmitter } from 'node:events';
|
|
2
1
|
import { AsyncQueue } from '@sapphire/async-queue';
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { RecoverableState } from '../recoverable_state.js';
|
|
4
|
+
import { serviceLocator } from '../service_locator.js';
|
|
6
5
|
import { KeyValueStore } from '../storages/key_value_store.js';
|
|
7
|
-
import {
|
|
6
|
+
import { parseArgument, schemas, validators } from '../validators.js';
|
|
7
|
+
import { MAX_POOL_SIZE, PERSIST_STATE_KEY } from './consts.js';
|
|
8
|
+
import { createDefaultSessionFingerprint } from './fingerprint.js';
|
|
8
9
|
import { Session } from './session.js';
|
|
10
|
+
const SESSION_REUSE_STRATEGIES = ['random', 'round-robin', 'use-until-failure'];
|
|
11
|
+
// `schemas.anyObject` passes values through by reference (object schemas return a pruned plain
|
|
12
|
+
// copy), so class instances like loggers keep their prototype.
|
|
13
|
+
const sessionPoolOptionsSchema = z.strictObject({
|
|
14
|
+
id: z.union([schemas.anyNumber, z.string()]).optional(),
|
|
15
|
+
maxPoolSize: schemas.anyNumber.default(MAX_POOL_SIZE),
|
|
16
|
+
persistStateKeyValueStoreId: z.string().optional(),
|
|
17
|
+
persistStateKey: z.string().optional(),
|
|
18
|
+
createSessionFunction: schemas.anyFunction.optional(),
|
|
19
|
+
sessionOptions: schemas.anyObject.default(() => ({})),
|
|
20
|
+
log: validators.logger.default(() => serviceLocator.getLogger()),
|
|
21
|
+
persistenceOptions: schemas.anyObject.default(() => ({ enable: true })),
|
|
22
|
+
sessionReuseStrategy: z.enum(SESSION_REUSE_STRATEGIES).default('random'),
|
|
23
|
+
});
|
|
24
|
+
const createSessionOptionsSchema = z.strictObject({
|
|
25
|
+
sessionOptions: schemas.anyObject.default(() => ({})),
|
|
26
|
+
});
|
|
27
|
+
// Only what the recreation reads is pinned down - the rest of a session's state is the session's own business.
|
|
28
|
+
const persistedSessionState = z.custom((value) => typeof value === 'object' &&
|
|
29
|
+
value !== null &&
|
|
30
|
+
typeof value.createdAt === 'string' &&
|
|
31
|
+
typeof value.expiresAt === 'string', 'Expected a persisted session with `createdAt` and `expiresAt` timestamps');
|
|
32
|
+
const persistedSessionPoolState = z.object({
|
|
33
|
+
usableSessionsCount: z.number(),
|
|
34
|
+
retiredSessionsCount: z.number(),
|
|
35
|
+
sessions: z.array(persistedSessionState),
|
|
36
|
+
});
|
|
37
|
+
const sessionPoolInternalState = z.object({
|
|
38
|
+
sessions: z.array(z.instanceof(Session)),
|
|
39
|
+
});
|
|
40
|
+
/**
|
|
41
|
+
* The conversion between the pool's sessions and its persisted record. Decoding recreates the sessions through
|
|
42
|
+
* the pool's factory and keeps only the usable ones, which is why the codec is built per pool.
|
|
43
|
+
*/
|
|
44
|
+
function buildSessionPoolStateCodec(pool) {
|
|
45
|
+
return z.codec(persistedSessionPoolState, sessionPoolInternalState, {
|
|
46
|
+
decode: async (record) => {
|
|
47
|
+
const sessions = [];
|
|
48
|
+
for (const sessionState of record.sessions) {
|
|
49
|
+
const session = await pool.recreateSession({
|
|
50
|
+
...sessionState,
|
|
51
|
+
createdAt: new Date(sessionState.createdAt),
|
|
52
|
+
expiresAt: new Date(sessionState.expiresAt),
|
|
53
|
+
});
|
|
54
|
+
if (session.isUsable()) {
|
|
55
|
+
sessions.push(session);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
pool.log.debug(`${sessions.length} active sessions loaded from KeyValueStore`);
|
|
59
|
+
return { sessions };
|
|
60
|
+
},
|
|
61
|
+
encode: ({ sessions }) => pool.toRecord(sessions),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
9
64
|
/**
|
|
10
65
|
* Handles the rotation, creation and persistence of user-like sessions.
|
|
11
66
|
* Creates a pool of {@link Session} instances, that are randomly rotated.
|
|
12
67
|
* When some session is marked as blocked, it is removed and new one is created instead (the pool never returns an unusable session).
|
|
13
68
|
* Learn more in the {@doclink guides/session-management | Session management guide}.
|
|
14
69
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* Session pool is already integrated into crawlers, and it can significantly improve your scraper
|
|
18
|
-
* performance with just 2 lines of code.
|
|
19
|
-
*
|
|
20
|
-
* **Example usage:**
|
|
21
|
-
*
|
|
22
|
-
* ```javascript
|
|
23
|
-
* const crawler = new CheerioCrawler({
|
|
24
|
-
* useSessionPool: true,
|
|
25
|
-
* persistCookiesPerSession: true,
|
|
26
|
-
* // ...
|
|
27
|
-
* })
|
|
28
|
-
* ```
|
|
70
|
+
* Session pool is already integrated into crawlers and is always active.
|
|
71
|
+
* All public methods are lazy-initialized — the pool initializes itself on first use.
|
|
29
72
|
*
|
|
30
73
|
* You can configure the pool with many options. See the {@link SessionPoolOptions}.
|
|
31
74
|
* Session pool is by default persisted in default {@link KeyValueStore}.
|
|
@@ -35,7 +78,7 @@ import { Session } from './session.js';
|
|
|
35
78
|
* **Advanced usage:**
|
|
36
79
|
*
|
|
37
80
|
* ```javascript
|
|
38
|
-
* const sessionPool =
|
|
81
|
+
* const sessionPool = new SessionPool({
|
|
39
82
|
* maxPoolSize: 25,
|
|
40
83
|
* sessionOptions:{
|
|
41
84
|
* maxAgeSecs: 10,
|
|
@@ -71,94 +114,94 @@ import { Session } from './session.js';
|
|
|
71
114
|
*
|
|
72
115
|
* @category Scaling
|
|
73
116
|
*/
|
|
74
|
-
export class SessionPool
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
events;
|
|
87
|
-
blockedStatusCodes;
|
|
88
|
-
persistenceOptions;
|
|
89
|
-
isInitialized = false;
|
|
90
|
-
queue = new AsyncQueue();
|
|
117
|
+
export class SessionPool {
|
|
118
|
+
static #nextId = 0;
|
|
119
|
+
id;
|
|
120
|
+
#log;
|
|
121
|
+
#maxPoolSize;
|
|
122
|
+
#createSessionFunction;
|
|
123
|
+
#sessionMap = new Map();
|
|
124
|
+
#sessionOptions;
|
|
125
|
+
#persistStateKeyValueStoreId;
|
|
126
|
+
#persistStateKey;
|
|
127
|
+
#persistenceOptions;
|
|
128
|
+
#sessionReuseStrategy;
|
|
91
129
|
/**
|
|
92
|
-
* @
|
|
130
|
+
* The sessions live in a {@link RecoverableState} that handles the loading, recreation and periodic
|
|
131
|
+
* persistence of the record under `persistStateKey`.
|
|
93
132
|
*/
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
persistenceOptions: ow.optional.object,
|
|
106
|
-
}));
|
|
107
|
-
const { maxPoolSize = MAX_POOL_SIZE, persistStateKeyValueStoreId, persistStateKey = PERSIST_STATE_KEY, createSessionFunction, sessionOptions = {}, blockedStatusCodes = BLOCKED_STATUS_CODES, log = defaultLog, persistenceOptions = {
|
|
108
|
-
enable: true,
|
|
109
|
-
}, } = options;
|
|
110
|
-
this.config = config;
|
|
111
|
-
this.blockedStatusCodes = blockedStatusCodes;
|
|
112
|
-
this.events = config.getEventManager();
|
|
113
|
-
this.log = log.child({ prefix: 'SessionPool' });
|
|
114
|
-
this.persistenceOptions = persistenceOptions;
|
|
133
|
+
#state;
|
|
134
|
+
#stateCodec;
|
|
135
|
+
#initPromise;
|
|
136
|
+
#queue = new AsyncQueue();
|
|
137
|
+
#roundRobinIndex = 0;
|
|
138
|
+
constructor(options = {}) {
|
|
139
|
+
const { id, maxPoolSize, persistStateKeyValueStoreId, persistStateKey, createSessionFunction, sessionOptions, log, persistenceOptions, sessionReuseStrategy, } = parseArgument(options, sessionPoolOptionsSchema);
|
|
140
|
+
this.id = id != null ? String(id) : String(SessionPool.#nextId++);
|
|
141
|
+
this.#sessionReuseStrategy = sessionReuseStrategy;
|
|
142
|
+
this.#log = log.child({ prefix: 'SessionPool' });
|
|
143
|
+
this.#persistenceOptions = persistenceOptions;
|
|
115
144
|
// Pool Configuration
|
|
116
|
-
this
|
|
117
|
-
this
|
|
118
|
-
// Session configuration
|
|
119
|
-
|
|
145
|
+
this.#maxPoolSize = maxPoolSize;
|
|
146
|
+
this.#createSessionFunction = createSessionFunction || this.defaultCreateSessionFunction;
|
|
147
|
+
// Session configuration. The pool-scoped logger is merged into per-call sessionOptions inside
|
|
148
|
+
// `invokeCreateSessionFunction`, so every Session inherits it without custom createSessionFunctions
|
|
149
|
+
// having to know about it.
|
|
150
|
+
this.#sessionOptions = {
|
|
120
151
|
...sessionOptions,
|
|
121
|
-
|
|
122
|
-
// and can't go inside _defaultCreateSessionFunction
|
|
123
|
-
log: this.log,
|
|
152
|
+
log: this.#log,
|
|
124
153
|
};
|
|
125
154
|
// Session keyValueStore
|
|
126
|
-
this
|
|
127
|
-
this
|
|
155
|
+
this.#persistStateKeyValueStoreId = persistStateKeyValueStoreId;
|
|
156
|
+
this.#persistStateKey = persistStateKey ?? `${PERSIST_STATE_KEY}_${this.id}`;
|
|
157
|
+
this.#stateCodec = buildSessionPoolStateCodec({
|
|
158
|
+
recreateSession: (sessionState) => this.invokeCreateSessionFunction(sessionState),
|
|
159
|
+
toRecord: (sessions) => this.#buildPersistedState(sessions),
|
|
160
|
+
log: this.#log,
|
|
161
|
+
});
|
|
162
|
+
this.#state = new RecoverableState({
|
|
163
|
+
defaultState: () => ({ sessions: [] }),
|
|
164
|
+
persistStateKey: this.#persistStateKey,
|
|
165
|
+
persistenceEnabled: !!this.#persistenceOptions.enable,
|
|
166
|
+
logger: this.#log,
|
|
167
|
+
keyValueStore: this.#persistenceOptions.enable && persistStateKeyValueStoreId
|
|
168
|
+
? KeyValueStore.open({ id: persistStateKeyValueStoreId }, { configuration: serviceLocator.getConfiguration() })
|
|
169
|
+
: undefined,
|
|
170
|
+
// The codec validates in the decode direction, so it is a Standard Schema as-is; encoding needs a call.
|
|
171
|
+
deserialize: this.#stateCodec,
|
|
172
|
+
serialize: (state) => this.#stateCodec.encode(state),
|
|
173
|
+
});
|
|
174
|
+
this.#state.reset();
|
|
128
175
|
}
|
|
129
176
|
/**
|
|
130
177
|
* Gets count of usable sessions in the pool.
|
|
131
178
|
*/
|
|
132
|
-
|
|
133
|
-
|
|
179
|
+
async usableSessionsCount() {
|
|
180
|
+
await this.ensureInitialized();
|
|
181
|
+
return this.#state.currentValue.sessions.filter((session) => session.isUsable()).length;
|
|
134
182
|
}
|
|
135
183
|
/**
|
|
136
184
|
* Gets count of retired sessions in the pool.
|
|
137
185
|
*/
|
|
138
|
-
|
|
139
|
-
|
|
186
|
+
async retiredSessionsCount() {
|
|
187
|
+
await this.ensureInitialized();
|
|
188
|
+
return this.#state.currentValue.sessions.filter((session) => !session.isUsable()).length;
|
|
140
189
|
}
|
|
141
190
|
/**
|
|
142
191
|
* Starts periodic state persistence and potentially loads SessionPool state from {@link KeyValueStore}.
|
|
143
|
-
*
|
|
192
|
+
* Called automatically on first use of any public method.
|
|
144
193
|
*/
|
|
145
|
-
async
|
|
146
|
-
if (this
|
|
147
|
-
|
|
194
|
+
async ensureInitialized() {
|
|
195
|
+
if (!this.#initPromise) {
|
|
196
|
+
this.#initPromise = this.setupPool();
|
|
148
197
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
this.log.debug(`No 'persistStateKeyValueStoreId' options specified, this session pool's data has been saved in the KeyValueStore with the id: ${this.keyValueStore.id}`);
|
|
198
|
+
return this.#initPromise;
|
|
199
|
+
}
|
|
200
|
+
async setupPool() {
|
|
201
|
+
await this.#state.initialize();
|
|
202
|
+
for (const session of this.#state.currentValue.sessions) {
|
|
203
|
+
this.#sessionMap.set(session.id, session);
|
|
156
204
|
}
|
|
157
|
-
// in case of migration happened and SessionPool state should be restored from the keyValueStore.
|
|
158
|
-
await this._maybeLoadSessionPool();
|
|
159
|
-
this._listener = this.persistState.bind(this);
|
|
160
|
-
this.events.on("persistState" /* EventType.PERSIST_STATE */, this._listener);
|
|
161
|
-
this.isInitialized = true;
|
|
162
205
|
}
|
|
163
206
|
/**
|
|
164
207
|
* Adds a new session to the session pool. The pool automatically creates sessions up to the maximum size of the pool,
|
|
@@ -167,20 +210,20 @@ export class SessionPool extends EventEmitter {
|
|
|
167
210
|
* @param [options] The configuration options for the session being added to the session pool.
|
|
168
211
|
*/
|
|
169
212
|
async addSession(options = {}) {
|
|
170
|
-
this.
|
|
213
|
+
await this.ensureInitialized();
|
|
171
214
|
const { id } = options;
|
|
172
215
|
if (id) {
|
|
173
|
-
const sessionExists = this
|
|
216
|
+
const sessionExists = this.#sessionMap.has(id);
|
|
174
217
|
if (sessionExists) {
|
|
175
218
|
throw new Error(`Cannot add session with id '${id}' as it already exists in the pool`);
|
|
176
219
|
}
|
|
177
220
|
}
|
|
178
|
-
if (!this.
|
|
179
|
-
this.
|
|
221
|
+
if (!this.hasSpaceForSession()) {
|
|
222
|
+
this.removeRetiredSessions();
|
|
180
223
|
}
|
|
181
|
-
const newSession = options instanceof Session ? options : await this.
|
|
182
|
-
this
|
|
183
|
-
this.
|
|
224
|
+
const newSession = options instanceof Session ? options : await this.invokeCreateSessionFunction(options);
|
|
225
|
+
this.#log.debug(`Adding new Session - ${newSession.id}`);
|
|
226
|
+
this.registerSession(newSession);
|
|
184
227
|
}
|
|
185
228
|
/**
|
|
186
229
|
* Adds a new session to the session pool. The pool automatically creates sessions up to the maximum size of the pool,
|
|
@@ -189,9 +232,9 @@ export class SessionPool extends EventEmitter {
|
|
|
189
232
|
* @param [options] The configuration options for the session being added to the session pool.
|
|
190
233
|
*/
|
|
191
234
|
async newSession(sessionOptions) {
|
|
192
|
-
this.
|
|
193
|
-
const newSession = await this.
|
|
194
|
-
this.
|
|
235
|
+
await this.ensureInitialized();
|
|
236
|
+
const newSession = await this.invokeCreateSessionFunction(sessionOptions);
|
|
237
|
+
this.registerSession(newSession);
|
|
195
238
|
return newSession;
|
|
196
239
|
}
|
|
197
240
|
/**
|
|
@@ -202,96 +245,99 @@ export class SessionPool extends EventEmitter {
|
|
|
202
245
|
* @param [sessionId] If provided, it returns the usable session with this id, `undefined` otherwise.
|
|
203
246
|
*/
|
|
204
247
|
async getSession(sessionId) {
|
|
205
|
-
await this.
|
|
248
|
+
await this.ensureInitialized();
|
|
249
|
+
await this.#queue.wait();
|
|
206
250
|
try {
|
|
207
|
-
this._throwIfNotInitialized();
|
|
208
251
|
if (sessionId) {
|
|
209
|
-
const session = this
|
|
210
|
-
if (session
|
|
252
|
+
const session = this.#sessionMap.get(sessionId);
|
|
253
|
+
if (session?.isUsable())
|
|
211
254
|
return session;
|
|
212
255
|
return undefined;
|
|
213
256
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
}
|
|
217
|
-
const pickedSession = this._pickSession();
|
|
218
|
-
if (pickedSession.isUsable()) {
|
|
257
|
+
const pickedSession = this.pickSession();
|
|
258
|
+
if (pickedSession)
|
|
219
259
|
return pickedSession;
|
|
260
|
+
if (this.hasSpaceForSession()) {
|
|
261
|
+
return await this.createSession();
|
|
220
262
|
}
|
|
221
|
-
this.
|
|
222
|
-
return await this.
|
|
263
|
+
this.removeRetiredSessions();
|
|
264
|
+
return await this.createSession();
|
|
223
265
|
}
|
|
224
266
|
finally {
|
|
225
|
-
this
|
|
267
|
+
this.#queue.shift();
|
|
226
268
|
}
|
|
227
269
|
}
|
|
228
270
|
/**
|
|
229
|
-
*
|
|
271
|
+
* Discards all sessions in the pool, both usable and retired, resetting it to a blank state.
|
|
272
|
+
*
|
|
273
|
+
* The persisted record is left alone - use {@link SessionPool.resetStore} to clear that as well.
|
|
230
274
|
*/
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
275
|
+
reset() {
|
|
276
|
+
this.#state.reset();
|
|
277
|
+
this.#sessionMap.clear();
|
|
278
|
+
this.#roundRobinIndex = 0;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Clears the persisted pool record, leaving the in-memory sessions alone.
|
|
282
|
+
*
|
|
283
|
+
* Throws while the state is still being persisted periodically - the next PERSIST_STATE event would write
|
|
284
|
+
* the record straight back. Call {@link SessionPool.teardown} first, or use {@link SessionPool.reset}
|
|
285
|
+
* to discard the sessions themselves. A no-op if persistence is disabled.
|
|
286
|
+
*/
|
|
287
|
+
async resetStore() {
|
|
288
|
+
await this.#state.resetStore();
|
|
236
289
|
}
|
|
237
290
|
/**
|
|
238
291
|
* Returns an object representing the internal state of the `SessionPool` instance.
|
|
239
|
-
*
|
|
292
|
+
* @internal
|
|
240
293
|
*/
|
|
241
|
-
getState() {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
retiredSessionsCount: this.retiredSessionsCount,
|
|
245
|
-
sessions: this.sessions.map((session) => session.getState()),
|
|
246
|
-
};
|
|
294
|
+
async getState() {
|
|
295
|
+
await this.ensureInitialized();
|
|
296
|
+
return this.#buildPersistedState(this.#state.currentValue.sessions);
|
|
247
297
|
}
|
|
248
298
|
/**
|
|
249
299
|
* Persists the current state of the `SessionPool` into the default {@link KeyValueStore}.
|
|
250
300
|
* The state is persisted automatically in regular intervals.
|
|
251
|
-
* @param options - Override the persistence options provided in the constructor
|
|
252
301
|
*/
|
|
253
|
-
async persistState(
|
|
254
|
-
if (!this
|
|
302
|
+
async persistState() {
|
|
303
|
+
if (!this.#persistenceOptions.enable) {
|
|
255
304
|
return;
|
|
256
305
|
}
|
|
257
|
-
this.
|
|
258
|
-
|
|
259
|
-
|
|
306
|
+
await this.ensureInitialized();
|
|
307
|
+
this.#log.debug('Persisting state', {
|
|
308
|
+
persistStateKeyValueStoreId: this.#persistStateKeyValueStoreId,
|
|
309
|
+
persistStateKey: this.#persistStateKey,
|
|
260
310
|
});
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
doNotRetryTimeouts: true,
|
|
268
|
-
})
|
|
269
|
-
.catch((error) => this.log.warning(`Failed to persist the session pool stats to ${this.persistStateKey}`, { error }));
|
|
311
|
+
await this.#state
|
|
312
|
+
.persistState()
|
|
313
|
+
.catch((error) => this.#log.warning(`Failed to persist the session pool stats to ${this.#persistStateKey}`, { error }));
|
|
314
|
+
}
|
|
315
|
+
async [Symbol.asyncDispose]() {
|
|
316
|
+
await this.teardown();
|
|
270
317
|
}
|
|
271
318
|
/**
|
|
272
|
-
*
|
|
319
|
+
* Stops the periodic state persistence and persists the state one last time.
|
|
273
320
|
* This function should be called after you are done with using the `SessionPool` instance.
|
|
321
|
+
* Using the pool again afterwards initializes it anew.
|
|
274
322
|
*/
|
|
275
323
|
async teardown() {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
if (!this.isInitialized)
|
|
284
|
-
throw new Error('SessionPool is not initialized.');
|
|
324
|
+
if (!this.#initPromise)
|
|
325
|
+
return;
|
|
326
|
+
await this.ensureInitialized();
|
|
327
|
+
await this.#state.teardown();
|
|
328
|
+
// The next use initializes the pool again, restoring the record and resuming the periodic persistence -
|
|
329
|
+
// a crawler that is run twice gets a persisting pool both times.
|
|
330
|
+
this.#initPromise = undefined;
|
|
285
331
|
}
|
|
286
332
|
/**
|
|
287
333
|
* Removes retired `Session` instances from `SessionPool`.
|
|
288
334
|
*/
|
|
289
|
-
|
|
290
|
-
this.sessions = this.sessions.filter((storedSession) => {
|
|
335
|
+
removeRetiredSessions() {
|
|
336
|
+
this.#state.currentValue.sessions = this.#state.currentValue.sessions.filter((storedSession) => {
|
|
291
337
|
if (storedSession.isUsable())
|
|
292
338
|
return true;
|
|
293
|
-
this
|
|
294
|
-
this
|
|
339
|
+
this.#sessionMap.delete(storedSession.id);
|
|
340
|
+
this.#log.debug(`Removed Session - ${storedSession.id}`);
|
|
295
341
|
return false;
|
|
296
342
|
});
|
|
297
343
|
}
|
|
@@ -299,89 +345,87 @@ export class SessionPool extends EventEmitter {
|
|
|
299
345
|
* Adds `Session` instance to `SessionPool`.
|
|
300
346
|
* @param newSession `Session` instance to be added.
|
|
301
347
|
*/
|
|
302
|
-
|
|
303
|
-
this.sessions.push(newSession);
|
|
304
|
-
this
|
|
348
|
+
registerSession(newSession) {
|
|
349
|
+
this.#state.currentValue.sessions.push(newSession);
|
|
350
|
+
this.#sessionMap.set(newSession.id, newSession);
|
|
305
351
|
}
|
|
306
352
|
/**
|
|
307
353
|
* Gets random index.
|
|
308
354
|
*/
|
|
309
|
-
|
|
310
|
-
return Math.floor(Math.random() * this.sessions.length);
|
|
355
|
+
getRandomIndex() {
|
|
356
|
+
return Math.floor(Math.random() * this.#state.currentValue.sessions.length);
|
|
311
357
|
}
|
|
312
358
|
/**
|
|
313
359
|
* Creates new session without any extra behavior.
|
|
314
|
-
* @param sessionPool
|
|
315
360
|
* @param [options]
|
|
316
361
|
* @param [options.sessionOptions] The configuration options for the session being created.
|
|
317
362
|
* @returns New session.
|
|
318
363
|
*/
|
|
319
|
-
async
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
364
|
+
async defaultCreateSessionFunction(options = {}) {
|
|
365
|
+
const { sessionOptions } = parseArgument(options, createSessionOptionsSchema);
|
|
366
|
+
return new Session(sessionOptions);
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Invokes `createSessionFunction` with `sessionOptions` already merged from pool-wide defaults and
|
|
370
|
+
* the supplied per-call overrides, so custom implementations don't need to spread `pool.sessionOptions` themselves.
|
|
371
|
+
*
|
|
372
|
+
* A default {@link SessionFingerprint} is generated up front (host OS as
|
|
373
|
+
* `platform`, a random valid `browser`/`device` for that platform). Pool-wide
|
|
374
|
+
* and per-call options override it, and a persisted fingerprint coming
|
|
375
|
+
* through `maybeLoadSessionPool` naturally wins because it arrives in
|
|
376
|
+
* `perCallOptions`.
|
|
377
|
+
*/
|
|
378
|
+
async invokeCreateSessionFunction(perCallOptions) {
|
|
379
|
+
const sessionOptions = {
|
|
380
|
+
fingerprint: createDefaultSessionFingerprint(),
|
|
381
|
+
...this.#sessionOptions,
|
|
382
|
+
...perCallOptions,
|
|
383
|
+
};
|
|
384
|
+
return this.#createSessionFunction({ sessionOptions });
|
|
327
385
|
}
|
|
328
386
|
/**
|
|
329
387
|
* Creates new session and adds it to the pool.
|
|
330
388
|
* @returns Newly created `Session` instance.
|
|
331
389
|
*/
|
|
332
|
-
async
|
|
333
|
-
const newSession = await this.
|
|
334
|
-
this.
|
|
335
|
-
this
|
|
390
|
+
async createSession() {
|
|
391
|
+
const newSession = await this.invokeCreateSessionFunction();
|
|
392
|
+
this.registerSession(newSession);
|
|
393
|
+
this.#log.debug(`Created new Session - ${newSession.id}`);
|
|
336
394
|
return newSession;
|
|
337
395
|
}
|
|
338
396
|
/**
|
|
339
397
|
* Decides whether there is enough space for creating new session.
|
|
340
398
|
*/
|
|
341
|
-
|
|
342
|
-
return this.sessions.length < this
|
|
399
|
+
hasSpaceForSession() {
|
|
400
|
+
return this.#state.currentValue.sessions.length < this.#maxPoolSize;
|
|
343
401
|
}
|
|
344
402
|
/**
|
|
345
|
-
* Picks
|
|
346
|
-
*
|
|
403
|
+
* Picks a session from the `SessionPool` according to the configured `sessionReuseStrategy`.
|
|
404
|
+
* Returns `undefined` when no session should be reused and a new one should be created instead.
|
|
347
405
|
*/
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
// Invalidate old sessions and load active sessions only
|
|
360
|
-
this.log.debug('Recreating state from KeyValueStore', {
|
|
361
|
-
persistStateKeyValueStoreId: this.persistStateKeyValueStoreId,
|
|
362
|
-
persistStateKey: this.persistStateKey,
|
|
363
|
-
});
|
|
364
|
-
for (const sessionObject of loadedSessionPool.sessions) {
|
|
365
|
-
sessionObject.sessionPool = this;
|
|
366
|
-
sessionObject.createdAt = new Date(sessionObject.createdAt);
|
|
367
|
-
sessionObject.expiresAt = new Date(sessionObject.expiresAt);
|
|
368
|
-
const recreatedSession = await this.createSessionFunction(this, { sessionOptions: sessionObject });
|
|
369
|
-
if (recreatedSession.isUsable()) {
|
|
370
|
-
this._addSession(recreatedSession);
|
|
371
|
-
}
|
|
406
|
+
pickSession() {
|
|
407
|
+
if (this.#sessionReuseStrategy !== 'use-until-failure' && this.hasSpaceForSession())
|
|
408
|
+
return undefined;
|
|
409
|
+
if (this.#sessionReuseStrategy === 'use-until-failure') {
|
|
410
|
+
return this.#state.currentValue.sessions.find((session) => session.isUsable());
|
|
411
|
+
}
|
|
412
|
+
let picked;
|
|
413
|
+
if (this.#sessionReuseStrategy === 'round-robin') {
|
|
414
|
+
const index = this.#roundRobinIndex % this.#state.currentValue.sessions.length;
|
|
415
|
+
this.#roundRobinIndex = index + 1;
|
|
416
|
+
picked = this.#state.currentValue.sessions[index];
|
|
372
417
|
}
|
|
373
|
-
|
|
418
|
+
else {
|
|
419
|
+
picked = this.#state.currentValue.sessions[this.getRandomIndex()];
|
|
420
|
+
}
|
|
421
|
+
return picked.isUsable() ? picked : undefined;
|
|
374
422
|
}
|
|
375
|
-
/**
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
const sessionPool = new SessionPool(options, config);
|
|
383
|
-
await sessionPool.initialize();
|
|
384
|
-
return sessionPool;
|
|
423
|
+
/** The persisted form of the pool, which is also what {@link SessionPool.getState} returns. */
|
|
424
|
+
#buildPersistedState(sessions) {
|
|
425
|
+
return {
|
|
426
|
+
usableSessionsCount: sessions.filter((session) => session.isUsable()).length,
|
|
427
|
+
retiredSessionsCount: sessions.filter((session) => !session.isUsable()).length,
|
|
428
|
+
sessions: sessions.map((session) => session.getState()),
|
|
429
|
+
};
|
|
385
430
|
}
|
|
386
431
|
}
|
|
387
|
-
//# sourceMappingURL=session_pool.js.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ProcessedRequest } from '@crawlee/types';
|
|
2
|
+
import type { Source } from '../request.js';
|
|
3
|
+
import type { AddRequestsBatchedResult } from './request_queue.js';
|
|
4
|
+
export interface DrainRequestBatchesOptions<TItem extends Source> {
|
|
5
|
+
/**
|
|
6
|
+
* The requests to add, already normalized by the caller. Consumed lazily: an unbounded or expensive
|
|
7
|
+
* iterable is only pulled from as far as the batching (and any `maxNewRequests` budget) requires.
|
|
8
|
+
*/
|
|
9
|
+
items: AsyncGenerator<TItem>;
|
|
10
|
+
batchSize: number;
|
|
11
|
+
waitBetweenBatchesMillis: number;
|
|
12
|
+
waitForAllRequestsToBeAdded: boolean;
|
|
13
|
+
maxNewRequests?: number;
|
|
14
|
+
/**
|
|
15
|
+
* Adds a single chunk and reports what it processed.
|
|
16
|
+
*
|
|
17
|
+
* @param isInitial Whether this is the first chunk, which is added before this function returns. Later
|
|
18
|
+
* chunks land in the background, which is why some callers cache only the first.
|
|
19
|
+
*/
|
|
20
|
+
processChunk: (chunk: TItem[], isInitial: boolean) => Promise<ProcessedRequest[]>;
|
|
21
|
+
/**
|
|
22
|
+
* Called with the promise covering every chunk after the first, so the caller can keep its own
|
|
23
|
+
* `isFinished` honest while batches are still landing.
|
|
24
|
+
*/
|
|
25
|
+
trackBackgroundBatches?: (batches: Promise<unknown>) => void;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Drives the chunk-by-chunk half of `addRequestsBatched`: the first chunk is added before returning and the
|
|
29
|
+
* rest continue in the background, paced by `waitBetweenBatchesMillis`.
|
|
30
|
+
*
|
|
31
|
+
* Callers differ only in how a chunk is added and how the input is normalized, so that is all
|
|
32
|
+
* {@link DrainRequestBatchesOptions} asks for - the budget arithmetic, the lazy chunking, the
|
|
33
|
+
* over-limit reporting and the transaction handling are identical for everyone and live here. In
|
|
34
|
+
* particular, every caller has to keep its background chunks out of a transaction they will outlive,
|
|
35
|
+
* so that is read from the ambient transaction rather than asked of the caller.
|
|
36
|
+
*/
|
|
37
|
+
export declare function drainRequestBatches<TItem extends Source>(options: DrainRequestBatchesOptions<TItem>): Promise<AddRequestsBatchedResult>;
|