@nxtedition/nxt-undici 7.3.9 → 7.3.11
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/lib/sqlite-cache-store.js +124 -34
- package/package.json +1 -1
|
@@ -63,6 +63,9 @@ export class SqliteCacheStore {
|
|
|
63
63
|
*/
|
|
64
64
|
#evictQuery
|
|
65
65
|
|
|
66
|
+
#insertBatch = []
|
|
67
|
+
#closed = false
|
|
68
|
+
|
|
66
69
|
/**
|
|
67
70
|
* @param {import('undici-types/cache-interceptor.d.ts').default.SqliteCacheStoreOpts & { maxSize?: number } | undefined} opts
|
|
68
71
|
*/
|
|
@@ -123,7 +126,7 @@ export class SqliteCacheStore {
|
|
|
123
126
|
AND start <= ?
|
|
124
127
|
AND deleteAt > ?
|
|
125
128
|
ORDER BY
|
|
126
|
-
|
|
129
|
+
cachedAt DESC
|
|
127
130
|
`)
|
|
128
131
|
|
|
129
132
|
this.#insertValueQuery = this.#db.prepare(`
|
|
@@ -170,6 +173,10 @@ export class SqliteCacheStore {
|
|
|
170
173
|
|
|
171
174
|
close() {
|
|
172
175
|
stores.delete(this)
|
|
176
|
+
if (this.#insertBatch.length > 0) {
|
|
177
|
+
this.#flush()
|
|
178
|
+
}
|
|
179
|
+
this.#closed = true
|
|
173
180
|
this.#db.close()
|
|
174
181
|
}
|
|
175
182
|
|
|
@@ -180,6 +187,10 @@ export class SqliteCacheStore {
|
|
|
180
187
|
get(key) {
|
|
181
188
|
assertCacheKey(key)
|
|
182
189
|
|
|
190
|
+
if (this.#closed) {
|
|
191
|
+
return undefined
|
|
192
|
+
}
|
|
193
|
+
|
|
183
194
|
const value = this.#findValue(key)
|
|
184
195
|
return value ? makeResult(value) : undefined
|
|
185
196
|
}
|
|
@@ -220,39 +231,106 @@ export class SqliteCacheStore {
|
|
|
220
231
|
)
|
|
221
232
|
}
|
|
222
233
|
|
|
234
|
+
if (this.#closed) {
|
|
235
|
+
return
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (this.#insertBatch.length === 0) {
|
|
239
|
+
setImmediate(this.#flush)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
this.#insertBatch.push({
|
|
243
|
+
url: makeValueUrl(key),
|
|
244
|
+
method: key.method,
|
|
245
|
+
body,
|
|
246
|
+
start: value.start,
|
|
247
|
+
end: value.end,
|
|
248
|
+
deleteAt: value.deleteAt,
|
|
249
|
+
statusCode: value.statusCode,
|
|
250
|
+
statusMessage: value.statusMessage,
|
|
251
|
+
headers: value.headers ? JSON.stringify(value.headers) : null,
|
|
252
|
+
etag: value.etag != null ? value.etag : null,
|
|
253
|
+
cacheControlDirectives: value.cacheControlDirectives
|
|
254
|
+
? JSON.stringify(value.cacheControlDirectives)
|
|
255
|
+
: null,
|
|
256
|
+
vary: value.vary ? JSON.stringify(value.vary) : null,
|
|
257
|
+
cachedAt: value.cachedAt,
|
|
258
|
+
staleAt: value.staleAt,
|
|
259
|
+
})
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
#flush = () => {
|
|
263
|
+
if (this.#insertBatch.length === 0) return
|
|
264
|
+
if (this.#closed) {
|
|
265
|
+
this.#insertBatch.length = 0
|
|
266
|
+
return
|
|
267
|
+
}
|
|
223
268
|
try {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
269
|
+
const startTime = performance.now()
|
|
270
|
+
for (let retryCount = 0; true; retryCount++) {
|
|
271
|
+
this.#db.exec('BEGIN')
|
|
272
|
+
let n = 0
|
|
227
273
|
try {
|
|
228
|
-
this.#
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
274
|
+
while (n < this.#insertBatch.length) {
|
|
275
|
+
const {
|
|
276
|
+
url,
|
|
277
|
+
method,
|
|
278
|
+
body,
|
|
279
|
+
start,
|
|
280
|
+
end,
|
|
281
|
+
deleteAt,
|
|
282
|
+
statusCode,
|
|
283
|
+
statusMessage,
|
|
284
|
+
headers,
|
|
285
|
+
etag,
|
|
286
|
+
cacheControlDirectives,
|
|
287
|
+
vary,
|
|
288
|
+
cachedAt,
|
|
289
|
+
staleAt,
|
|
290
|
+
} = this.#insertBatch[n++]
|
|
291
|
+
this.#insertValueQuery.run(
|
|
292
|
+
url,
|
|
293
|
+
method,
|
|
294
|
+
body,
|
|
295
|
+
start,
|
|
296
|
+
end,
|
|
297
|
+
deleteAt,
|
|
298
|
+
statusCode,
|
|
299
|
+
statusMessage,
|
|
300
|
+
headers,
|
|
301
|
+
etag,
|
|
302
|
+
cacheControlDirectives,
|
|
303
|
+
vary,
|
|
304
|
+
cachedAt,
|
|
305
|
+
staleAt,
|
|
306
|
+
)
|
|
307
|
+
if ((n & 0xf) === 0 && performance.now() - startTime > 10) {
|
|
308
|
+
break
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
this.#db.exec('COMMIT')
|
|
312
|
+
this.#insertBatch.splice(0, n)
|
|
313
|
+
break
|
|
314
|
+
} catch (err) {
|
|
315
|
+
// ROLLBACK is required: a failed statement leaves the connection with
|
|
316
|
+
// an open transaction; without it the next BEGIN would throw.
|
|
317
|
+
this.#db.exec('ROLLBACK')
|
|
318
|
+
if (err?.errcode === 13 /* SQLITE_FULL */ && retryCount < 3) {
|
|
319
|
+
this.#evictQuery.run(256)
|
|
320
|
+
} else {
|
|
321
|
+
this.#insertBatch.splice(0, n)
|
|
322
|
+
throw err
|
|
323
|
+
}
|
|
232
324
|
}
|
|
233
|
-
} else {
|
|
234
|
-
process.emitWarning(err)
|
|
235
325
|
}
|
|
326
|
+
} catch (err) {
|
|
327
|
+
process.emitWarning(err)
|
|
236
328
|
}
|
|
237
|
-
}
|
|
238
329
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
body,
|
|
244
|
-
value.start,
|
|
245
|
-
value.end,
|
|
246
|
-
value.deleteAt,
|
|
247
|
-
value.statusCode,
|
|
248
|
-
value.statusMessage,
|
|
249
|
-
value.headers ? JSON.stringify(value.headers) : null,
|
|
250
|
-
value.etag != null ? value.etag : null,
|
|
251
|
-
value.cacheControlDirectives ? JSON.stringify(value.cacheControlDirectives) : null,
|
|
252
|
-
value.vary ? JSON.stringify(value.vary) : null,
|
|
253
|
-
value.cachedAt,
|
|
254
|
-
value.staleAt,
|
|
255
|
-
)
|
|
330
|
+
if (this.#insertBatch.length > 0) {
|
|
331
|
+
// If we weren't able to flush the entire batch within the time limit, schedule another flush.
|
|
332
|
+
setImmediate(this.#flush)
|
|
333
|
+
}
|
|
256
334
|
}
|
|
257
335
|
|
|
258
336
|
/**
|
|
@@ -272,20 +350,32 @@ export class SqliteCacheStore {
|
|
|
272
350
|
return undefined
|
|
273
351
|
}
|
|
274
352
|
|
|
353
|
+
const url = makeValueUrl(key)
|
|
354
|
+
const now = getFastNow()
|
|
355
|
+
const requestedStart = range?.start ?? 0
|
|
356
|
+
|
|
275
357
|
/**
|
|
276
358
|
* @type {SqliteStoreValue[]}
|
|
277
359
|
*/
|
|
278
|
-
const values = this.#getValuesQuery.all(
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
360
|
+
const values = this.#getValuesQuery.all(url, method, requestedStart, now)
|
|
361
|
+
|
|
362
|
+
for (const entry of this.#insertBatch) {
|
|
363
|
+
if (
|
|
364
|
+
entry.url === url &&
|
|
365
|
+
entry.method === method &&
|
|
366
|
+
entry.start <= requestedStart &&
|
|
367
|
+
entry.deleteAt > now
|
|
368
|
+
) {
|
|
369
|
+
values.push(entry)
|
|
370
|
+
}
|
|
371
|
+
}
|
|
284
372
|
|
|
285
373
|
if (values.length === 0) {
|
|
286
374
|
return undefined
|
|
287
375
|
}
|
|
288
376
|
|
|
377
|
+
values.sort((a, b) => b.cachedAt - a.cachedAt)
|
|
378
|
+
|
|
289
379
|
for (const value of values) {
|
|
290
380
|
// TODO (fix): Allow full and partial match?
|
|
291
381
|
if (range && (range.start !== value.start || range.end !== value.end)) {
|