Skip to content

Commit 624128b

Browse files
authored
Further improvements to awaitPageResources to avoid stuck requests (#1066)
- In #1063, addressed requests that were being removed too quickly, but also need to be more careful with pending requests being stuck - Unset `.intercepting` after handleFetchResponse() since its now being checked before early cleanup - As an extra precaution, remove requests add a way to track requests that have been 'stuck', eg. have size > 0, but have not changed for 20+ seconds after page has been closed. Hopefully this will not be hit much at all, but adding as extra check. - Don't mark request as fetchContinued if its a redirect, as request will be repeated with same id.
1 parent 217b8bd commit 624128b

2 files changed

Lines changed: 80 additions & 21 deletions

File tree

src/util/recorder.ts

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ const MAX_NETWORK_LOAD_SIZE = 200_000_000;
4444

4545
const TAKE_STREAM_BUFF_SIZE = 1024 * 64;
4646

47+
const PENDING_UNCHANGED_COUNT = 4;
48+
4749
const ASYNC_FETCH_DUPE_KEY = "s:fetchdupe";
4850

4951
const WRITE_DUPE_KEY = "s:writedupe";
@@ -639,6 +641,13 @@ export class Recorder extends EventEmitter {
639641
{ url, ...formatErr(e), ...this.logDetails },
640642
"recorder",
641643
);
644+
} finally {
645+
if (networkId) {
646+
const reqresp = this.pendingReqResp(networkId, true);
647+
if (reqresp) {
648+
reqresp.intercepting = false;
649+
}
650+
}
642651
}
643652

644653
if (!continued) {
@@ -771,10 +780,13 @@ export class Recorder extends EventEmitter {
771780
return false;
772781
}
773782

774-
reqresp.fetchContinued = true;
775-
776783
reqresp.fillFetchRequestPaused(params);
777784

785+
// if this is a redirect, requestPaused() will be called with same id, so don't mark as continued yet
786+
if (!reqresp.isRedirectStatus()) {
787+
reqresp.fetchContinued = true;
788+
}
789+
778790
if (
779791
url === this.pageUrl &&
780792
(!this.pageInfo.ts ||
@@ -880,6 +892,7 @@ export class Recorder extends EventEmitter {
880892
// If page is at dedupePagesMinDepth or higher and HTML is a duplicate,
881893
// write a revisit record, track pages as a duplicate, and abort the page
882894
if (
895+
!streamingConsume &&
883896
url === this.pageUrl &&
884897
reqresp.payload &&
885898
this.dedupePagesMinDepth >= 0 &&
@@ -1131,33 +1144,29 @@ export class Recorder extends EventEmitter {
11311144

11321145
let numPending = this.pendingRequests.size;
11331146

1134-
let pending = [];
11351147
while (
11361148
numPending &&
11371149
!this.pageFinished &&
11381150
!this.crawler.interruptReason &&
11391151
!this.crawler.postCrawling
11401152
) {
1141-
pending = [];
1153+
const pending = [];
11421154
for (const [requestId, reqresp] of this.pendingRequests.entries()) {
1143-
const url = reqresp.url || "";
1144-
const entry: {
1145-
requestId: string;
1146-
url: string;
1147-
expectedSize?: number;
1148-
readSize?: number;
1149-
resourceType?: string;
1150-
} = { requestId, url };
1151-
if (reqresp.expectedSize) {
1152-
entry.expectedSize = reqresp.expectedSize;
1153-
}
1154-
if (reqresp.readSize) {
1155-
entry.readSize = reqresp.readSize;
1156-
}
1157-
if (reqresp.resourceType) {
1158-
entry.resourceType = reqresp.resourceType;
1155+
if (reqresp.unchangedSizeCount() >= PENDING_UNCHANGED_COUNT) {
1156+
if (reqresp.currSize) {
1157+
logger.debug(
1158+
"Async request appears unchanged, serializing and removing",
1159+
{ lastSize: reqresp.lastSize },
1160+
);
1161+
await this.serializeToWARC(reqresp);
1162+
} else {
1163+
logger.debug("Async request appears empty, removing", {
1164+
lastSize: reqresp.lastSize,
1165+
});
1166+
}
1167+
this.removeReqResp(requestId);
11591168
}
1160-
pending.push(entry);
1169+
pending.push(reqresp.toJSON());
11611170
}
11621171

11631172
logger.debug(
@@ -1588,6 +1597,7 @@ export class Recorder extends EventEmitter {
15881597
stream: Protocol.IO.StreamHandle,
15891598
) {
15901599
let size = 0;
1600+
reqresp.currSize = 0;
15911601
try {
15921602
while (true) {
15931603
const { data, base64Encoded, eof } = await cdp.send("IO.read", {
@@ -1597,6 +1607,7 @@ export class Recorder extends EventEmitter {
15971607
const buff = Buffer.from(data, base64Encoded ? "base64" : "utf-8");
15981608

15991609
size += buff.length;
1610+
reqresp.currSize += buff.length;
16001611
yield buff;
16011612

16021613
if (eof) {
@@ -2172,6 +2183,7 @@ class AsyncFetcher {
21722183
try {
21732184
for await (const value of reader) {
21742185
size += value.length;
2186+
this.reqresp.currSize += value.length;
21752187
yield value;
21762188
}
21772189
} catch (e) {

src/util/reqresp.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,11 @@ export class RequestResponseInfo {
8686
// set to add truncated message
8787
truncated?: string;
8888

89+
// for pending request tracking
90+
currSize = 0;
91+
lastSize = -1;
92+
unchangedCount = 0;
93+
8994
constructor(requestId: string) {
9095
this.requestId = requestId;
9196
}
@@ -436,6 +441,48 @@ export class RequestResponseInfo {
436441
// replace newlines with spaces
437442
return value.replace(/\n/g, ", ");
438443
}
444+
445+
unchangedSizeCount() {
446+
// increment counter if current size of request not changed since the size
447+
// was last checked, and increment counter
448+
if (this.lastSize === this.currSize) {
449+
this.unchangedCount++;
450+
}
451+
this.lastSize = this.currSize;
452+
return this.unchangedCount;
453+
}
454+
455+
toJSON(): string {
456+
const entry: {
457+
requestId: string;
458+
url: string;
459+
currSize: number;
460+
lastSize: number;
461+
unchangedCount: number;
462+
resourceType?: string;
463+
status?: string;
464+
} = {
465+
url: this.url,
466+
requestId: this.requestId,
467+
currSize: this.currSize,
468+
lastSize: this.lastSize,
469+
unchangedCount: this.unchangedCount,
470+
};
471+
472+
if (this.resourceType) {
473+
entry.resourceType = this.resourceType;
474+
}
475+
476+
if (this.intercepting) {
477+
entry.status = "intercepting";
478+
} else if (this.asyncLoading) {
479+
entry.status = "async";
480+
} else {
481+
entry.status = "unknown";
482+
}
483+
484+
return JSON.stringify(entry);
485+
}
439486
}
440487

441488
export function isHTMLMime(mime: string) {

0 commit comments

Comments
 (0)