Skip to content

Commit fe21269

Browse files
authored
feat: sqlite add set and minor cleanup (#4018)
* feat: sqlite add set and minor cleanup * fixup
1 parent a364e7c commit fe21269

File tree

3 files changed

+126
-71
lines changed

3 files changed

+126
-71
lines changed

lib/cache/sqlite-cache-store.js

+81-69
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,18 @@ const MAX_ENTRY_SIZE = 2 * 1000 * 1000 * 1000
1515
* @implements {CacheStore}
1616
*
1717
* @typedef {{
18-
* id: Readonly<number>
19-
* headers?: Record<string, string | string[]>
20-
* vary?: string | object
21-
* body: string
22-
* } & import('../../types/cache-interceptor.d.ts').default.CacheValue} SqliteStoreValue
18+
* id: Readonly<number>,
19+
* body?: Uint8Array
20+
* statusCode: number
21+
* statusMessage: string
22+
* headers?: string
23+
* vary?: string
24+
* etag?: string
25+
* cacheControlDirectives?: string
26+
* cachedAt: number
27+
* staleAt: number
28+
* deleteAt: number
29+
* }} SqliteStoreValue
2330
*/
2431
module.exports = class SqliteCacheStore {
2532
#maxEntrySize = MAX_ENTRY_SIZE
@@ -217,36 +224,78 @@ module.exports = class SqliteCacheStore {
217224

218225
/**
219226
* @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} key
220-
* @returns {import('../../types/cache-interceptor.d.ts').default.GetResult | undefined}
227+
* @returns {(import('../../types/cache-interceptor.d.ts').default.GetResult & { body?: Buffer }) | undefined}
221228
*/
222229
get (key) {
223230
assertCacheKey(key)
224231

225232
const value = this.#findValue(key)
233+
return value
234+
? {
235+
body: value.body ? Buffer.from(value.body.buffer) : undefined,
236+
statusCode: value.statusCode,
237+
statusMessage: value.statusMessage,
238+
headers: value.headers ? JSON.parse(value.headers) : undefined,
239+
etag: value.etag ? value.etag : undefined,
240+
vary: value.vary ? JSON.parse(value.vary) : undefined,
241+
cacheControlDirectives: value.cacheControlDirectives
242+
? JSON.parse(value.cacheControlDirectives)
243+
: undefined,
244+
cachedAt: value.cachedAt,
245+
staleAt: value.staleAt,
246+
deleteAt: value.deleteAt
247+
}
248+
: undefined
249+
}
226250

227-
if (!value) {
228-
return undefined
229-
}
251+
/**
252+
* @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} key
253+
* @param {import('../../types/cache-interceptor.d.ts').default.CacheValue & { body: null | Buffer | Array<Buffer>}} value
254+
*/
255+
set (key, value) {
256+
assertCacheKey(key)
230257

231-
/**
232-
* @type {import('../../types/cache-interceptor.d.ts').default.GetResult}
233-
*/
234-
const result = {
235-
body: Buffer.from(value.body),
236-
statusCode: value.statusCode,
237-
statusMessage: value.statusMessage,
238-
headers: value.headers ? JSON.parse(value.headers) : undefined,
239-
etag: value.etag ? value.etag : undefined,
240-
vary: value.vary ?? undefined,
241-
cacheControlDirectives: value.cacheControlDirectives
242-
? JSON.parse(value.cacheControlDirectives)
243-
: undefined,
244-
cachedAt: value.cachedAt,
245-
staleAt: value.staleAt,
246-
deleteAt: value.deleteAt
258+
const url = this.#makeValueUrl(key)
259+
const body = Array.isArray(value.body) ? Buffer.concat(value.body) : value.body
260+
const size = body?.byteLength
261+
262+
if (size && size > this.#maxEntrySize) {
263+
return
247264
}
248265

249-
return result
266+
const existingValue = this.#findValue(key, true)
267+
if (existingValue) {
268+
// Updating an existing response, let's overwrite it
269+
this.#updateValueQuery.run(
270+
body,
271+
value.deleteAt,
272+
value.statusCode,
273+
value.statusMessage,
274+
value.headers ? JSON.stringify(value.headers) : null,
275+
value.etag ? value.etag : null,
276+
value.cacheControlDirectives ? JSON.stringify(value.cacheControlDirectives) : null,
277+
value.cachedAt,
278+
value.staleAt,
279+
existingValue.id
280+
)
281+
} else {
282+
this.#prune()
283+
// New response, let's insert it
284+
this.#insertValueQuery.run(
285+
url,
286+
key.method,
287+
body,
288+
value.deleteAt,
289+
value.statusCode,
290+
value.statusMessage,
291+
value.headers ? JSON.stringify(value.headers) : null,
292+
value.etag ? value.etag : null,
293+
value.cacheControlDirectives ? JSON.stringify(value.cacheControlDirectives) : null,
294+
value.vary ? JSON.stringify(value.vary) : null,
295+
value.cachedAt,
296+
value.staleAt
297+
)
298+
}
250299
}
251300

252301
/**
@@ -258,7 +307,6 @@ module.exports = class SqliteCacheStore {
258307
assertCacheKey(key)
259308
assertCacheValue(value)
260309

261-
const url = this.#makeValueUrl(key)
262310
let size = 0
263311
/**
264312
* @type {Buffer[] | null}
@@ -267,11 +315,8 @@ module.exports = class SqliteCacheStore {
267315
const store = this
268316

269317
return new Writable({
318+
decodeStrings: true,
270319
write (chunk, encoding, callback) {
271-
if (typeof chunk === 'string') {
272-
chunk = Buffer.from(chunk, encoding)
273-
}
274-
275320
size += chunk.byteLength
276321

277322
if (size < store.#maxEntrySize) {
@@ -283,40 +328,7 @@ module.exports = class SqliteCacheStore {
283328
callback()
284329
},
285330
final (callback) {
286-
const existingValue = store.#findValue(key, true)
287-
if (existingValue) {
288-
// Updating an existing response, let's overwrite it
289-
store.#updateValueQuery.run(
290-
Buffer.concat(body),
291-
value.deleteAt,
292-
value.statusCode,
293-
value.statusMessage,
294-
value.headers ? JSON.stringify(value.headers) : null,
295-
value.etag ? value.etag : null,
296-
value.cacheControlDirectives ? JSON.stringify(value.cacheControlDirectives) : null,
297-
value.cachedAt,
298-
value.staleAt,
299-
existingValue.id
300-
)
301-
} else {
302-
store.#prune()
303-
// New response, let's insert it
304-
store.#insertValueQuery.run(
305-
url,
306-
key.method,
307-
Buffer.concat(body),
308-
value.deleteAt,
309-
value.statusCode,
310-
value.statusMessage,
311-
value.headers ? JSON.stringify(value.headers) : null,
312-
value.etag ? value.etag : null,
313-
value.cacheControlDirectives ? JSON.stringify(value.cacheControlDirectives) : null,
314-
value.vary ? JSON.stringify(value.vary) : null,
315-
value.cachedAt,
316-
value.staleAt
317-
)
318-
}
319-
331+
store.set(key, { ...value, body })
320332
callback()
321333
}
322334
})
@@ -375,7 +387,7 @@ module.exports = class SqliteCacheStore {
375387
/**
376388
* @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} key
377389
* @param {boolean} [canBeExpired=false]
378-
* @returns {(SqliteStoreValue & { vary?: Record<string, string[]> }) | undefined}
390+
* @returns {SqliteStoreValue | undefined}
379391
*/
380392
#findValue (key, canBeExpired = false) {
381393
const url = this.#makeValueUrl(key)
@@ -403,10 +415,10 @@ module.exports = class SqliteCacheStore {
403415
return undefined
404416
}
405417

406-
value.vary = JSON.parse(value.vary)
418+
const vary = JSON.parse(value.vary)
407419

408-
for (const header in value.vary) {
409-
if (!headerValueEquals(headers[header], value.vary[header])) {
420+
for (const header in vary) {
421+
if (!headerValueEquals(headers[header], vary[header])) {
410422
matches = false
411423
break
412424
}

test/cache-interceptor/sqlite-cache-store-tests.js

+44-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use strict'
22

33
const { test, skip } = require('node:test')
4-
const { notEqual, strictEqual } = require('node:assert')
4+
const { notEqual, strictEqual, deepStrictEqual } = require('node:assert')
55
const { rm } = require('node:fs/promises')
66
const { cacheStoreTests, writeBody, compareGetResults } = require('./cache-store-test-utils.js')
77

@@ -179,3 +179,46 @@ test('SqliteCacheStore two writes', async (t) => {
179179
writeBody(writable, body)
180180
}
181181
})
182+
183+
test('SqliteCacheStore write & read', async (t) => {
184+
if (!hasSqlite) {
185+
t.skip()
186+
return
187+
}
188+
189+
const SqliteCacheStore = require('../../lib/cache/sqlite-cache-store.js')
190+
191+
const store = new SqliteCacheStore({
192+
maxCount: 10
193+
})
194+
195+
/**
196+
* @type {import('../../types/cache-interceptor.d.ts').default.CacheKey}
197+
*/
198+
const key = {
199+
origin: 'localhost',
200+
path: '/',
201+
method: 'GET',
202+
headers: {}
203+
}
204+
205+
/**
206+
* @type {import('../../types/cache-interceptor.d.ts').default.CacheValue & { body: Buffer }}
207+
*/
208+
const value = {
209+
statusCode: 200,
210+
statusMessage: '',
211+
headers: { foo: 'bar' },
212+
cacheControlDirectives: { 'max-stale': 0 },
213+
cachedAt: Date.now(),
214+
staleAt: Date.now() + 10000,
215+
deleteAt: Date.now() + 20000,
216+
body: Buffer.from('asd'),
217+
etag: undefined,
218+
vary: undefined
219+
}
220+
221+
store.set(key, value)
222+
223+
deepStrictEqual(store.get(key), value)
224+
})

types/cache-interceptor.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ declare namespace CacheHandler {
9090
headers: Record<string, string | string[]>
9191
vary?: Record<string, string | string[]>
9292
etag?: string
93-
body: null | Readable | Iterable<Buffer> | AsyncIterable<Buffer> | Buffer | Iterable<string> | AsyncIterable<string> | string
93+
body?: Readable | Iterable<Buffer> | AsyncIterable<Buffer> | Buffer | Iterable<string> | AsyncIterable<string> | string
9494
cacheControlDirectives: CacheControlDirectives,
9595
cachedAt: number
9696
staleAt: number

0 commit comments

Comments
 (0)