@crawlee/core 4.0.0-beta.11 → 4.0.0-beta.111
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 +67 -172
- package/autoscaling/autoscaled_pool.js +165 -320
- package/autoscaling/client_load_signal.d.ts +55 -0
- package/autoscaling/client_load_signal.js +73 -0
- package/autoscaling/concurrency_system.d.ts +268 -0
- package/autoscaling/concurrency_system.js +351 -0
- package/autoscaling/cpu_load_signal.d.ts +43 -0
- package/autoscaling/cpu_load_signal.js +46 -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 +99 -0
- package/autoscaling/load_signal.js +104 -0
- package/autoscaling/memory_load_signal.d.ts +47 -0
- package/autoscaling/memory_load_signal.js +105 -0
- package/autoscaling/snapshotter.d.ts +58 -163
- package/autoscaling/snapshotter.js +45 -263
- package/autoscaling/system_status.d.ts +62 -84
- 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 +4 -3
- package/cookie_utils.js +22 -13
- package/crawlers/context_pipeline.d.ts +10 -1
- package/crawlers/context_pipeline.js +31 -8
- package/crawlers/crawler_commons.d.ts +90 -83
- package/crawlers/crawler_commons.js +1 -116
- package/crawlers/error_snapshotter.d.ts +1 -4
- package/crawlers/error_snapshotter.js +3 -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 +91 -41
- package/crawlers/statistics.js +83 -72
- package/debug.d.ts +36 -0
- package/debug.js +70 -0
- package/enqueue_links/enqueue_links.d.ts +59 -68
- package/enqueue_links/enqueue_links.js +57 -62
- package/enqueue_links/index.d.ts +0 -1
- package/enqueue_links/index.js +0 -1
- package/enqueue_links/shared.d.ts +40 -27
- package/enqueue_links/shared.js +90 -68
- package/errors.d.ts +41 -4
- package/errors.js +50 -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 -3
- package/events/local_event_manager.js +37 -11
- package/index.d.ts +6 -4
- package/index.js +5 -3
- package/iterables.d.ts +79 -0
- package/iterables.js +134 -0
- package/log.d.ts +82 -3
- package/log.js +106 -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 +42 -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 +114 -0
- package/memory-storage/resource-clients/key-value-store.d.ts +63 -0
- package/memory-storage/resource-clients/key-value-store.js +204 -0
- package/memory-storage/resource-clients/request-queue.d.ts +77 -0
- package/memory-storage/resource-clients/request-queue.js +422 -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 +13 -12
- package/proxy_configuration.d.ts +27 -152
- package/proxy_configuration.js +27 -179
- package/recoverable_state.d.ts +111 -0
- package/recoverable_state.js +143 -0
- package/request.d.ts +86 -17
- package/request.js +117 -41
- package/router.d.ts +193 -21
- package/router.js +188 -43
- package/serialization.d.ts +0 -1
- package/serialization.js +9 -11
- package/service_locator.d.ts +147 -0
- package/service_locator.js +244 -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 +37 -86
- package/session_pool/session.js +85 -138
- package/session_pool/session_pool.d.ts +77 -91
- package/session_pool/session_pool.js +159 -147
- package/storages/dataset.d.ts +114 -54
- package/storages/dataset.js +285 -144
- package/storages/index.d.ts +9 -8
- package/storages/index.js +7 -8
- package/storages/key_value_store.d.ts +185 -42
- package/storages/key_value_store.js +424 -151
- 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 +52 -116
- package/storages/request_list.js +158 -133
- 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 +97 -0
- package/storages/request_manager_tandem.js +197 -0
- package/storages/request_queue.d.ts +290 -47
- package/storages/request_queue.js +825 -216
- package/storages/{sitemap_request_list.d.ts → sitemap_request_loader.d.ts} +37 -88
- package/storages/{sitemap_request_list.js → sitemap_request_loader.js} +136 -143
- 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/transaction.d.ts +252 -0
- package/storages/transaction.js +251 -0
- package/storages/utils.d.ts +54 -9
- package/storages/utils.js +64 -13
- 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/validators.d.ts +8 -1
- package/validators.js +10 -3
- 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 -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 +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_provider.d.ts +0 -308
- 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,94 +59,98 @@ import { Session } from './session.js';
|
|
|
71
59
|
*
|
|
72
60
|
* @category Scaling
|
|
73
61
|
*/
|
|
74
|
-
export class SessionPool
|
|
75
|
-
|
|
76
|
-
|
|
62
|
+
export class SessionPool {
|
|
63
|
+
static #nextId = 0;
|
|
64
|
+
id;
|
|
65
|
+
#log;
|
|
66
|
+
#sessions = [];
|
|
67
|
+
// kept as TS-private: session_pool tests read/override the members below directly
|
|
77
68
|
maxPoolSize;
|
|
78
69
|
createSessionFunction;
|
|
79
70
|
keyValueStore;
|
|
80
|
-
sessions = [];
|
|
81
71
|
sessionMap = new Map();
|
|
82
72
|
sessionOptions;
|
|
83
73
|
persistStateKeyValueStoreId;
|
|
84
74
|
persistStateKey;
|
|
85
|
-
|
|
86
|
-
events;
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
queue = new AsyncQueue();
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
*/
|
|
94
|
-
constructor(options = {}, config = Configuration.getGlobalConfig()) {
|
|
95
|
-
super();
|
|
96
|
-
this.config = config;
|
|
75
|
+
#listener;
|
|
76
|
+
#events;
|
|
77
|
+
#persistenceOptions;
|
|
78
|
+
#sessionReuseStrategy;
|
|
79
|
+
#initPromise;
|
|
80
|
+
#queue = new AsyncQueue();
|
|
81
|
+
#roundRobinIndex = 0;
|
|
82
|
+
constructor(options = {}) {
|
|
97
83
|
ow(options, ow.object.exactShape({
|
|
84
|
+
id: ow.optional.any(ow.number, ow.string),
|
|
98
85
|
maxPoolSize: ow.optional.number,
|
|
99
86
|
persistStateKeyValueStoreId: ow.optional.string,
|
|
100
87
|
persistStateKey: ow.optional.string,
|
|
101
88
|
createSessionFunction: ow.optional.function,
|
|
102
89
|
sessionOptions: ow.optional.object,
|
|
103
|
-
blockedStatusCodes: ow.optional.array.ofType(ow.number),
|
|
104
90
|
log: ow.optional.object,
|
|
105
91
|
persistenceOptions: ow.optional.object,
|
|
92
|
+
sessionReuseStrategy: ow.optional.string.oneOf([...SESSION_REUSE_STRATEGIES]),
|
|
106
93
|
}));
|
|
107
|
-
const { maxPoolSize = MAX_POOL_SIZE, persistStateKeyValueStoreId, persistStateKey
|
|
94
|
+
const { id, maxPoolSize = MAX_POOL_SIZE, persistStateKeyValueStoreId, persistStateKey, createSessionFunction, sessionOptions = {}, log = serviceLocator.getLogger(), persistenceOptions = {
|
|
108
95
|
enable: true,
|
|
109
|
-
}, } = options;
|
|
110
|
-
this.
|
|
111
|
-
this
|
|
112
|
-
this
|
|
113
|
-
this
|
|
114
|
-
this
|
|
96
|
+
}, sessionReuseStrategy = 'random', } = options;
|
|
97
|
+
this.id = id != null ? String(id) : String(SessionPool.#nextId++);
|
|
98
|
+
this.#sessionReuseStrategy = sessionReuseStrategy;
|
|
99
|
+
this.#events = serviceLocator.getEventManager();
|
|
100
|
+
this.#log = log.child({ prefix: 'SessionPool' });
|
|
101
|
+
this.#persistenceOptions = persistenceOptions;
|
|
115
102
|
// Pool Configuration
|
|
116
103
|
this.maxPoolSize = maxPoolSize;
|
|
117
|
-
this.createSessionFunction = createSessionFunction || this.
|
|
118
|
-
// Session configuration
|
|
104
|
+
this.createSessionFunction = createSessionFunction || this.defaultCreateSessionFunction;
|
|
105
|
+
// Session configuration. The pool-scoped logger is merged into per-call sessionOptions inside
|
|
106
|
+
// `invokeCreateSessionFunction`, so every Session inherits it without custom createSessionFunctions
|
|
107
|
+
// having to know about it.
|
|
119
108
|
this.sessionOptions = {
|
|
120
109
|
...sessionOptions,
|
|
121
|
-
|
|
122
|
-
// and can't go inside _defaultCreateSessionFunction
|
|
123
|
-
log: this.log,
|
|
110
|
+
log: this.#log,
|
|
124
111
|
};
|
|
125
112
|
// Session keyValueStore
|
|
126
113
|
this.persistStateKeyValueStoreId = persistStateKeyValueStoreId;
|
|
127
|
-
this.persistStateKey = persistStateKey
|
|
114
|
+
this.persistStateKey = persistStateKey ?? `${PERSIST_STATE_KEY}_${this.id}`;
|
|
128
115
|
}
|
|
129
116
|
/**
|
|
130
117
|
* Gets count of usable sessions in the pool.
|
|
131
118
|
*/
|
|
132
|
-
|
|
133
|
-
|
|
119
|
+
async usableSessionsCount() {
|
|
120
|
+
await this.ensureInitialized();
|
|
121
|
+
return this.#sessions.filter((session) => session.isUsable()).length;
|
|
134
122
|
}
|
|
135
123
|
/**
|
|
136
124
|
* Gets count of retired sessions in the pool.
|
|
137
125
|
*/
|
|
138
|
-
|
|
139
|
-
|
|
126
|
+
async retiredSessionsCount() {
|
|
127
|
+
await this.ensureInitialized();
|
|
128
|
+
return this.#sessions.filter((session) => !session.isUsable()).length;
|
|
140
129
|
}
|
|
141
130
|
/**
|
|
142
131
|
* Starts periodic state persistence and potentially loads SessionPool state from {@link KeyValueStore}.
|
|
143
|
-
*
|
|
132
|
+
* Called automatically on first use of any public method.
|
|
144
133
|
*/
|
|
145
|
-
async
|
|
146
|
-
if (this
|
|
147
|
-
|
|
134
|
+
async ensureInitialized() {
|
|
135
|
+
if (!this.#initPromise) {
|
|
136
|
+
this.#initPromise = this.setupPool();
|
|
148
137
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
138
|
+
return this.#initPromise;
|
|
139
|
+
}
|
|
140
|
+
async setupPool() {
|
|
141
|
+
if (!this.#persistenceOptions.enable) {
|
|
152
142
|
return;
|
|
153
143
|
}
|
|
144
|
+
this.keyValueStore = await KeyValueStore.open(this.persistStateKeyValueStoreId ? { id: this.persistStateKeyValueStoreId } : null, {
|
|
145
|
+
configuration: serviceLocator.getConfiguration(),
|
|
146
|
+
});
|
|
154
147
|
if (!this.persistStateKeyValueStoreId) {
|
|
155
|
-
this
|
|
148
|
+
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
149
|
}
|
|
157
150
|
// in case of migration happened and SessionPool state should be restored from the keyValueStore.
|
|
158
|
-
await this.
|
|
159
|
-
this
|
|
160
|
-
this
|
|
161
|
-
this.isInitialized = true;
|
|
151
|
+
await this.maybeLoadSessionPool();
|
|
152
|
+
this.#listener = this.persistState.bind(this);
|
|
153
|
+
this.#events.on("persistState" /* EventType.PERSIST_STATE */, this.#listener);
|
|
162
154
|
}
|
|
163
155
|
/**
|
|
164
156
|
* Adds a new session to the session pool. The pool automatically creates sessions up to the maximum size of the pool,
|
|
@@ -167,7 +159,7 @@ export class SessionPool extends EventEmitter {
|
|
|
167
159
|
* @param [options] The configuration options for the session being added to the session pool.
|
|
168
160
|
*/
|
|
169
161
|
async addSession(options = {}) {
|
|
170
|
-
this.
|
|
162
|
+
await this.ensureInitialized();
|
|
171
163
|
const { id } = options;
|
|
172
164
|
if (id) {
|
|
173
165
|
const sessionExists = this.sessionMap.has(id);
|
|
@@ -175,12 +167,24 @@ export class SessionPool extends EventEmitter {
|
|
|
175
167
|
throw new Error(`Cannot add session with id '${id}' as it already exists in the pool`);
|
|
176
168
|
}
|
|
177
169
|
}
|
|
178
|
-
if (!this.
|
|
179
|
-
this.
|
|
170
|
+
if (!this.hasSpaceForSession()) {
|
|
171
|
+
this.removeRetiredSessions();
|
|
180
172
|
}
|
|
181
|
-
const newSession = options instanceof Session ? options : await this.
|
|
182
|
-
this
|
|
183
|
-
this.
|
|
173
|
+
const newSession = options instanceof Session ? options : await this.invokeCreateSessionFunction(options);
|
|
174
|
+
this.#log.debug(`Adding new Session - ${newSession.id}`);
|
|
175
|
+
this.registerSession(newSession);
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Adds a new session to the session pool. The pool automatically creates sessions up to the maximum size of the pool,
|
|
179
|
+
* but this allows you to add more sessions once the max pool size is reached.
|
|
180
|
+
* This also allows you to add session with overridden session options (e.g. with specific session id).
|
|
181
|
+
* @param [options] The configuration options for the session being added to the session pool.
|
|
182
|
+
*/
|
|
183
|
+
async newSession(sessionOptions) {
|
|
184
|
+
await this.ensureInitialized();
|
|
185
|
+
const newSession = await this.invokeCreateSessionFunction(sessionOptions);
|
|
186
|
+
this.registerSession(newSession);
|
|
187
|
+
return newSession;
|
|
184
188
|
}
|
|
185
189
|
/**
|
|
186
190
|
* Gets session.
|
|
@@ -190,47 +194,48 @@ export class SessionPool extends EventEmitter {
|
|
|
190
194
|
* @param [sessionId] If provided, it returns the usable session with this id, `undefined` otherwise.
|
|
191
195
|
*/
|
|
192
196
|
async getSession(sessionId) {
|
|
193
|
-
await this.
|
|
197
|
+
await this.ensureInitialized();
|
|
198
|
+
await this.#queue.wait();
|
|
194
199
|
try {
|
|
195
|
-
this._throwIfNotInitialized();
|
|
196
200
|
if (sessionId) {
|
|
197
201
|
const session = this.sessionMap.get(sessionId);
|
|
198
|
-
if (session
|
|
202
|
+
if (session?.isUsable())
|
|
199
203
|
return session;
|
|
200
204
|
return undefined;
|
|
201
205
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
}
|
|
205
|
-
const pickedSession = this._pickSession();
|
|
206
|
-
if (pickedSession.isUsable()) {
|
|
206
|
+
const pickedSession = this.pickSession();
|
|
207
|
+
if (pickedSession)
|
|
207
208
|
return pickedSession;
|
|
209
|
+
if (this.hasSpaceForSession()) {
|
|
210
|
+
return await this.createSession();
|
|
208
211
|
}
|
|
209
|
-
this.
|
|
210
|
-
return await this.
|
|
212
|
+
this.removeRetiredSessions();
|
|
213
|
+
return await this.createSession();
|
|
211
214
|
}
|
|
212
215
|
finally {
|
|
213
|
-
this
|
|
216
|
+
this.#queue.shift();
|
|
214
217
|
}
|
|
215
218
|
}
|
|
216
219
|
/**
|
|
217
220
|
* @param options - Override the persistence options provided in the constructor
|
|
218
221
|
*/
|
|
219
222
|
async resetStore(options) {
|
|
220
|
-
if (!this
|
|
223
|
+
if (!this.#persistenceOptions.enable && !options?.enable) {
|
|
221
224
|
return;
|
|
222
225
|
}
|
|
226
|
+
await this.ensureInitialized();
|
|
223
227
|
await this.keyValueStore?.setValue(this.persistStateKey, null);
|
|
224
228
|
}
|
|
225
229
|
/**
|
|
226
230
|
* Returns an object representing the internal state of the `SessionPool` instance.
|
|
227
231
|
* Note that the object's fields can change in future releases.
|
|
228
232
|
*/
|
|
229
|
-
getState() {
|
|
233
|
+
async getState() {
|
|
234
|
+
await this.ensureInitialized();
|
|
230
235
|
return {
|
|
231
|
-
usableSessionsCount: this.usableSessionsCount,
|
|
232
|
-
retiredSessionsCount: this.retiredSessionsCount,
|
|
233
|
-
sessions: this
|
|
236
|
+
usableSessionsCount: await this.usableSessionsCount(),
|
|
237
|
+
retiredSessionsCount: await this.retiredSessionsCount(),
|
|
238
|
+
sessions: this.#sessions.map((session) => session.getState()),
|
|
234
239
|
};
|
|
235
240
|
}
|
|
236
241
|
/**
|
|
@@ -239,47 +244,40 @@ export class SessionPool extends EventEmitter {
|
|
|
239
244
|
* @param options - Override the persistence options provided in the constructor
|
|
240
245
|
*/
|
|
241
246
|
async persistState(options) {
|
|
242
|
-
if (!this
|
|
247
|
+
if (!this.#persistenceOptions.enable && !options?.enable) {
|
|
243
248
|
return;
|
|
244
249
|
}
|
|
245
|
-
this.
|
|
250
|
+
await this.ensureInitialized();
|
|
251
|
+
this.#log.debug('Persisting state', {
|
|
246
252
|
persistStateKeyValueStoreId: this.persistStateKeyValueStoreId,
|
|
247
253
|
persistStateKey: this.persistStateKey,
|
|
248
254
|
});
|
|
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
255
|
await this.keyValueStore
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
doNotRetryTimeouts: true,
|
|
256
|
-
})
|
|
257
|
-
.catch((error) => this.log.warning(`Failed to persist the session pool stats to ${this.persistStateKey}`, { error }));
|
|
256
|
+
?.setValue(this.persistStateKey, await this.getState())
|
|
257
|
+
.catch((error) => this.#log.warning(`Failed to persist the session pool stats to ${this.persistStateKey}`, { error }));
|
|
258
258
|
}
|
|
259
259
|
/**
|
|
260
260
|
* Removes listener from `persistState` event.
|
|
261
261
|
* This function should be called after you are done with using the `SessionPool` instance.
|
|
262
262
|
*/
|
|
263
263
|
async teardown() {
|
|
264
|
-
|
|
264
|
+
if (!this.#initPromise)
|
|
265
|
+
return;
|
|
266
|
+
await this.ensureInitialized();
|
|
267
|
+
if (this.#listener) {
|
|
268
|
+
this.#events.off("persistState" /* EventType.PERSIST_STATE */, this.#listener);
|
|
269
|
+
}
|
|
265
270
|
await this.persistState();
|
|
266
271
|
}
|
|
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
272
|
/**
|
|
275
273
|
* Removes retired `Session` instances from `SessionPool`.
|
|
276
274
|
*/
|
|
277
|
-
|
|
278
|
-
this
|
|
275
|
+
removeRetiredSessions() {
|
|
276
|
+
this.#sessions = this.#sessions.filter((storedSession) => {
|
|
279
277
|
if (storedSession.isUsable())
|
|
280
278
|
return true;
|
|
281
279
|
this.sessionMap.delete(storedSession.id);
|
|
282
|
-
this
|
|
280
|
+
this.#log.debug(`Removed Session - ${storedSession.id}`);
|
|
283
281
|
return false;
|
|
284
282
|
});
|
|
285
283
|
}
|
|
@@ -287,89 +285,103 @@ export class SessionPool extends EventEmitter {
|
|
|
287
285
|
* Adds `Session` instance to `SessionPool`.
|
|
288
286
|
* @param newSession `Session` instance to be added.
|
|
289
287
|
*/
|
|
290
|
-
|
|
291
|
-
this
|
|
288
|
+
registerSession(newSession) {
|
|
289
|
+
this.#sessions.push(newSession);
|
|
292
290
|
this.sessionMap.set(newSession.id, newSession);
|
|
293
291
|
}
|
|
294
292
|
/**
|
|
295
293
|
* Gets random index.
|
|
296
294
|
*/
|
|
297
|
-
|
|
298
|
-
return Math.floor(Math.random() * this
|
|
295
|
+
getRandomIndex() {
|
|
296
|
+
return Math.floor(Math.random() * this.#sessions.length);
|
|
299
297
|
}
|
|
300
298
|
/**
|
|
301
299
|
* Creates new session without any extra behavior.
|
|
302
|
-
* @param sessionPool
|
|
303
300
|
* @param [options]
|
|
304
301
|
* @param [options.sessionOptions] The configuration options for the session being created.
|
|
305
302
|
* @returns New session.
|
|
306
303
|
*/
|
|
307
|
-
|
|
304
|
+
async defaultCreateSessionFunction(options = {}) {
|
|
308
305
|
ow(options, ow.object.exactShape({ sessionOptions: ow.optional.object }));
|
|
309
306
|
const { sessionOptions = {} } = options;
|
|
310
|
-
return new Session(
|
|
307
|
+
return new Session(sessionOptions);
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Invokes `createSessionFunction` with `sessionOptions` already merged from pool-wide defaults and
|
|
311
|
+
* the supplied per-call overrides, so custom implementations don't need to spread `pool.sessionOptions` themselves.
|
|
312
|
+
*
|
|
313
|
+
* A default {@link SessionFingerprint} is generated up front (host OS as
|
|
314
|
+
* `platform`, a random valid `browser`/`device` for that platform). Pool-wide
|
|
315
|
+
* and per-call options override it, and a persisted fingerprint coming
|
|
316
|
+
* through `maybeLoadSessionPool` naturally wins because it arrives in
|
|
317
|
+
* `perCallOptions`.
|
|
318
|
+
*/
|
|
319
|
+
async invokeCreateSessionFunction(perCallOptions) {
|
|
320
|
+
const sessionOptions = {
|
|
321
|
+
fingerprint: createDefaultSessionFingerprint(),
|
|
311
322
|
...this.sessionOptions,
|
|
312
|
-
...
|
|
313
|
-
|
|
314
|
-
});
|
|
323
|
+
...perCallOptions,
|
|
324
|
+
};
|
|
325
|
+
return this.createSessionFunction({ sessionOptions });
|
|
315
326
|
}
|
|
316
327
|
/**
|
|
317
328
|
* Creates new session and adds it to the pool.
|
|
318
329
|
* @returns Newly created `Session` instance.
|
|
319
330
|
*/
|
|
320
|
-
async
|
|
321
|
-
const newSession = await this.
|
|
322
|
-
this.
|
|
323
|
-
this
|
|
331
|
+
async createSession() {
|
|
332
|
+
const newSession = await this.invokeCreateSessionFunction();
|
|
333
|
+
this.registerSession(newSession);
|
|
334
|
+
this.#log.debug(`Created new Session - ${newSession.id}`);
|
|
324
335
|
return newSession;
|
|
325
336
|
}
|
|
326
337
|
/**
|
|
327
338
|
* Decides whether there is enough space for creating new session.
|
|
328
339
|
*/
|
|
329
|
-
|
|
330
|
-
return this
|
|
340
|
+
hasSpaceForSession() {
|
|
341
|
+
return this.#sessions.length < this.maxPoolSize;
|
|
331
342
|
}
|
|
332
343
|
/**
|
|
333
|
-
* Picks
|
|
334
|
-
*
|
|
344
|
+
* Picks a session from the `SessionPool` according to the configured `sessionReuseStrategy`.
|
|
345
|
+
* Returns `undefined` when no session should be reused and a new one should be created instead.
|
|
335
346
|
*/
|
|
336
|
-
|
|
337
|
-
|
|
347
|
+
pickSession() {
|
|
348
|
+
if (this.#sessionReuseStrategy !== 'use-until-failure' && this.hasSpaceForSession())
|
|
349
|
+
return undefined;
|
|
350
|
+
if (this.#sessionReuseStrategy === 'use-until-failure') {
|
|
351
|
+
return this.#sessions.find((session) => session.isUsable());
|
|
352
|
+
}
|
|
353
|
+
let picked;
|
|
354
|
+
if (this.#sessionReuseStrategy === 'round-robin') {
|
|
355
|
+
const index = this.#roundRobinIndex % this.#sessions.length;
|
|
356
|
+
this.#roundRobinIndex = index + 1;
|
|
357
|
+
picked = this.#sessions[index];
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
picked = this.#sessions[this.getRandomIndex()];
|
|
361
|
+
}
|
|
362
|
+
return picked.isUsable() ? picked : undefined;
|
|
338
363
|
}
|
|
339
364
|
/**
|
|
340
365
|
* Potentially loads `SessionPool`.
|
|
341
366
|
* If the state was persisted it loads the `SessionPool` from the persisted state.
|
|
342
367
|
*/
|
|
343
|
-
async
|
|
344
|
-
const loadedSessionPool = await this.keyValueStore
|
|
368
|
+
async maybeLoadSessionPool() {
|
|
369
|
+
const loadedSessionPool = await this.keyValueStore?.getValue(this.persistStateKey);
|
|
345
370
|
if (!loadedSessionPool)
|
|
346
371
|
return;
|
|
347
372
|
// Invalidate old sessions and load active sessions only
|
|
348
|
-
this
|
|
373
|
+
this.#log.debug('Recreating state from KeyValueStore', {
|
|
349
374
|
persistStateKeyValueStoreId: this.persistStateKeyValueStoreId,
|
|
350
375
|
persistStateKey: this.persistStateKey,
|
|
351
376
|
});
|
|
352
377
|
for (const sessionObject of loadedSessionPool.sessions) {
|
|
353
|
-
sessionObject.sessionPool = this;
|
|
354
378
|
sessionObject.createdAt = new Date(sessionObject.createdAt);
|
|
355
379
|
sessionObject.expiresAt = new Date(sessionObject.expiresAt);
|
|
356
|
-
const recreatedSession = await this.
|
|
380
|
+
const recreatedSession = await this.invokeCreateSessionFunction(sessionObject);
|
|
357
381
|
if (recreatedSession.isUsable()) {
|
|
358
|
-
this.
|
|
382
|
+
this.registerSession(recreatedSession);
|
|
359
383
|
}
|
|
360
384
|
}
|
|
361
|
-
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;
|
|
385
|
+
this.#log.debug(`${this.#sessions.length} active sessions loaded from KeyValueStore`);
|
|
373
386
|
}
|
|
374
387
|
}
|
|
375
|
-
//# sourceMappingURL=session_pool.js.map
|