Skip to content

Commit a9ef06a

Browse files
author
Fletcher91
committed
[IMP] Memoize resource fetcher status
1 parent 1ceb6b9 commit a9ef06a

File tree

1 file changed

+51
-25
lines changed

1 file changed

+51
-25
lines changed

src/processor/DataProcessor.ts

Lines changed: 51 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import { ProcessorError } from "./ProcessorError";
4747
import { RequestInitGenerator } from "./RequestInitGenerator";
4848

4949
const SAFE_METHODS = ["GET", "HEAD", "OPTIONS", "CONNECT", "TRACE"];
50+
const FETCHER_CALLBACKS = ["done", "fail", "refresh", "request", "retract"];
5051

5152
async function handleStatus(res: ResponseAndFallbacks): Promise<ResponseAndFallbacks> {
5253
if (res.status === NOT_FOUND) {
@@ -127,7 +128,7 @@ export const emptyRequest = Object.freeze({
127128
requested: false,
128129
status: null,
129130
timesRequested: 0,
130-
});
131+
}) as EmptyRequestStatus;
131132

132133
/**
133134
* The client (User Agent) has closed the connection, e.g. due to CORS or going offline.
@@ -154,6 +155,7 @@ export class DataProcessor {
154155
private readonly requestInitGenerator: RequestInitGenerator;
155156
private readonly mapping: { [k: string]: ResponseTransformer[] };
156157
private readonly requestMap: Map<NamedNode, Promise<Statement[]> | undefined>;
158+
private readonly statusMap: Map<NamedNode, EmptyRequestStatus | FulfilledRequestStatus>;
157159
private readonly requestNotifier?: RequestCallbackHandler;
158160
private readonly store: RDFStore;
159161

@@ -163,11 +165,12 @@ export class DataProcessor {
163165
fetch: window && window.fetch.bind(window),
164166
timeout: this.timeout,
165167
});
166-
if (typeof this.requestNotifier !== "undefined") {
167-
["done", "fail", "refresh", "request", "retract"].forEach((hook) => {
168-
this._fetcher!.addCallback(hook, this.requestNotifier!);
169-
});
170-
}
168+
FETCHER_CALLBACKS.forEach((hook) => {
169+
this._fetcher!.addCallback(hook, this.invalidateCache.bind(this));
170+
if (typeof this.requestNotifier === "function") {
171+
this._fetcher!.addCallback(hook, this.requestNotifier);
172+
}
173+
});
171174
}
172175
return this._fetcher;
173176
}
@@ -183,6 +186,7 @@ export class DataProcessor {
183186
this.requestInitGenerator = opts.requestInitGenerator || new RequestInitGenerator();
184187
this.mapping = opts.mapping || {};
185188
this.requestMap = new Map();
189+
this.statusMap = new Map();
186190
this.store = opts.store;
187191
this.requestNotifier = opts.requestNotifier;
188192
if (opts.fetcher) {
@@ -318,13 +322,17 @@ export class DataProcessor {
318322
*/
319323
public getStatus(iri: NamedNode): EmptyRequestStatus | FulfilledRequestStatus {
320324
const irl = namedNodeByIRI(iri.value.split("#").shift()!);
325+
326+
if (this.statusMap.has(irl)) {
327+
return this.statusMap.get(irl)!;
328+
}
321329
const fetcherStatus = this.fetcher.requested[irl.value];
322330

323331
if (fetcherStatus === undefined) {
324332
if (irl.value in this.fetcher.requested) {
325-
return failedRequest();
333+
return this.memoizeStatus(irl, failedRequest());
326334
}
327-
return emptyRequest as EmptyRequestStatus;
335+
return this.memoizeStatus(irl, emptyRequest);
328336
}
329337

330338
const requests = this.store.match(
@@ -334,18 +342,21 @@ export class DataProcessor {
334342
);
335343
const totalRequested = requests.length;
336344
if (requests.length === 0) {
337-
return emptyRequest as EmptyRequestStatus;
345+
return this.memoizeStatus(irl, emptyRequest);
338346
}
339347
if (fetcherStatus === true) {
340-
return {
341-
lastRequested: new Date(),
342-
requested: true,
343-
status: 202,
344-
timesRequested: totalRequested,
345-
};
348+
return this.memoizeStatus(
349+
irl,
350+
{
351+
lastRequested: new Date(),
352+
requested: true,
353+
status: 202,
354+
timesRequested: totalRequested,
355+
},
356+
);
346357
}
347358
if (fetcherStatus === "timeout") {
348-
return timedOutRequest(totalRequested);
359+
return this.memoizeStatus(irl, timedOutRequest(totalRequested));
349360
}
350361
const requestIRI = requests.pop()!.subject as BlankNode;
351362
const requestObj = anyRDFValue(
@@ -354,7 +365,7 @@ export class DataProcessor {
354365
);
355366

356367
if (!requestObj) {
357-
return emptyRequest as EmptyRequestStatus;
368+
return this.memoizeStatus(irl, emptyRequest);
358369
}
359370

360371
const requestObjData = this.store.statementsFor(requestObj as BlankNode);
@@ -367,17 +378,20 @@ export class DataProcessor {
367378

368379
if (!requestStatus) {
369380
if (fetcherStatus === "done") {
370-
return timedOutRequest(totalRequested);
381+
return this.memoizeStatus(irl, timedOutRequest(totalRequested));
371382
}
372-
return emptyRequest as EmptyRequestStatus;
383+
return this.memoizeStatus(irl, emptyRequest);
373384
}
374385

375-
return {
376-
lastRequested: requestDate ? new Date(requestDate.value) : new Date(0),
377-
requested: true,
378-
status: Number.parseInt(requestStatus.value, 10),
379-
timesRequested: totalRequested,
380-
};
386+
return this.memoizeStatus(
387+
irl,
388+
{
389+
lastRequested: requestDate ? new Date(requestDate.value) : new Date(0),
390+
requested: true,
391+
status: Number.parseInt(requestStatus.value, 10),
392+
timesRequested: totalRequested,
393+
},
394+
);
381395
}
382396

383397
public processExternalResponse(response: Response): Promise<Statement[] | undefined> {
@@ -411,4 +425,16 @@ export class DataProcessor {
411425

412426
return processor(res);
413427
}
428+
429+
private invalidateCache(iri: string | NamedNode, _err?: Error): boolean {
430+
this.statusMap.delete(typeof iri === "string" ? namedNodeByIRI(iri) : iri);
431+
return true;
432+
}
433+
434+
private memoizeStatus(iri: NamedNode,
435+
s: EmptyRequestStatus | FulfilledRequestStatus): EmptyRequestStatus | FulfilledRequestStatus {
436+
this.statusMap.set(iri, s);
437+
438+
return s;
439+
}
414440
}

0 commit comments

Comments
 (0)