Skip to content

Commit b4de091

Browse files
committed
Implement disk cache
1 parent b419ade commit b4de091

File tree

5 files changed

+47
-47
lines changed

5 files changed

+47
-47
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,6 @@ src/svg/*.tsx
144144
_site
145145
.sass-cache
146146
.jekyll-metadata
147+
148+
# Tmp files (cache, etc.)
149+
*.cache

src/utils/memoization/cache.test.ts

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
import cache from '../memoization/cache';
2-
import { reset as cacheReset } from '../memoization/inMemoryCacheStorage';
2+
import { reset as inDiskCacheReset } from '../memoization/inFileCacheStorage';
3+
import { reset as inMemoryCacheReset } from '../memoization/inMemoryCacheStorage';
34
import waitFor from '../timers/waitFor';
45
import { StorageOptions } from './cacheStorage';
56

67
describe(`utils/memoization/cache.ts`, () => {
78
beforeEach(() => {
89
// Silent console log (used by logger.debug)
9-
// @ts-ignore-error
10-
global.console = { debug: jest.fn(), log: jest.fn() };
10+
// @ts-ignore
11+
global.console = global.muteConsole();
1112
});
1213

1314
describe(`cache`, () => {
1415
describe(`should not fetch multiple times in a row but rely on the cache instead`, () => {
15-
beforeEach(() => {
16-
cacheReset();
16+
afterEach(() => {
17+
inMemoryCacheReset();
1718
});
1819
const expectationResult = { key: 'value' };
1920

@@ -62,47 +63,39 @@ describe(`utils/memoization/cache.ts`, () => {
6263
});
6364

6465
describe(`when using file storage (on disk)`, () => {
65-
const storageOptions: StorageOptions = { storage: { type: 'disk', options: { filename: '.test-cache1' } } };
66+
const storageOptions: StorageOptions = { storage: { type: 'disk', options: { filename: 'test-cache1.cache' } } };
67+
const key = 'key';
6668

6769
test(`when using the default TTL`, async () => {
68-
const cacheHitsBefore = require('../memoization/inMemoryCacheStorage').cacheHits;
69-
const cacheMissBefore = require('../memoization/inMemoryCacheStorage').cacheMiss;
70-
expect(await cache('key', async () => {
70+
expect(await cache(key, async () => {
7171
await waitFor(1);
7272
return expectationResult;
7373
}, storageOptions)).toEqual(expectationResult);
74-
expect(await cache('key', async () => {
74+
expect(await cache(key, async () => {
7575
await waitFor(1);
7676
return Promise.resolve(expectationResult);
7777
}, storageOptions)).toEqual(expectationResult);
7878

79-
const cacheHitsAfter = require('../memoization/inMemoryCacheStorage').cacheHits;
80-
const cacheMissAfter = require('../memoization/inMemoryCacheStorage').cacheMiss;
81-
expect(cacheHitsAfter).toBeGreaterThan(cacheHitsBefore);
82-
expect(cacheMissAfter).toEqual(cacheMissBefore + 1); // Cache should have been missed only for the first call
79+
await inDiskCacheReset(key, storageOptions);
8380
});
8481

8582
describe(`should fetch multiple times and miss the cache`, () => {
8683
const expectationResult = { key2: 'value2' };
84+
const key = 'key2';
8785

8886
test(`when using TTL of 1 second and waiting more than 1 second between calls`, async () => {
89-
const cacheHitsBefore = require('../memoization/inMemoryCacheStorage').cacheHits;
90-
const cacheMissBefore = require('../memoization/inMemoryCacheStorage').cacheMiss;
9187
await waitFor(1001);
92-
expect(await cache('key2', async () => {
88+
expect(await cache(key, async () => {
9389
await waitFor(1);
9490
return Promise.resolve(expectationResult);
9591
}, storageOptions)).toEqual(expectationResult);
9692
await waitFor(1001);
97-
expect(await cache('key2', async () => {
93+
expect(await cache(key, async () => {
9894
await waitFor(1);
9995
return Promise.resolve(expectationResult);
10096
}, storageOptions)).toEqual(expectationResult);
10197

102-
const cacheHitsAfter = require('../memoization/inMemoryCacheStorage').cacheHits;
103-
const cacheMissAfter = require('../memoization/inMemoryCacheStorage').cacheMiss;
104-
expect(cacheHitsAfter).toEqual(cacheHitsBefore + 1);
105-
expect(cacheMissAfter).toBeGreaterThan(cacheMissBefore);
98+
await inDiskCacheReset(key, storageOptions);
10699
});
107100
});
108101
});

src/utils/memoization/cacheStorage.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export type CachedItem<T = any> = {
1111
export type StorageOptions<Options extends {} = {}> = Options;
1212
export type Get<T = any, Options extends StorageOptions = {}> = <T>(key: string, options?: Options) => Promise<CachedItem<T>>;
1313
export type Set<T = any, Options extends StorageOptions = {}> = <T>(key: string, item: T, options?: Options) => Promise<T>;
14-
export type Reset<> = () => Promise<void>;
14+
export type Reset<Options extends StorageOptions = {}> = (string?, options?: Options) => Promise<void>;
1515
export type Cache<T = any> = {
1616
[key: string]: CachedItem<T>;
1717
}

src/utils/memoization/inFileCacheStorage.ts

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,32 @@
11
import path from 'path';
2-
import { readFile, writeFile } from '../node/fs-utils';
3-
import {
4-
CachedItem,
5-
CacheHits,
6-
CacheMiss,
7-
CacheStorage as GenericCacheStorage,
8-
Get,
9-
Reset,
10-
Set,
11-
StorageOptions as GenericStorageOptions,
12-
} from './cacheStorage';
2+
import { deleteFile, readFile, writeFile } from '../node/fs-utils';
3+
import { CachedItem, CacheStorage as GenericCacheStorage, Get, Reset, Set, StorageOptions as GenericStorageOptions } from './cacheStorage';
134

145
type StorageOptions = GenericStorageOptions<{ filename: string }>;
156
type CacheStorage = GenericCacheStorage<any, StorageOptions>;
167

17-
export let cacheHits: CacheHits = 0;
18-
export let cacheMiss: CacheMiss = 0;
8+
const PREFIX = '.nrn';
199

2010
export const get: Get = async <T>(key: string, options: StorageOptions): Promise<CachedItem<T>> => {
2111
const { filename } = options;
22-
const content = await readFile(path.resolve(filename + '-' + key), 'utf8');
12+
let content;
13+
14+
try {
15+
content = await readFile(path.resolve(PREFIX + '-' + key + '-' + filename), 'utf8');
16+
} catch (e) {
17+
// File doesn't exist (normal when cache has never been written)
18+
}
19+
20+
let cachedItem: CachedItem;
21+
2322
console.log('content', content);
24-
const cachedItem: CachedItem = JSON.parse(content);
23+
try {
24+
cachedItem = JSON.parse(content);
25+
console.log('cachedItem', cachedItem);
2526

26-
if (typeof cachedItem !== 'undefined') {
27-
++cacheHits;
27+
} catch (e) {
28+
console.error(e);
29+
// TODO sentry
2830
}
2931

3032
return cachedItem;
@@ -37,18 +39,19 @@ export const set: Set = async <T>(key: string, item: T, options: StorageOptions)
3739
value: item,
3840
};
3941

40-
++cacheMiss;
41-
42-
await writeFile(filename + '-' + key, JSON.stringify(cachedItem), 'utf8');
42+
await writeFile(path.resolve(PREFIX + '-' + key + '-' + filename), JSON.stringify(cachedItem), 'utf8');
4343

4444
return item;
4545
};
4646

47-
export const reset: Reset = (): Promise<void> => {
48-
cacheHits = 0;
49-
cacheMiss = 0;
50-
// TODO
47+
export const reset: Reset = async (key, options: StorageOptions): Promise<void> => {
48+
const { filename } = options;
5149

50+
try {
51+
await deleteFile(PREFIX + '-' + key + '-' + filename);
52+
} catch (e) {
53+
// File doesn't exist (normal when cache has never been written)
54+
}
5255
return;
5356
};
5457

src/utils/node/fs-utils.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ import { promisify } from 'util';
33

44
export const readFile = promisify(fs.readFile);
55
export const writeFile = promisify(fs.writeFile);
6+
export const deleteFile = promisify(fs.unlink);

0 commit comments

Comments
 (0)