@crawlee/core 4.0.0-beta.8 → 4.0.0-beta.80
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -13
- package/autoscaling/autoscaled_pool.d.ts +5 -6
- package/autoscaling/autoscaled_pool.js +12 -11
- package/autoscaling/client_load_signal.d.ts +25 -0
- package/autoscaling/client_load_signal.js +36 -0
- package/autoscaling/cpu_load_signal.d.ts +28 -0
- package/autoscaling/cpu_load_signal.js +24 -0
- package/autoscaling/event_loop_load_signal.d.ts +23 -0
- package/autoscaling/event_loop_load_signal.js +35 -0
- package/autoscaling/index.d.ts +5 -1
- package/autoscaling/index.js +5 -1
- package/autoscaling/load_signal.d.ts +99 -0
- package/autoscaling/load_signal.js +135 -0
- package/autoscaling/memory_load_signal.d.ts +43 -0
- package/autoscaling/memory_load_signal.js +102 -0
- package/autoscaling/snapshotter.d.ts +35 -72
- package/autoscaling/snapshotter.js +78 -181
- package/autoscaling/system_status.d.ts +21 -36
- package/autoscaling/system_status.js +46 -84
- package/configuration.d.ts +88 -228
- package/configuration.js +162 -224
- package/cookie_utils.d.ts +4 -3
- package/cookie_utils.js +20 -13
- package/crawlers/context_pipeline.d.ts +70 -0
- package/crawlers/context_pipeline.js +122 -0
- package/crawlers/crawler_commons.d.ts +20 -30
- package/crawlers/crawler_commons.js +12 -21
- package/crawlers/crawler_utils.d.ts +2 -3
- package/crawlers/crawler_utils.js +1 -2
- package/crawlers/error_snapshotter.d.ts +3 -3
- package/crawlers/error_snapshotter.js +2 -3
- package/crawlers/error_tracker.d.ts +2 -2
- package/crawlers/error_tracker.js +0 -1
- package/crawlers/index.d.ts +1 -2
- package/crawlers/index.js +1 -2
- package/crawlers/internals/types.d.ts +7 -0
- package/crawlers/internals/types.js +1 -0
- package/crawlers/statistics.d.ts +22 -17
- package/crawlers/statistics.js +30 -26
- package/enqueue_links/enqueue_links.d.ts +44 -22
- package/enqueue_links/enqueue_links.js +51 -30
- package/enqueue_links/index.d.ts +0 -1
- package/enqueue_links/index.js +0 -1
- package/enqueue_links/shared.d.ts +25 -9
- package/enqueue_links/shared.js +69 -38
- package/errors.d.ts +53 -4
- package/errors.js +70 -5
- package/events/event_manager.d.ts +34 -8
- package/events/event_manager.js +8 -10
- package/events/index.d.ts +0 -1
- package/events/index.js +0 -1
- package/events/local_event_manager.d.ts +15 -5
- package/events/local_event_manager.js +37 -40
- package/index.d.ts +4 -3
- package/index.js +3 -2
- package/log.d.ts +82 -3
- package/log.js +102 -1
- package/memory-storage/consts.d.ts +4 -0
- package/memory-storage/consts.js +4 -0
- package/memory-storage/index.d.ts +1 -0
- package/memory-storage/index.js +1 -0
- package/memory-storage/memory-storage.d.ts +46 -0
- package/memory-storage/memory-storage.js +136 -0
- package/memory-storage/resource-clients/common/base-client.d.ts +4 -0
- package/memory-storage/resource-clients/common/base-client.js +6 -0
- package/memory-storage/resource-clients/dataset.d.ts +40 -0
- package/memory-storage/resource-clients/dataset.js +113 -0
- package/memory-storage/resource-clients/key-value-store.d.ts +63 -0
- package/memory-storage/resource-clients/key-value-store.js +203 -0
- package/memory-storage/resource-clients/request-queue.d.ts +96 -0
- package/memory-storage/resource-clients/request-queue.js +421 -0
- package/memory-storage/utils.d.ts +17 -0
- package/memory-storage/utils.js +42 -0
- package/package.json +12 -10
- package/proxy_configuration.d.ts +14 -149
- package/proxy_configuration.js +19 -168
- package/recoverable_state.d.ts +120 -0
- package/recoverable_state.js +141 -0
- package/request.d.ts +84 -13
- package/request.js +107 -28
- package/router.d.ts +117 -15
- package/router.js +124 -30
- package/serialization.d.ts +0 -1
- package/serialization.js +1 -2
- package/service_locator.d.ts +156 -0
- package/service_locator.js +238 -0
- package/session_pool/consts.d.ts +1 -2
- package/session_pool/consts.js +1 -2
- package/session_pool/errors.d.ts +0 -1
- package/session_pool/errors.js +0 -1
- package/session_pool/fingerprint.d.ts +9 -0
- package/session_pool/fingerprint.js +30 -0
- package/session_pool/index.d.ts +0 -2
- package/session_pool/index.js +0 -2
- package/session_pool/session.d.ts +35 -73
- package/session_pool/session.js +46 -99
- package/session_pool/session_pool.d.ts +71 -72
- package/session_pool/session_pool.js +111 -100
- package/storages/access_checking.d.ts +0 -1
- package/storages/access_checking.js +5 -2
- package/storages/dataset.d.ts +101 -51
- package/storages/dataset.js +168 -126
- package/storages/index.d.ts +8 -7
- package/storages/index.js +6 -7
- package/storages/key_value_store.d.ts +165 -36
- package/storages/key_value_store.js +269 -122
- package/storages/key_value_store_codec.d.ts +32 -0
- package/storages/key_value_store_codec.js +113 -0
- package/storages/request_dedup_cache.d.ts +23 -0
- package/storages/request_dedup_cache.js +48 -0
- package/storages/request_list.d.ts +38 -82
- package/storages/request_list.js +65 -41
- package/storages/request_loader.d.ts +96 -0
- package/storages/request_loader.js +1 -0
- package/storages/request_manager.d.ts +33 -0
- package/storages/request_manager.js +1 -0
- package/storages/request_manager_tandem.d.ts +106 -0
- package/storages/request_manager_tandem.js +197 -0
- package/storages/request_queue.d.ts +303 -46
- package/storages/request_queue.js +629 -214
- package/storages/{sitemap_request_list.d.ts → sitemap_request_loader.d.ts} +28 -24
- package/storages/{sitemap_request_list.js → sitemap_request_loader.js} +49 -46
- package/storages/storage_instance_manager.d.ts +91 -0
- package/storages/storage_instance_manager.js +258 -0
- package/storages/storage_stats.d.ts +48 -0
- package/storages/storage_stats.js +29 -0
- package/storages/utils.d.ts +51 -6
- package/storages/utils.js +60 -9
- package/system-info/cpu-info.d.ts +67 -0
- package/system-info/cpu-info.js +216 -0
- package/system-info/memory-info.d.ts +31 -0
- package/system-info/memory-info.js +115 -0
- package/system-info/ps-tree.d.ts +17 -0
- package/system-info/ps-tree.js +144 -0
- package/system-info/runtime.d.ts +14 -0
- package/system-info/runtime.js +80 -0
- package/typedefs.d.ts +1 -2
- package/typedefs.js +0 -1
- package/validators.d.ts +8 -1
- package/validators.js +8 -1
- package/autoscaling/autoscaled_pool.d.ts.map +0 -1
- package/autoscaling/autoscaled_pool.js.map +0 -1
- package/autoscaling/index.d.ts.map +0 -1
- package/autoscaling/index.js.map +0 -1
- package/autoscaling/snapshotter.d.ts.map +0 -1
- package/autoscaling/snapshotter.js.map +0 -1
- package/autoscaling/system_status.d.ts.map +0 -1
- package/autoscaling/system_status.js.map +0 -1
- package/configuration.d.ts.map +0 -1
- package/configuration.js.map +0 -1
- package/cookie_utils.d.ts.map +0 -1
- package/cookie_utils.js.map +0 -1
- package/crawlers/crawler_commons.d.ts.map +0 -1
- package/crawlers/crawler_commons.js.map +0 -1
- package/crawlers/crawler_extension.d.ts +0 -12
- package/crawlers/crawler_extension.d.ts.map +0 -1
- package/crawlers/crawler_extension.js +0 -14
- package/crawlers/crawler_extension.js.map +0 -1
- package/crawlers/crawler_utils.d.ts.map +0 -1
- package/crawlers/crawler_utils.js.map +0 -1
- package/crawlers/error_snapshotter.d.ts.map +0 -1
- package/crawlers/error_snapshotter.js.map +0 -1
- package/crawlers/error_tracker.d.ts.map +0 -1
- package/crawlers/error_tracker.js.map +0 -1
- package/crawlers/index.d.ts.map +0 -1
- package/crawlers/index.js.map +0 -1
- package/crawlers/statistics.d.ts.map +0 -1
- package/crawlers/statistics.js.map +0 -1
- package/enqueue_links/enqueue_links.d.ts.map +0 -1
- package/enqueue_links/enqueue_links.js.map +0 -1
- package/enqueue_links/index.d.ts.map +0 -1
- package/enqueue_links/index.js.map +0 -1
- package/enqueue_links/shared.d.ts.map +0 -1
- package/enqueue_links/shared.js.map +0 -1
- package/errors.d.ts.map +0 -1
- package/errors.js.map +0 -1
- package/events/event_manager.d.ts.map +0 -1
- package/events/event_manager.js.map +0 -1
- package/events/index.d.ts.map +0 -1
- package/events/index.js.map +0 -1
- package/events/local_event_manager.d.ts.map +0 -1
- package/events/local_event_manager.js.map +0 -1
- package/http_clients/base-http-client.d.ts +0 -134
- package/http_clients/base-http-client.d.ts.map +0 -1
- package/http_clients/base-http-client.js +0 -33
- package/http_clients/base-http-client.js.map +0 -1
- package/http_clients/form-data-like.d.ts +0 -67
- package/http_clients/form-data-like.d.ts.map +0 -1
- package/http_clients/form-data-like.js +0 -5
- package/http_clients/form-data-like.js.map +0 -1
- package/http_clients/got-scraping-http-client.d.ts +0 -15
- package/http_clients/got-scraping-http-client.d.ts.map +0 -1
- package/http_clients/got-scraping-http-client.js +0 -69
- package/http_clients/got-scraping-http-client.js.map +0 -1
- package/http_clients/index.d.ts +0 -3
- package/http_clients/index.d.ts.map +0 -1
- package/http_clients/index.js +0 -3
- package/http_clients/index.js.map +0 -1
- package/index.d.ts.map +0 -1
- package/index.js.map +0 -1
- package/log.d.ts.map +0 -1
- package/log.js.map +0 -1
- package/proxy_configuration.d.ts.map +0 -1
- package/proxy_configuration.js.map +0 -1
- package/request.d.ts.map +0 -1
- package/request.js.map +0 -1
- package/router.d.ts.map +0 -1
- package/router.js.map +0 -1
- package/serialization.d.ts.map +0 -1
- package/serialization.js.map +0 -1
- package/session_pool/consts.d.ts.map +0 -1
- package/session_pool/consts.js.map +0 -1
- package/session_pool/errors.d.ts.map +0 -1
- package/session_pool/errors.js.map +0 -1
- package/session_pool/events.d.ts +0 -3
- package/session_pool/events.d.ts.map +0 -1
- package/session_pool/events.js +0 -3
- package/session_pool/events.js.map +0 -1
- package/session_pool/index.d.ts.map +0 -1
- package/session_pool/index.js.map +0 -1
- package/session_pool/session.d.ts.map +0 -1
- package/session_pool/session.js.map +0 -1
- package/session_pool/session_pool.d.ts.map +0 -1
- package/session_pool/session_pool.js.map +0 -1
- package/storages/access_checking.d.ts.map +0 -1
- package/storages/access_checking.js.map +0 -1
- package/storages/dataset.d.ts.map +0 -1
- package/storages/dataset.js.map +0 -1
- package/storages/index.d.ts.map +0 -1
- package/storages/index.js.map +0 -1
- package/storages/key_value_store.d.ts.map +0 -1
- package/storages/key_value_store.js.map +0 -1
- package/storages/request_list.d.ts.map +0 -1
- package/storages/request_list.js.map +0 -1
- package/storages/request_provider.d.ts +0 -307
- package/storages/request_provider.d.ts.map +0 -1
- package/storages/request_provider.js +0 -555
- package/storages/request_provider.js.map +0 -1
- package/storages/request_queue.d.ts.map +0 -1
- package/storages/request_queue.js.map +0 -1
- package/storages/request_queue_v2.d.ts +0 -87
- package/storages/request_queue_v2.d.ts.map +0 -1
- package/storages/request_queue_v2.js +0 -438
- package/storages/request_queue_v2.js.map +0 -1
- package/storages/sitemap_request_list.d.ts.map +0 -1
- package/storages/sitemap_request_list.js.map +0 -1
- package/storages/storage_manager.d.ts +0 -58
- package/storages/storage_manager.d.ts.map +0 -1
- package/storages/storage_manager.js +0 -105
- package/storages/storage_manager.js.map +0 -1
- package/storages/utils.d.ts.map +0 -1
- package/storages/utils.js.map +0 -1
- package/tsconfig.build.tsbuildinfo +0 -1
- package/typedefs.d.ts.map +0 -1
- package/typedefs.js.map +0 -1
- package/validators.d.ts.map +0 -1
- package/validators.js.map +0 -1
|
@@ -1,31 +1,19 @@
|
|
|
1
|
-
import { EventEmitter } from 'node:events';
|
|
2
1
|
import { AsyncQueue } from '@sapphire/async-queue';
|
|
3
2
|
import ow from 'ow';
|
|
4
|
-
import {
|
|
5
|
-
import { log as defaultLog } from '../log.js';
|
|
3
|
+
import { serviceLocator } from '../service_locator.js';
|
|
6
4
|
import { KeyValueStore } from '../storages/key_value_store.js';
|
|
7
|
-
import {
|
|
5
|
+
import { MAX_POOL_SIZE, PERSIST_STATE_KEY } from './consts.js';
|
|
6
|
+
import { createDefaultSessionFingerprint } from './fingerprint.js';
|
|
8
7
|
import { Session } from './session.js';
|
|
8
|
+
const SESSION_REUSE_STRATEGIES = ['random', 'round-robin', 'use-until-failure'];
|
|
9
9
|
/**
|
|
10
10
|
* Handles the rotation, creation and persistence of user-like sessions.
|
|
11
11
|
* Creates a pool of {@link Session} instances, that are randomly rotated.
|
|
12
12
|
* When some session is marked as blocked, it is removed and new one is created instead (the pool never returns an unusable session).
|
|
13
13
|
* Learn more in the {@doclink guides/session-management | Session management guide}.
|
|
14
14
|
*
|
|
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
|
-
* ```
|
|
15
|
+
* Session pool is already integrated into crawlers and is always active.
|
|
16
|
+
* All public methods are lazy-initialized — the pool initializes itself on first use.
|
|
29
17
|
*
|
|
30
18
|
* You can configure the pool with many options. See the {@link SessionPoolOptions}.
|
|
31
19
|
* Session pool is by default persisted in default {@link KeyValueStore}.
|
|
@@ -35,7 +23,7 @@ import { Session } from './session.js';
|
|
|
35
23
|
* **Advanced usage:**
|
|
36
24
|
*
|
|
37
25
|
* ```javascript
|
|
38
|
-
* const sessionPool =
|
|
26
|
+
* const sessionPool = new SessionPool({
|
|
39
27
|
* maxPoolSize: 25,
|
|
40
28
|
* sessionOptions:{
|
|
41
29
|
* maxAgeSecs: 10,
|
|
@@ -71,8 +59,9 @@ import { Session } from './session.js';
|
|
|
71
59
|
*
|
|
72
60
|
* @category Scaling
|
|
73
61
|
*/
|
|
74
|
-
export class SessionPool
|
|
75
|
-
|
|
62
|
+
export class SessionPool {
|
|
63
|
+
static nextId = 0;
|
|
64
|
+
id;
|
|
76
65
|
log;
|
|
77
66
|
maxPoolSize;
|
|
78
67
|
createSessionFunction;
|
|
@@ -84,73 +73,76 @@ export class SessionPool extends EventEmitter {
|
|
|
84
73
|
persistStateKey;
|
|
85
74
|
_listener;
|
|
86
75
|
events;
|
|
87
|
-
blockedStatusCodes;
|
|
88
76
|
persistenceOptions;
|
|
89
|
-
|
|
77
|
+
sessionReuseStrategy;
|
|
78
|
+
initPromise;
|
|
90
79
|
queue = new AsyncQueue();
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
*/
|
|
94
|
-
constructor(options = {}, config = Configuration.getGlobalConfig()) {
|
|
95
|
-
super();
|
|
96
|
-
this.config = config;
|
|
80
|
+
roundRobinIndex = 0;
|
|
81
|
+
constructor(options = {}) {
|
|
97
82
|
ow(options, ow.object.exactShape({
|
|
83
|
+
id: ow.optional.any(ow.number, ow.string),
|
|
98
84
|
maxPoolSize: ow.optional.number,
|
|
99
85
|
persistStateKeyValueStoreId: ow.optional.string,
|
|
100
86
|
persistStateKey: ow.optional.string,
|
|
101
87
|
createSessionFunction: ow.optional.function,
|
|
102
88
|
sessionOptions: ow.optional.object,
|
|
103
|
-
blockedStatusCodes: ow.optional.array.ofType(ow.number),
|
|
104
89
|
log: ow.optional.object,
|
|
105
90
|
persistenceOptions: ow.optional.object,
|
|
91
|
+
sessionReuseStrategy: ow.optional.string.oneOf([...SESSION_REUSE_STRATEGIES]),
|
|
106
92
|
}));
|
|
107
|
-
const { maxPoolSize = MAX_POOL_SIZE, persistStateKeyValueStoreId, persistStateKey
|
|
93
|
+
const { id, maxPoolSize = MAX_POOL_SIZE, persistStateKeyValueStoreId, persistStateKey, createSessionFunction, sessionOptions = {}, log = serviceLocator.getLogger(), persistenceOptions = {
|
|
108
94
|
enable: true,
|
|
109
|
-
}, } = options;
|
|
110
|
-
this.
|
|
111
|
-
this.
|
|
112
|
-
this.events =
|
|
95
|
+
}, sessionReuseStrategy = 'random', } = options;
|
|
96
|
+
this.id = id != null ? String(id) : String(SessionPool.nextId++);
|
|
97
|
+
this.sessionReuseStrategy = sessionReuseStrategy;
|
|
98
|
+
this.events = serviceLocator.getEventManager();
|
|
113
99
|
this.log = log.child({ prefix: 'SessionPool' });
|
|
114
100
|
this.persistenceOptions = persistenceOptions;
|
|
115
101
|
// Pool Configuration
|
|
116
102
|
this.maxPoolSize = maxPoolSize;
|
|
117
103
|
this.createSessionFunction = createSessionFunction || this._defaultCreateSessionFunction;
|
|
118
|
-
// Session configuration
|
|
104
|
+
// Session configuration. The pool-scoped logger is merged into per-call sessionOptions inside
|
|
105
|
+
// `_invokeCreateSessionFunction`, so every Session inherits it without custom createSessionFunctions
|
|
106
|
+
// having to know about it.
|
|
119
107
|
this.sessionOptions = {
|
|
120
108
|
...sessionOptions,
|
|
121
|
-
// the log needs to propagate to createSessionFunction as in "new Session({ ...sessionPool.sessionOptions })"
|
|
122
|
-
// and can't go inside _defaultCreateSessionFunction
|
|
123
109
|
log: this.log,
|
|
124
110
|
};
|
|
125
111
|
// Session keyValueStore
|
|
126
112
|
this.persistStateKeyValueStoreId = persistStateKeyValueStoreId;
|
|
127
|
-
this.persistStateKey = persistStateKey
|
|
113
|
+
this.persistStateKey = persistStateKey ?? `${PERSIST_STATE_KEY}_${this.id}`;
|
|
128
114
|
}
|
|
129
115
|
/**
|
|
130
116
|
* Gets count of usable sessions in the pool.
|
|
131
117
|
*/
|
|
132
|
-
|
|
118
|
+
async usableSessionsCount() {
|
|
119
|
+
await this.ensureInitialized();
|
|
133
120
|
return this.sessions.filter((session) => session.isUsable()).length;
|
|
134
121
|
}
|
|
135
122
|
/**
|
|
136
123
|
* Gets count of retired sessions in the pool.
|
|
137
124
|
*/
|
|
138
|
-
|
|
125
|
+
async retiredSessionsCount() {
|
|
126
|
+
await this.ensureInitialized();
|
|
139
127
|
return this.sessions.filter((session) => !session.isUsable()).length;
|
|
140
128
|
}
|
|
141
129
|
/**
|
|
142
130
|
* Starts periodic state persistence and potentially loads SessionPool state from {@link KeyValueStore}.
|
|
143
|
-
*
|
|
131
|
+
* Called automatically on first use of any public method.
|
|
144
132
|
*/
|
|
145
|
-
async
|
|
146
|
-
if (this.
|
|
147
|
-
|
|
133
|
+
async ensureInitialized() {
|
|
134
|
+
if (!this.initPromise) {
|
|
135
|
+
this.initPromise = this.setupPool();
|
|
148
136
|
}
|
|
149
|
-
|
|
137
|
+
return this.initPromise;
|
|
138
|
+
}
|
|
139
|
+
async setupPool() {
|
|
150
140
|
if (!this.persistenceOptions.enable) {
|
|
151
|
-
this.isInitialized = true;
|
|
152
141
|
return;
|
|
153
142
|
}
|
|
143
|
+
this.keyValueStore = await KeyValueStore.open(this.persistStateKeyValueStoreId ? { id: this.persistStateKeyValueStoreId } : null, {
|
|
144
|
+
config: serviceLocator.getConfiguration(),
|
|
145
|
+
});
|
|
154
146
|
if (!this.persistStateKeyValueStoreId) {
|
|
155
147
|
this.log.debug(`No 'persistStateKeyValueStoreId' options specified, this session pool's data has been saved in the KeyValueStore with the id: ${this.keyValueStore.id}`);
|
|
156
148
|
}
|
|
@@ -158,7 +150,6 @@ export class SessionPool extends EventEmitter {
|
|
|
158
150
|
await this._maybeLoadSessionPool();
|
|
159
151
|
this._listener = this.persistState.bind(this);
|
|
160
152
|
this.events.on("persistState" /* EventType.PERSIST_STATE */, this._listener);
|
|
161
|
-
this.isInitialized = true;
|
|
162
153
|
}
|
|
163
154
|
/**
|
|
164
155
|
* Adds a new session to the session pool. The pool automatically creates sessions up to the maximum size of the pool,
|
|
@@ -167,7 +158,7 @@ export class SessionPool extends EventEmitter {
|
|
|
167
158
|
* @param [options] The configuration options for the session being added to the session pool.
|
|
168
159
|
*/
|
|
169
160
|
async addSession(options = {}) {
|
|
170
|
-
this.
|
|
161
|
+
await this.ensureInitialized();
|
|
171
162
|
const { id } = options;
|
|
172
163
|
if (id) {
|
|
173
164
|
const sessionExists = this.sessionMap.has(id);
|
|
@@ -178,10 +169,22 @@ export class SessionPool extends EventEmitter {
|
|
|
178
169
|
if (!this._hasSpaceForSession()) {
|
|
179
170
|
this._removeRetiredSessions();
|
|
180
171
|
}
|
|
181
|
-
const newSession = options instanceof Session ? options : await this.
|
|
172
|
+
const newSession = options instanceof Session ? options : await this._invokeCreateSessionFunction(options);
|
|
182
173
|
this.log.debug(`Adding new Session - ${newSession.id}`);
|
|
183
174
|
this._addSession(newSession);
|
|
184
175
|
}
|
|
176
|
+
/**
|
|
177
|
+
* Adds a new session to the session pool. The pool automatically creates sessions up to the maximum size of the pool,
|
|
178
|
+
* but this allows you to add more sessions once the max pool size is reached.
|
|
179
|
+
* This also allows you to add session with overridden session options (e.g. with specific session id).
|
|
180
|
+
* @param [options] The configuration options for the session being added to the session pool.
|
|
181
|
+
*/
|
|
182
|
+
async newSession(sessionOptions) {
|
|
183
|
+
await this.ensureInitialized();
|
|
184
|
+
const newSession = await this._invokeCreateSessionFunction(sessionOptions);
|
|
185
|
+
this._addSession(newSession);
|
|
186
|
+
return newSession;
|
|
187
|
+
}
|
|
185
188
|
/**
|
|
186
189
|
* Gets session.
|
|
187
190
|
* If there is space for new session, it creates and returns new session.
|
|
@@ -190,21 +193,20 @@ export class SessionPool extends EventEmitter {
|
|
|
190
193
|
* @param [sessionId] If provided, it returns the usable session with this id, `undefined` otherwise.
|
|
191
194
|
*/
|
|
192
195
|
async getSession(sessionId) {
|
|
196
|
+
await this.ensureInitialized();
|
|
193
197
|
await this.queue.wait();
|
|
194
198
|
try {
|
|
195
|
-
this._throwIfNotInitialized();
|
|
196
199
|
if (sessionId) {
|
|
197
200
|
const session = this.sessionMap.get(sessionId);
|
|
198
|
-
if (session
|
|
201
|
+
if (session?.isUsable())
|
|
199
202
|
return session;
|
|
200
203
|
return undefined;
|
|
201
204
|
}
|
|
202
|
-
if (this._hasSpaceForSession()) {
|
|
203
|
-
return await this._createSession();
|
|
204
|
-
}
|
|
205
205
|
const pickedSession = this._pickSession();
|
|
206
|
-
if (pickedSession
|
|
206
|
+
if (pickedSession)
|
|
207
207
|
return pickedSession;
|
|
208
|
+
if (this._hasSpaceForSession()) {
|
|
209
|
+
return await this._createSession();
|
|
208
210
|
}
|
|
209
211
|
this._removeRetiredSessions();
|
|
210
212
|
return await this._createSession();
|
|
@@ -220,16 +222,18 @@ export class SessionPool extends EventEmitter {
|
|
|
220
222
|
if (!this.persistenceOptions.enable && !options?.enable) {
|
|
221
223
|
return;
|
|
222
224
|
}
|
|
225
|
+
await this.ensureInitialized();
|
|
223
226
|
await this.keyValueStore?.setValue(this.persistStateKey, null);
|
|
224
227
|
}
|
|
225
228
|
/**
|
|
226
229
|
* Returns an object representing the internal state of the `SessionPool` instance.
|
|
227
230
|
* Note that the object's fields can change in future releases.
|
|
228
231
|
*/
|
|
229
|
-
getState() {
|
|
232
|
+
async getState() {
|
|
233
|
+
await this.ensureInitialized();
|
|
230
234
|
return {
|
|
231
|
-
usableSessionsCount: this.usableSessionsCount,
|
|
232
|
-
retiredSessionsCount: this.retiredSessionsCount,
|
|
235
|
+
usableSessionsCount: await this.usableSessionsCount(),
|
|
236
|
+
retiredSessionsCount: await this.retiredSessionsCount(),
|
|
233
237
|
sessions: this.sessions.map((session) => session.getState()),
|
|
234
238
|
};
|
|
235
239
|
}
|
|
@@ -242,18 +246,13 @@ export class SessionPool extends EventEmitter {
|
|
|
242
246
|
if (!this.persistenceOptions.enable && !options?.enable) {
|
|
243
247
|
return;
|
|
244
248
|
}
|
|
249
|
+
await this.ensureInitialized();
|
|
245
250
|
this.log.debug('Persisting state', {
|
|
246
251
|
persistStateKeyValueStoreId: this.persistStateKeyValueStoreId,
|
|
247
252
|
persistStateKey: this.persistStateKey,
|
|
248
253
|
});
|
|
249
|
-
// use half the interval of `persistState` to avoid race conditions
|
|
250
|
-
const persistStateIntervalMillis = this.config.get('persistStateIntervalMillis');
|
|
251
|
-
const timeoutSecs = persistStateIntervalMillis / 2_000;
|
|
252
254
|
await this.keyValueStore
|
|
253
|
-
|
|
254
|
-
timeoutSecs,
|
|
255
|
-
doNotRetryTimeouts: true,
|
|
256
|
-
})
|
|
255
|
+
?.setValue(this.persistStateKey, await this.getState())
|
|
257
256
|
.catch((error) => this.log.warning(`Failed to persist the session pool stats to ${this.persistStateKey}`, { error }));
|
|
258
257
|
}
|
|
259
258
|
/**
|
|
@@ -261,16 +260,14 @@ export class SessionPool extends EventEmitter {
|
|
|
261
260
|
* This function should be called after you are done with using the `SessionPool` instance.
|
|
262
261
|
*/
|
|
263
262
|
async teardown() {
|
|
264
|
-
|
|
263
|
+
if (!this.initPromise)
|
|
264
|
+
return;
|
|
265
|
+
await this.ensureInitialized();
|
|
266
|
+
if (this._listener) {
|
|
267
|
+
this.events.off("persistState" /* EventType.PERSIST_STATE */, this._listener);
|
|
268
|
+
}
|
|
265
269
|
await this.persistState();
|
|
266
270
|
}
|
|
267
|
-
/**
|
|
268
|
-
* SessionPool should not work before initialization.
|
|
269
|
-
*/
|
|
270
|
-
_throwIfNotInitialized() {
|
|
271
|
-
if (!this.isInitialized)
|
|
272
|
-
throw new Error('SessionPool is not initialized.');
|
|
273
|
-
}
|
|
274
271
|
/**
|
|
275
272
|
* Removes retired `Session` instances from `SessionPool`.
|
|
276
273
|
*/
|
|
@@ -299,26 +296,39 @@ export class SessionPool extends EventEmitter {
|
|
|
299
296
|
}
|
|
300
297
|
/**
|
|
301
298
|
* Creates new session without any extra behavior.
|
|
302
|
-
* @param sessionPool
|
|
303
299
|
* @param [options]
|
|
304
300
|
* @param [options.sessionOptions] The configuration options for the session being created.
|
|
305
301
|
* @returns New session.
|
|
306
302
|
*/
|
|
307
|
-
_defaultCreateSessionFunction(
|
|
303
|
+
async _defaultCreateSessionFunction(options = {}) {
|
|
308
304
|
ow(options, ow.object.exactShape({ sessionOptions: ow.optional.object }));
|
|
309
305
|
const { sessionOptions = {} } = options;
|
|
310
|
-
return new Session(
|
|
306
|
+
return new Session(sessionOptions);
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Invokes `createSessionFunction` with `sessionOptions` already merged from pool-wide defaults and
|
|
310
|
+
* the supplied per-call overrides, so custom implementations don't need to spread `pool.sessionOptions` themselves.
|
|
311
|
+
*
|
|
312
|
+
* A default {@link SessionFingerprint} is generated up front (host OS as
|
|
313
|
+
* `platform`, a random valid `browser`/`device` for that platform). Pool-wide
|
|
314
|
+
* and per-call options override it, and a persisted fingerprint coming
|
|
315
|
+
* through `_maybeLoadSessionPool` naturally wins because it arrives in
|
|
316
|
+
* `perCallOptions`.
|
|
317
|
+
*/
|
|
318
|
+
async _invokeCreateSessionFunction(perCallOptions) {
|
|
319
|
+
const sessionOptions = {
|
|
320
|
+
fingerprint: createDefaultSessionFingerprint(),
|
|
311
321
|
...this.sessionOptions,
|
|
312
|
-
...
|
|
313
|
-
|
|
314
|
-
});
|
|
322
|
+
...perCallOptions,
|
|
323
|
+
};
|
|
324
|
+
return this.createSessionFunction({ sessionOptions });
|
|
315
325
|
}
|
|
316
326
|
/**
|
|
317
327
|
* Creates new session and adds it to the pool.
|
|
318
328
|
* @returns Newly created `Session` instance.
|
|
319
329
|
*/
|
|
320
330
|
async _createSession() {
|
|
321
|
-
const newSession = await this.
|
|
331
|
+
const newSession = await this._invokeCreateSessionFunction();
|
|
322
332
|
this._addSession(newSession);
|
|
323
333
|
this.log.debug(`Created new Session - ${newSession.id}`);
|
|
324
334
|
return newSession;
|
|
@@ -330,18 +340,32 @@ export class SessionPool extends EventEmitter {
|
|
|
330
340
|
return this.sessions.length < this.maxPoolSize;
|
|
331
341
|
}
|
|
332
342
|
/**
|
|
333
|
-
* Picks
|
|
334
|
-
*
|
|
343
|
+
* Picks a session from the `SessionPool` according to the configured `sessionReuseStrategy`.
|
|
344
|
+
* Returns `undefined` when no session should be reused and a new one should be created instead.
|
|
335
345
|
*/
|
|
336
346
|
_pickSession() {
|
|
337
|
-
|
|
347
|
+
if (this.sessionReuseStrategy !== 'use-until-failure' && this._hasSpaceForSession())
|
|
348
|
+
return undefined;
|
|
349
|
+
if (this.sessionReuseStrategy === 'use-until-failure') {
|
|
350
|
+
return this.sessions.find((session) => session.isUsable());
|
|
351
|
+
}
|
|
352
|
+
let picked;
|
|
353
|
+
if (this.sessionReuseStrategy === 'round-robin') {
|
|
354
|
+
const index = this.roundRobinIndex % this.sessions.length;
|
|
355
|
+
this.roundRobinIndex = index + 1;
|
|
356
|
+
picked = this.sessions[index];
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
picked = this.sessions[this._getRandomIndex()];
|
|
360
|
+
}
|
|
361
|
+
return picked.isUsable() ? picked : undefined;
|
|
338
362
|
}
|
|
339
363
|
/**
|
|
340
364
|
* Potentially loads `SessionPool`.
|
|
341
365
|
* If the state was persisted it loads the `SessionPool` from the persisted state.
|
|
342
366
|
*/
|
|
343
367
|
async _maybeLoadSessionPool() {
|
|
344
|
-
const loadedSessionPool = await this.keyValueStore
|
|
368
|
+
const loadedSessionPool = await this.keyValueStore?.getValue(this.persistStateKey);
|
|
345
369
|
if (!loadedSessionPool)
|
|
346
370
|
return;
|
|
347
371
|
// Invalidate old sessions and load active sessions only
|
|
@@ -350,26 +374,13 @@ export class SessionPool extends EventEmitter {
|
|
|
350
374
|
persistStateKey: this.persistStateKey,
|
|
351
375
|
});
|
|
352
376
|
for (const sessionObject of loadedSessionPool.sessions) {
|
|
353
|
-
sessionObject.sessionPool = this;
|
|
354
377
|
sessionObject.createdAt = new Date(sessionObject.createdAt);
|
|
355
378
|
sessionObject.expiresAt = new Date(sessionObject.expiresAt);
|
|
356
|
-
const recreatedSession = await this.
|
|
379
|
+
const recreatedSession = await this._invokeCreateSessionFunction(sessionObject);
|
|
357
380
|
if (recreatedSession.isUsable()) {
|
|
358
381
|
this._addSession(recreatedSession);
|
|
359
382
|
}
|
|
360
383
|
}
|
|
361
|
-
this.log.debug(`${this.
|
|
362
|
-
}
|
|
363
|
-
/**
|
|
364
|
-
* Opens a SessionPool and returns a promise resolving to an instance
|
|
365
|
-
* of the {@link SessionPool} class that is already initialized.
|
|
366
|
-
*
|
|
367
|
-
* For more details and code examples, see the {@link SessionPool} class.
|
|
368
|
-
*/
|
|
369
|
-
static async open(options, config) {
|
|
370
|
-
const sessionPool = new SessionPool(options, config);
|
|
371
|
-
await sessionPool.initialize();
|
|
372
|
-
return sessionPool;
|
|
384
|
+
this.log.debug(`${this.sessions.length} active sessions loaded from KeyValueStore`);
|
|
373
385
|
}
|
|
374
386
|
}
|
|
375
|
-
//# sourceMappingURL=session_pool.js.map
|
|
@@ -10,4 +10,3 @@ export declare const checkStorageAccess: () => void | undefined;
|
|
|
10
10
|
* @param callback The code that should be invoked with the `checkFunction` setting
|
|
11
11
|
*/
|
|
12
12
|
export declare const withCheckedStorageAccess: <T>(checkFunction: () => void, callback: () => Awaitable<T>) => Promise<T>;
|
|
13
|
-
//# sourceMappingURL=access_checking.d.ts.map
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
import { tryCancel } from '@apify/timeout';
|
|
2
3
|
const storage = new AsyncLocalStorage();
|
|
3
4
|
/**
|
|
4
5
|
* Invoke a storage access checker function defined using {@link withCheckedStorageAccess} higher up in the call stack.
|
|
5
6
|
*/
|
|
6
|
-
export const checkStorageAccess = () =>
|
|
7
|
+
export const checkStorageAccess = () => {
|
|
8
|
+
tryCancel();
|
|
9
|
+
return storage.getStore()?.checkFunction();
|
|
10
|
+
};
|
|
7
11
|
/**
|
|
8
12
|
* Define a storage access checker function that should be used by calls to {@link checkStorageAccess} in the callbacks.
|
|
9
13
|
*
|
|
@@ -11,4 +15,3 @@ export const checkStorageAccess = () => storage.getStore()?.checkFunction();
|
|
|
11
15
|
* @param callback The code that should be invoked with the `checkFunction` setting
|
|
12
16
|
*/
|
|
13
17
|
export const withCheckedStorageAccess = async (checkFunction, callback) => storage.run({ checkFunction }, callback);
|
|
14
|
-
//# sourceMappingURL=access_checking.js.map
|