You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/upgrading/upgrading_v4.md
+46Lines changed: 46 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -532,6 +532,52 @@ If you implemented a custom `StorageClient`, you need to:
532
532
2. Replace the six getter methods (`dataset`, `datasets`, `keyValueStore`, `keyValueStores`, `requestQueue`, `requestQueues`) with three async factory methods (`createDatasetClient`, `createKeyValueStoreClient`, `createRequestQueueClient`). Each factory should handle both opening an existing storage and creating a new one.
533
533
3. Apply the sub-client renames listed above (`get` → `getMetadata`, `delete` → `drop`, etc.) and implement the new `purge()` method.
534
534
535
+
## Multiple crawler instances use separate default request queues
536
+
537
+
In v3, every `BasicCrawler` (or subclass) that didn't receive an explicit `requestQueue` option would open the same default request queue. If you created two crawlers in the same process, they would silently share a queue — leading to request collisions and hard-to-debug deduplication issues.
538
+
539
+
In v4, only the **first** crawler instance uses the default request queue. Each subsequent instance automatically gets its own queue via an internal alias (e.g. `__default_1__`, `__default_2__`, etc.). This means multiple crawlers can safely coexist without interfering with each other's requests.
540
+
541
+
If you explicitly pass a `requestQueue` (or `requestManager`) to the crawler, that queue is used as-is regardless of instance order.
542
+
543
+
## Repeated `run()` calls use `purge()` instead of `drop()` + recreate
544
+
545
+
When calling `crawler.run()` multiple times on the same crawler instance, v3 would drop the default request queue and create a fresh one between runs. In v4, the crawler **purges** the queue instead — clearing all requests and resetting internal counters, but keeping the same queue object. This is more efficient and avoids edge cases around stale references.
546
+
547
+
The new `purge()` method is available on `RequestProvider` (the base class for `RequestQueue`) and is also defined as an optional method on the `IRequestManager` interface.
548
+
549
+
By default, only queues that the crawler created itself (the "owned" queue) are purged between runs — a user-supplied queue is never touched unless you explicitly opt in. The `purgeRequestQueue` option in `CrawlerRunOptions` controls this behavior:
550
+
551
+
|`purgeRequestQueue` value | Owned queue (auto-created) | User-supplied queue |
552
+
|---|---|---|
553
+
| omitted (default) | Purged | Not purged |
554
+
|`true`| Purged | Purged |
555
+
|`false`| Not purged | Not purged |
556
+
557
+
```typescript
558
+
// The purge happens automatically between run() calls:
## Storage `.open()` now also accepts `{ id?, name? }`
536
582
537
583
`Dataset.open()`, `KeyValueStore.open()`, and `RequestQueue.open()` previously accepted a single `idOrName?: string` parameter. This was ambiguous — callers couldn't express whether they were opening a storage by its ID or by name.
this.handledRequestsCount=0;// This would've been reset by this._init() further down below, but at that point `handledRequestsCount` could prevent `addRequests` from adding the initial requests
1301
1319
}
1302
1320
@@ -1425,6 +1443,7 @@ export class BasicCrawler<
1425
1443
1426
1444
if(!this.requestQueue){
1427
1445
this.requestQueue=awaitthis._getRequestQueue();
1446
+
this.ownedRequestQueue=this.requestQueue;
1428
1447
this.requestManager=undefined;
1429
1448
}
1430
1449
@@ -2262,6 +2281,11 @@ export class BasicCrawler<
2262
2281
}
2263
2282
2264
2283
privateasync_getRequestQueue(){
2284
+
// The first crawler instance uses the default queue (null identifier);
2285
+
// subsequent instances get their own queue via a unique alias so they don't collide.
0 commit comments