@crawlee/core 4.0.0-beta.104 → 4.0.0-beta.106
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/autoscaling/autoscaled_pool.d.ts +3 -21
- package/autoscaling/autoscaled_pool.js +85 -85
- package/autoscaling/client_load_signal.d.ts +1 -5
- package/autoscaling/client_load_signal.js +20 -20
- package/autoscaling/concurrency_system.d.ts +5 -20
- package/autoscaling/concurrency_system.js +81 -80
- package/autoscaling/cpu_load_signal.d.ts +1 -2
- package/autoscaling/cpu_load_signal.js +10 -10
- package/autoscaling/event_loop_load_signal.d.ts +1 -4
- package/autoscaling/event_loop_load_signal.js +18 -18
- package/autoscaling/load_signal.d.ts +1 -1
- package/autoscaling/load_signal.js +12 -11
- package/autoscaling/memory_load_signal.d.ts +3 -12
- package/autoscaling/memory_load_signal.js +40 -41
- package/autoscaling/snapshotter.d.ts +1 -4
- package/autoscaling/snapshotter.js +12 -12
- package/autoscaling/system_status.d.ts +1 -3
- package/autoscaling/system_status.js +11 -11
- package/configuration.d.ts +1 -1
- package/configuration.js +3 -3
- package/crawlers/context_pipeline.js +6 -6
- package/crawlers/crawler_commons.d.ts +6 -56
- package/crawlers/crawler_commons.js +1 -107
- package/crawlers/index.d.ts +1 -1
- package/crawlers/index.js +0 -1
- package/crawlers/statistics.d.ts +1 -8
- package/crawlers/statistics.js +45 -44
- package/events/event_manager.d.ts +1 -1
- package/events/event_manager.js +3 -3
- package/events/local_event_manager.d.ts +1 -1
- package/events/local_event_manager.js +3 -3
- package/log.js +5 -1
- package/memory-storage/memory-storage.d.ts +1 -5
- package/memory-storage/memory-storage.js +2 -2
- package/memory-storage/resource-clients/dataset.d.ts +1 -1
- package/memory-storage/resource-clients/dataset.js +6 -5
- package/memory-storage/resource-clients/key-value-store.d.ts +1 -1
- package/memory-storage/resource-clients/key-value-store.js +13 -12
- package/memory-storage/resource-clients/request-queue.d.ts +4 -23
- package/memory-storage/resource-clients/request-queue.js +59 -58
- package/owned_or_injected.d.ts +1 -3
- package/owned_or_injected.js +17 -17
- package/package.json +5 -5
- package/proxy_configuration.d.ts +1 -3
- package/proxy_configuration.js +8 -8
- package/recoverable_state.d.ts +1 -10
- package/recoverable_state.js +41 -41
- package/request.d.ts +1 -2
- package/request.js +10 -13
- package/router.d.ts +1 -4
- package/router.js +23 -23
- package/serialization.js +8 -9
- package/service_locator.d.ts +1 -10
- package/service_locator.js +48 -48
- package/session_pool/session.d.ts +1 -12
- package/session_pool/session.js +50 -50
- package/session_pool/session_pool.d.ts +2 -11
- package/session_pool/session_pool.js +59 -58
- package/storages/dataset.d.ts +12 -1
- package/storages/dataset.js +121 -22
- package/storages/index.d.ts +1 -1
- package/storages/index.js +1 -1
- package/storages/key_value_store.d.ts +19 -4
- package/storages/key_value_store.js +174 -48
- package/storages/request_dedup_cache.d.ts +1 -2
- package/storages/request_dedup_cache.js +9 -9
- package/storages/request_list.d.ts +2 -22
- package/storages/request_list.js +74 -73
- package/storages/request_manager_tandem.d.ts +1 -10
- package/storages/request_manager_tandem.js +27 -27
- package/storages/request_queue.d.ts +21 -18
- package/storages/request_queue.js +256 -53
- package/storages/sitemap_request_loader.d.ts +1 -44
- package/storages/sitemap_request_loader.js +87 -87
- package/storages/storage_instance_manager.d.ts +1 -2
- package/storages/storage_instance_manager.js +17 -17
- package/storages/storage_stats.d.ts +1 -1
- package/storages/storage_stats.js +4 -4
- package/storages/transaction.d.ts +252 -0
- package/storages/transaction.js +251 -0
- package/system-info/runtime.js +7 -7
- package/storages/access_checking.d.ts +0 -12
- package/storages/access_checking.js +0 -17
package/session_pool/session.js
CHANGED
|
@@ -12,57 +12,57 @@ import { serviceLocator } from '../service_locator.js';
|
|
|
12
12
|
export class Session {
|
|
13
13
|
id;
|
|
14
14
|
userData;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
log;
|
|
15
|
+
#maxErrorScore;
|
|
16
|
+
#errorScoreDecrement;
|
|
17
|
+
#createdAt;
|
|
18
|
+
#expiresAt;
|
|
19
|
+
#usageCount;
|
|
20
|
+
#maxUsageCount;
|
|
21
|
+
#errorScore;
|
|
22
|
+
#retired = false;
|
|
23
|
+
#proxyInfo;
|
|
24
|
+
#cookieJar;
|
|
25
|
+
#fingerprint;
|
|
26
|
+
#log;
|
|
27
27
|
get errorScore() {
|
|
28
|
-
return this
|
|
28
|
+
return this.#errorScore;
|
|
29
29
|
}
|
|
30
30
|
get usageCount() {
|
|
31
|
-
return this
|
|
31
|
+
return this.#usageCount;
|
|
32
32
|
}
|
|
33
33
|
get maxErrorScore() {
|
|
34
|
-
return this
|
|
34
|
+
return this.#maxErrorScore;
|
|
35
35
|
}
|
|
36
36
|
get errorScoreDecrement() {
|
|
37
|
-
return this
|
|
37
|
+
return this.#errorScoreDecrement;
|
|
38
38
|
}
|
|
39
39
|
get expiresAt() {
|
|
40
|
-
return this
|
|
40
|
+
return this.#expiresAt;
|
|
41
41
|
}
|
|
42
42
|
get createdAt() {
|
|
43
|
-
return this
|
|
43
|
+
return this.#createdAt;
|
|
44
44
|
}
|
|
45
45
|
get maxUsageCount() {
|
|
46
|
-
return this
|
|
46
|
+
return this.#maxUsageCount;
|
|
47
47
|
}
|
|
48
48
|
get cookieJar() {
|
|
49
|
-
return this
|
|
49
|
+
return this.#cookieJar;
|
|
50
50
|
}
|
|
51
51
|
get proxyInfo() {
|
|
52
|
-
return this
|
|
52
|
+
return this.#proxyInfo;
|
|
53
53
|
}
|
|
54
54
|
get fingerprint() {
|
|
55
|
-
return this
|
|
55
|
+
return this.#fingerprint;
|
|
56
56
|
}
|
|
57
57
|
set fingerprint(fingerprint) {
|
|
58
|
-
this
|
|
58
|
+
this.#fingerprint = fingerprint;
|
|
59
59
|
}
|
|
60
60
|
/**
|
|
61
61
|
* `true` once {@link Session.retire|`retire()`} has been called. Retirement is terminal:
|
|
62
62
|
* a retired session is never picked by the pool and cannot be revived via `markGood()`.
|
|
63
63
|
*/
|
|
64
64
|
get retired() {
|
|
65
|
-
return this
|
|
65
|
+
return this.#retired;
|
|
66
66
|
}
|
|
67
67
|
/**
|
|
68
68
|
* Session configuration.
|
|
@@ -87,21 +87,21 @@ export class Session {
|
|
|
87
87
|
}));
|
|
88
88
|
const { id = `session_${cryptoRandomObjectId(10)}`, cookieJar = new CookieJar(), proxyInfo = undefined, maxAgeSecs = 3000, userData = {}, maxErrorScore = 3, errorScoreDecrement = 0.5, createdAt = new Date(), usageCount = 0, errorScore = 0, maxUsageCount = 50, retired = false, log = serviceLocator.getLogger(), fingerprint, } = options;
|
|
89
89
|
const { expiresAt = getDefaultCookieExpirationDate(maxAgeSecs) } = options;
|
|
90
|
-
this
|
|
91
|
-
this
|
|
92
|
-
this
|
|
93
|
-
this
|
|
90
|
+
this.#log = log.child({ prefix: 'Session' });
|
|
91
|
+
this.#cookieJar = cookieJar.setCookie ? cookieJar : CookieJar.fromJSON(JSON.stringify(cookieJar));
|
|
92
|
+
this.#proxyInfo = proxyInfo;
|
|
93
|
+
this.#fingerprint = fingerprint;
|
|
94
94
|
this.id = id;
|
|
95
95
|
this.userData = userData;
|
|
96
|
-
this
|
|
97
|
-
this
|
|
96
|
+
this.#maxErrorScore = maxErrorScore;
|
|
97
|
+
this.#errorScoreDecrement = errorScoreDecrement;
|
|
98
98
|
// Internal
|
|
99
|
-
this
|
|
100
|
-
this
|
|
101
|
-
this
|
|
102
|
-
this
|
|
103
|
-
this
|
|
104
|
-
this
|
|
99
|
+
this.#expiresAt = expiresAt;
|
|
100
|
+
this.#createdAt = createdAt;
|
|
101
|
+
this.#usageCount = usageCount; // indicates how many times the session has been used
|
|
102
|
+
this.#errorScore = errorScore; // indicates number of markBaded request with the session
|
|
103
|
+
this.#maxUsageCount = maxUsageCount;
|
|
104
|
+
this.#retired = retired;
|
|
105
105
|
}
|
|
106
106
|
/**
|
|
107
107
|
* Indicates whether the session is blocked.
|
|
@@ -130,16 +130,16 @@ export class Session {
|
|
|
130
130
|
* Session is usable when it is not retired, not expired, not blocked and the maximum usage count has not be reached.
|
|
131
131
|
*/
|
|
132
132
|
isUsable() {
|
|
133
|
-
return !this
|
|
133
|
+
return !this.#retired && !this.isBlocked() && !this.isExpired() && !this.isMaxUsageCountReached();
|
|
134
134
|
}
|
|
135
135
|
/**
|
|
136
136
|
* This method should be called after a successful session usage.
|
|
137
137
|
* It increases `usageCount` and potentially lowers the `errorScore` by the `errorScoreDecrement`.
|
|
138
138
|
*/
|
|
139
139
|
markGood() {
|
|
140
|
-
this
|
|
141
|
-
if (this
|
|
142
|
-
this
|
|
140
|
+
this.#usageCount += 1;
|
|
141
|
+
if (this.#errorScore > 0) {
|
|
142
|
+
this.#errorScore -= this.#errorScoreDecrement;
|
|
143
143
|
}
|
|
144
144
|
this.maybeSelfRetire();
|
|
145
145
|
}
|
|
@@ -151,9 +151,9 @@ export class Session {
|
|
|
151
151
|
return {
|
|
152
152
|
id: this.id,
|
|
153
153
|
cookieJar: this.cookieJar.toJSON(),
|
|
154
|
-
proxyInfo: this
|
|
154
|
+
proxyInfo: this.#proxyInfo,
|
|
155
155
|
userData: this.userData,
|
|
156
|
-
fingerprint: this
|
|
156
|
+
fingerprint: this.#fingerprint,
|
|
157
157
|
maxErrorScore: this.maxErrorScore,
|
|
158
158
|
errorScoreDecrement: this.errorScoreDecrement,
|
|
159
159
|
expiresAt: this.expiresAt.toISOString(),
|
|
@@ -161,7 +161,7 @@ export class Session {
|
|
|
161
161
|
usageCount: this.usageCount,
|
|
162
162
|
maxUsageCount: this.maxUsageCount,
|
|
163
163
|
errorScore: this.errorScore,
|
|
164
|
-
retired: this
|
|
164
|
+
retired: this.#retired,
|
|
165
165
|
};
|
|
166
166
|
}
|
|
167
167
|
/**
|
|
@@ -172,19 +172,19 @@ export class Session {
|
|
|
172
172
|
* For transient external failures (such as `5XX` responses), use `markBad()` instead.
|
|
173
173
|
*/
|
|
174
174
|
retire() {
|
|
175
|
-
if (this
|
|
175
|
+
if (this.#retired)
|
|
176
176
|
return;
|
|
177
|
-
this
|
|
178
|
-
this
|
|
179
|
-
this
|
|
177
|
+
this.#errorScore += this.#maxErrorScore;
|
|
178
|
+
this.#usageCount += 1;
|
|
179
|
+
this.#retired = true;
|
|
180
180
|
}
|
|
181
181
|
/**
|
|
182
182
|
* Increases usage and error count.
|
|
183
183
|
* Should be used when the session has been used unsuccessfully. For example because of timeouts.
|
|
184
184
|
*/
|
|
185
185
|
markBad() {
|
|
186
|
-
this
|
|
187
|
-
this
|
|
186
|
+
this.#errorScore += 1;
|
|
187
|
+
this.#usageCount += 1;
|
|
188
188
|
this.maybeSelfRetire();
|
|
189
189
|
}
|
|
190
190
|
/**
|
|
@@ -204,7 +204,7 @@ export class Session {
|
|
|
204
204
|
this.cookieJar.setCookieSync(rawCookie, url);
|
|
205
205
|
}
|
|
206
206
|
catch (e) {
|
|
207
|
-
this
|
|
207
|
+
this.#log.warning('Could not set cookie.', { url, error: e.message });
|
|
208
208
|
}
|
|
209
209
|
}
|
|
210
210
|
/**
|
|
@@ -112,24 +112,15 @@ export interface SessionPoolOptions {
|
|
|
112
112
|
* @category Scaling
|
|
113
113
|
*/
|
|
114
114
|
export declare class SessionPool implements ISessionPool {
|
|
115
|
-
private
|
|
115
|
+
#private;
|
|
116
116
|
readonly id: string;
|
|
117
|
-
private log;
|
|
118
117
|
private maxPoolSize;
|
|
119
118
|
private createSessionFunction;
|
|
120
119
|
private keyValueStore?;
|
|
121
|
-
private sessions;
|
|
122
120
|
private sessionMap;
|
|
123
121
|
private sessionOptions;
|
|
124
122
|
private persistStateKeyValueStoreId?;
|
|
125
123
|
private persistStateKey;
|
|
126
|
-
private listener?;
|
|
127
|
-
private events;
|
|
128
|
-
private persistenceOptions;
|
|
129
|
-
private sessionReuseStrategy;
|
|
130
|
-
private initPromise?;
|
|
131
|
-
private queue;
|
|
132
|
-
private roundRobinIndex;
|
|
133
124
|
constructor(options?: SessionPoolOptions);
|
|
134
125
|
/**
|
|
135
126
|
* Gets count of usable sessions in the pool.
|
|
@@ -222,7 +213,7 @@ export declare class SessionPool implements ISessionPool {
|
|
|
222
213
|
* through `maybeLoadSessionPool` naturally wins because it arrives in
|
|
223
214
|
* `perCallOptions`.
|
|
224
215
|
*/
|
|
225
|
-
private
|
|
216
|
+
private invokeCreateSessionFunction;
|
|
226
217
|
/**
|
|
227
218
|
* Creates new session and adds it to the pool.
|
|
228
219
|
* @returns Newly created `Session` instance.
|
|
@@ -60,24 +60,25 @@ const SESSION_REUSE_STRATEGIES = ['random', 'round-robin', 'use-until-failure'];
|
|
|
60
60
|
* @category Scaling
|
|
61
61
|
*/
|
|
62
62
|
export class SessionPool {
|
|
63
|
-
static nextId = 0;
|
|
63
|
+
static #nextId = 0;
|
|
64
64
|
id;
|
|
65
|
-
log;
|
|
65
|
+
#log;
|
|
66
|
+
#sessions = [];
|
|
67
|
+
// kept as TS-private: session_pool tests read/override the members below directly
|
|
66
68
|
maxPoolSize;
|
|
67
69
|
createSessionFunction;
|
|
68
70
|
keyValueStore;
|
|
69
|
-
sessions = [];
|
|
70
71
|
sessionMap = new Map();
|
|
71
72
|
sessionOptions;
|
|
72
73
|
persistStateKeyValueStoreId;
|
|
73
74
|
persistStateKey;
|
|
74
|
-
listener;
|
|
75
|
-
events;
|
|
76
|
-
persistenceOptions;
|
|
77
|
-
sessionReuseStrategy;
|
|
78
|
-
initPromise;
|
|
79
|
-
queue = new AsyncQueue();
|
|
80
|
-
roundRobinIndex = 0;
|
|
75
|
+
#listener;
|
|
76
|
+
#events;
|
|
77
|
+
#persistenceOptions;
|
|
78
|
+
#sessionReuseStrategy;
|
|
79
|
+
#initPromise;
|
|
80
|
+
#queue = new AsyncQueue();
|
|
81
|
+
#roundRobinIndex = 0;
|
|
81
82
|
constructor(options = {}) {
|
|
82
83
|
ow(options, ow.object.exactShape({
|
|
83
84
|
id: ow.optional.any(ow.number, ow.string),
|
|
@@ -93,20 +94,20 @@ export class SessionPool {
|
|
|
93
94
|
const { id, maxPoolSize = MAX_POOL_SIZE, persistStateKeyValueStoreId, persistStateKey, createSessionFunction, sessionOptions = {}, log = serviceLocator.getLogger(), persistenceOptions = {
|
|
94
95
|
enable: true,
|
|
95
96
|
}, sessionReuseStrategy = 'random', } = options;
|
|
96
|
-
this.id = id != null ? String(id) : String(SessionPool
|
|
97
|
-
this
|
|
98
|
-
this
|
|
99
|
-
this
|
|
100
|
-
this
|
|
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;
|
|
101
102
|
// Pool Configuration
|
|
102
103
|
this.maxPoolSize = maxPoolSize;
|
|
103
104
|
this.createSessionFunction = createSessionFunction || this.defaultCreateSessionFunction;
|
|
104
105
|
// Session configuration. The pool-scoped logger is merged into per-call sessionOptions inside
|
|
105
|
-
// `
|
|
106
|
+
// `invokeCreateSessionFunction`, so every Session inherits it without custom createSessionFunctions
|
|
106
107
|
// having to know about it.
|
|
107
108
|
this.sessionOptions = {
|
|
108
109
|
...sessionOptions,
|
|
109
|
-
log: this
|
|
110
|
+
log: this.#log,
|
|
110
111
|
};
|
|
111
112
|
// Session keyValueStore
|
|
112
113
|
this.persistStateKeyValueStoreId = persistStateKeyValueStoreId;
|
|
@@ -117,39 +118,39 @@ export class SessionPool {
|
|
|
117
118
|
*/
|
|
118
119
|
async usableSessionsCount() {
|
|
119
120
|
await this.ensureInitialized();
|
|
120
|
-
return this
|
|
121
|
+
return this.#sessions.filter((session) => session.isUsable()).length;
|
|
121
122
|
}
|
|
122
123
|
/**
|
|
123
124
|
* Gets count of retired sessions in the pool.
|
|
124
125
|
*/
|
|
125
126
|
async retiredSessionsCount() {
|
|
126
127
|
await this.ensureInitialized();
|
|
127
|
-
return this
|
|
128
|
+
return this.#sessions.filter((session) => !session.isUsable()).length;
|
|
128
129
|
}
|
|
129
130
|
/**
|
|
130
131
|
* Starts periodic state persistence and potentially loads SessionPool state from {@link KeyValueStore}.
|
|
131
132
|
* Called automatically on first use of any public method.
|
|
132
133
|
*/
|
|
133
134
|
async ensureInitialized() {
|
|
134
|
-
if (!this
|
|
135
|
-
this
|
|
135
|
+
if (!this.#initPromise) {
|
|
136
|
+
this.#initPromise = this.setupPool();
|
|
136
137
|
}
|
|
137
|
-
return this
|
|
138
|
+
return this.#initPromise;
|
|
138
139
|
}
|
|
139
140
|
async setupPool() {
|
|
140
|
-
if (!this
|
|
141
|
+
if (!this.#persistenceOptions.enable) {
|
|
141
142
|
return;
|
|
142
143
|
}
|
|
143
144
|
this.keyValueStore = await KeyValueStore.open(this.persistStateKeyValueStoreId ? { id: this.persistStateKeyValueStoreId } : null, {
|
|
144
145
|
configuration: serviceLocator.getConfiguration(),
|
|
145
146
|
});
|
|
146
147
|
if (!this.persistStateKeyValueStoreId) {
|
|
147
|
-
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}`);
|
|
148
149
|
}
|
|
149
150
|
// in case of migration happened and SessionPool state should be restored from the keyValueStore.
|
|
150
151
|
await this.maybeLoadSessionPool();
|
|
151
|
-
this
|
|
152
|
-
this
|
|
152
|
+
this.#listener = this.persistState.bind(this);
|
|
153
|
+
this.#events.on("persistState" /* EventType.PERSIST_STATE */, this.#listener);
|
|
153
154
|
}
|
|
154
155
|
/**
|
|
155
156
|
* Adds a new session to the session pool. The pool automatically creates sessions up to the maximum size of the pool,
|
|
@@ -169,8 +170,8 @@ export class SessionPool {
|
|
|
169
170
|
if (!this.hasSpaceForSession()) {
|
|
170
171
|
this.removeRetiredSessions();
|
|
171
172
|
}
|
|
172
|
-
const newSession = options instanceof Session ? options : await this.
|
|
173
|
-
this
|
|
173
|
+
const newSession = options instanceof Session ? options : await this.invokeCreateSessionFunction(options);
|
|
174
|
+
this.#log.debug(`Adding new Session - ${newSession.id}`);
|
|
174
175
|
this.registerSession(newSession);
|
|
175
176
|
}
|
|
176
177
|
/**
|
|
@@ -181,7 +182,7 @@ export class SessionPool {
|
|
|
181
182
|
*/
|
|
182
183
|
async newSession(sessionOptions) {
|
|
183
184
|
await this.ensureInitialized();
|
|
184
|
-
const newSession = await this.
|
|
185
|
+
const newSession = await this.invokeCreateSessionFunction(sessionOptions);
|
|
185
186
|
this.registerSession(newSession);
|
|
186
187
|
return newSession;
|
|
187
188
|
}
|
|
@@ -194,7 +195,7 @@ export class SessionPool {
|
|
|
194
195
|
*/
|
|
195
196
|
async getSession(sessionId) {
|
|
196
197
|
await this.ensureInitialized();
|
|
197
|
-
await this
|
|
198
|
+
await this.#queue.wait();
|
|
198
199
|
try {
|
|
199
200
|
if (sessionId) {
|
|
200
201
|
const session = this.sessionMap.get(sessionId);
|
|
@@ -212,14 +213,14 @@ export class SessionPool {
|
|
|
212
213
|
return await this.createSession();
|
|
213
214
|
}
|
|
214
215
|
finally {
|
|
215
|
-
this
|
|
216
|
+
this.#queue.shift();
|
|
216
217
|
}
|
|
217
218
|
}
|
|
218
219
|
/**
|
|
219
220
|
* @param options - Override the persistence options provided in the constructor
|
|
220
221
|
*/
|
|
221
222
|
async resetStore(options) {
|
|
222
|
-
if (!this
|
|
223
|
+
if (!this.#persistenceOptions.enable && !options?.enable) {
|
|
223
224
|
return;
|
|
224
225
|
}
|
|
225
226
|
await this.ensureInitialized();
|
|
@@ -234,7 +235,7 @@ export class SessionPool {
|
|
|
234
235
|
return {
|
|
235
236
|
usableSessionsCount: await this.usableSessionsCount(),
|
|
236
237
|
retiredSessionsCount: await this.retiredSessionsCount(),
|
|
237
|
-
sessions: this
|
|
238
|
+
sessions: this.#sessions.map((session) => session.getState()),
|
|
238
239
|
};
|
|
239
240
|
}
|
|
240
241
|
/**
|
|
@@ -243,28 +244,28 @@ export class SessionPool {
|
|
|
243
244
|
* @param options - Override the persistence options provided in the constructor
|
|
244
245
|
*/
|
|
245
246
|
async persistState(options) {
|
|
246
|
-
if (!this
|
|
247
|
+
if (!this.#persistenceOptions.enable && !options?.enable) {
|
|
247
248
|
return;
|
|
248
249
|
}
|
|
249
250
|
await this.ensureInitialized();
|
|
250
|
-
this
|
|
251
|
+
this.#log.debug('Persisting state', {
|
|
251
252
|
persistStateKeyValueStoreId: this.persistStateKeyValueStoreId,
|
|
252
253
|
persistStateKey: this.persistStateKey,
|
|
253
254
|
});
|
|
254
255
|
await this.keyValueStore
|
|
255
256
|
?.setValue(this.persistStateKey, await this.getState())
|
|
256
|
-
.catch((error) => this
|
|
257
|
+
.catch((error) => this.#log.warning(`Failed to persist the session pool stats to ${this.persistStateKey}`, { error }));
|
|
257
258
|
}
|
|
258
259
|
/**
|
|
259
260
|
* Removes listener from `persistState` event.
|
|
260
261
|
* This function should be called after you are done with using the `SessionPool` instance.
|
|
261
262
|
*/
|
|
262
263
|
async teardown() {
|
|
263
|
-
if (!this
|
|
264
|
+
if (!this.#initPromise)
|
|
264
265
|
return;
|
|
265
266
|
await this.ensureInitialized();
|
|
266
|
-
if (this
|
|
267
|
-
this
|
|
267
|
+
if (this.#listener) {
|
|
268
|
+
this.#events.off("persistState" /* EventType.PERSIST_STATE */, this.#listener);
|
|
268
269
|
}
|
|
269
270
|
await this.persistState();
|
|
270
271
|
}
|
|
@@ -272,11 +273,11 @@ export class SessionPool {
|
|
|
272
273
|
* Removes retired `Session` instances from `SessionPool`.
|
|
273
274
|
*/
|
|
274
275
|
removeRetiredSessions() {
|
|
275
|
-
this
|
|
276
|
+
this.#sessions = this.#sessions.filter((storedSession) => {
|
|
276
277
|
if (storedSession.isUsable())
|
|
277
278
|
return true;
|
|
278
279
|
this.sessionMap.delete(storedSession.id);
|
|
279
|
-
this
|
|
280
|
+
this.#log.debug(`Removed Session - ${storedSession.id}`);
|
|
280
281
|
return false;
|
|
281
282
|
});
|
|
282
283
|
}
|
|
@@ -285,14 +286,14 @@ export class SessionPool {
|
|
|
285
286
|
* @param newSession `Session` instance to be added.
|
|
286
287
|
*/
|
|
287
288
|
registerSession(newSession) {
|
|
288
|
-
this
|
|
289
|
+
this.#sessions.push(newSession);
|
|
289
290
|
this.sessionMap.set(newSession.id, newSession);
|
|
290
291
|
}
|
|
291
292
|
/**
|
|
292
293
|
* Gets random index.
|
|
293
294
|
*/
|
|
294
295
|
getRandomIndex() {
|
|
295
|
-
return Math.floor(Math.random() * this
|
|
296
|
+
return Math.floor(Math.random() * this.#sessions.length);
|
|
296
297
|
}
|
|
297
298
|
/**
|
|
298
299
|
* Creates new session without any extra behavior.
|
|
@@ -315,7 +316,7 @@ export class SessionPool {
|
|
|
315
316
|
* through `maybeLoadSessionPool` naturally wins because it arrives in
|
|
316
317
|
* `perCallOptions`.
|
|
317
318
|
*/
|
|
318
|
-
async
|
|
319
|
+
async invokeCreateSessionFunction(perCallOptions) {
|
|
319
320
|
const sessionOptions = {
|
|
320
321
|
fingerprint: createDefaultSessionFingerprint(),
|
|
321
322
|
...this.sessionOptions,
|
|
@@ -328,35 +329,35 @@ export class SessionPool {
|
|
|
328
329
|
* @returns Newly created `Session` instance.
|
|
329
330
|
*/
|
|
330
331
|
async createSession() {
|
|
331
|
-
const newSession = await this.
|
|
332
|
+
const newSession = await this.invokeCreateSessionFunction();
|
|
332
333
|
this.registerSession(newSession);
|
|
333
|
-
this
|
|
334
|
+
this.#log.debug(`Created new Session - ${newSession.id}`);
|
|
334
335
|
return newSession;
|
|
335
336
|
}
|
|
336
337
|
/**
|
|
337
338
|
* Decides whether there is enough space for creating new session.
|
|
338
339
|
*/
|
|
339
340
|
hasSpaceForSession() {
|
|
340
|
-
return this
|
|
341
|
+
return this.#sessions.length < this.maxPoolSize;
|
|
341
342
|
}
|
|
342
343
|
/**
|
|
343
344
|
* Picks a session from the `SessionPool` according to the configured `sessionReuseStrategy`.
|
|
344
345
|
* Returns `undefined` when no session should be reused and a new one should be created instead.
|
|
345
346
|
*/
|
|
346
347
|
pickSession() {
|
|
347
|
-
if (this
|
|
348
|
+
if (this.#sessionReuseStrategy !== 'use-until-failure' && this.hasSpaceForSession())
|
|
348
349
|
return undefined;
|
|
349
|
-
if (this
|
|
350
|
-
return this
|
|
350
|
+
if (this.#sessionReuseStrategy === 'use-until-failure') {
|
|
351
|
+
return this.#sessions.find((session) => session.isUsable());
|
|
351
352
|
}
|
|
352
353
|
let picked;
|
|
353
|
-
if (this
|
|
354
|
-
const index = this
|
|
355
|
-
this
|
|
356
|
-
picked = this
|
|
354
|
+
if (this.#sessionReuseStrategy === 'round-robin') {
|
|
355
|
+
const index = this.#roundRobinIndex % this.#sessions.length;
|
|
356
|
+
this.#roundRobinIndex = index + 1;
|
|
357
|
+
picked = this.#sessions[index];
|
|
357
358
|
}
|
|
358
359
|
else {
|
|
359
|
-
picked = this
|
|
360
|
+
picked = this.#sessions[this.getRandomIndex()];
|
|
360
361
|
}
|
|
361
362
|
return picked.isUsable() ? picked : undefined;
|
|
362
363
|
}
|
|
@@ -369,18 +370,18 @@ export class SessionPool {
|
|
|
369
370
|
if (!loadedSessionPool)
|
|
370
371
|
return;
|
|
371
372
|
// Invalidate old sessions and load active sessions only
|
|
372
|
-
this
|
|
373
|
+
this.#log.debug('Recreating state from KeyValueStore', {
|
|
373
374
|
persistStateKeyValueStoreId: this.persistStateKeyValueStoreId,
|
|
374
375
|
persistStateKey: this.persistStateKey,
|
|
375
376
|
});
|
|
376
377
|
for (const sessionObject of loadedSessionPool.sessions) {
|
|
377
378
|
sessionObject.createdAt = new Date(sessionObject.createdAt);
|
|
378
379
|
sessionObject.expiresAt = new Date(sessionObject.expiresAt);
|
|
379
|
-
const recreatedSession = await this.
|
|
380
|
+
const recreatedSession = await this.invokeCreateSessionFunction(sessionObject);
|
|
380
381
|
if (recreatedSession.isUsable()) {
|
|
381
382
|
this.registerSession(recreatedSession);
|
|
382
383
|
}
|
|
383
384
|
}
|
|
384
|
-
this
|
|
385
|
+
this.#log.debug(`${this.#sessions.length} active sessions loaded from KeyValueStore`);
|
|
385
386
|
}
|
|
386
387
|
}
|
package/storages/dataset.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Awaitable, DatasetBackend, DatasetInfo, Dictionary } from '@crawlee/types';
|
|
2
2
|
import { Configuration } from '../configuration.js';
|
|
3
3
|
import type { CrawleeLogger } from '../log.js';
|
|
4
|
+
import type { JournalEntry } from './transaction.js';
|
|
4
5
|
import type { DatasetStats } from './storage_stats.js';
|
|
5
6
|
import type { StorageOpenOptions } from './utils.js';
|
|
6
7
|
import type { StorageIdentifier } from './storage_instance_manager.js';
|
|
@@ -139,12 +140,12 @@ export interface DatasetExportToOptions extends DatasetExportOptions {
|
|
|
139
140
|
* @category Result Stores
|
|
140
141
|
*/
|
|
141
142
|
export declare class Dataset<Data extends Dictionary = Dictionary> {
|
|
143
|
+
#private;
|
|
142
144
|
readonly configuration: Configuration;
|
|
143
145
|
id: string;
|
|
144
146
|
name?: string;
|
|
145
147
|
backend: DatasetBackend<Data>;
|
|
146
148
|
log: CrawleeLogger;
|
|
147
|
-
private readonly statsTracker;
|
|
148
149
|
/**
|
|
149
150
|
* @internal
|
|
150
151
|
*/
|
|
@@ -170,6 +171,16 @@ export declare class Dataset<Data extends Dictionary = Dictionary> {
|
|
|
170
171
|
* Returns {@link DatasetContent} object holding the items in the dataset based on the provided parameters.
|
|
171
172
|
*/
|
|
172
173
|
getData(options?: DatasetDataOptions): Promise<DatasetContent<Data>>;
|
|
174
|
+
/**
|
|
175
|
+
* The single transaction-aware page read all dataset read paths go through — both `getData()` and
|
|
176
|
+
* the private `fetchPages()`. Returns the real page concatenated with the current transaction's
|
|
177
|
+
* buffered items, with `offset` / `limit` / `desc` windowing applied across the concatenation.
|
|
178
|
+
*/
|
|
179
|
+
private readPage;
|
|
180
|
+
/** The active transaction's buffered writes to this dataset, derived from its journal. */
|
|
181
|
+
private bufferedJournalEntries;
|
|
182
|
+
/** @internal */
|
|
183
|
+
commitJournalEntries(entries: JournalEntry[]): Promise<void>;
|
|
173
184
|
/**
|
|
174
185
|
* Returns all the data from the dataset. This will iterate through the whole dataset
|
|
175
186
|
* via the `listItems()` client method, which gives you only paginated results.
|