Skip to content

Commit c541b4d

Browse files
committed
avoid need for sorted array by keeping map insertion order consistent with access order
1 parent 6141b13 commit c541b4d

File tree

4 files changed

+20
-29
lines changed

4 files changed

+20
-29
lines changed

library/src/methods/cache/cache.test.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { setTimeout } from 'node:timers/promises';
21
import QuickLRU from 'quick-lru';
32
import { afterAll, beforeEach, describe, expect, test, vi } from 'vitest';
43
import { string } from '../../schemas/index.ts';
@@ -14,25 +13,21 @@ describe('cache', () => {
1413
);
1514
expect(runSpy).toHaveBeenCalledTimes(1);
1615
});
17-
test('should allow custom max size', async () => {
16+
test('should allow custom max size', () => {
1817
const schema = cache(string(), { maxSize: 2 });
1918
expect(schema.options.maxSize).toBe(2);
2019

2120
const fooDataset = schema['~run']({ value: 'foo' }, {});
2221
expect(schema['~run']({ value: 'foo' }, {})).toBe(fooDataset);
2322

24-
// wait for next tick, to prevent same millisecond
25-
await setTimeout(0);
2623
expect(schema['~run']({ value: 'bar' }, {})).toBe(
2724
schema['~run']({ value: 'bar' }, {})
2825
);
2926

30-
await setTimeout(0);
3127
expect(schema['~run']({ value: 'baz' }, {})).toBe(
3228
schema['~run']({ value: 'baz' }, {})
3329
);
3430

35-
await setTimeout(0);
3631
expect(schema['~run']({ value: 'foo' }, {})).not.toBe(fooDataset);
3732
});
3833
describe('should allow custom duration', () => {
@@ -45,6 +40,7 @@ describe('cache', () => {
4540

4641
test('and clear expired values', () => {
4742
const schema = cache(string(), { duration: 1000 });
43+
4844
const fooDataset = schema['~run']({ value: 'foo' }, {});
4945
expect(schema['~run']({ value: 'foo' }, {})).toBe(fooDataset);
5046
vi.advanceTimersByTime(1001);

library/src/methods/cache/cacheAsync.test.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { setTimeout } from 'node:timers/promises';
21
import QuickLRU from 'quick-lru';
32
import { afterAll, beforeEach, describe, expect, test, vi } from 'vitest';
43
import { string } from '../../schemas/index.ts';
@@ -21,18 +20,14 @@ describe('cacheAsync', () => {
2120
const fooDataset = await schema['~run']({ value: 'foo' }, {});
2221
expect(await schema['~run']({ value: 'foo' }, {})).toBe(fooDataset);
2322

24-
// wait for next tick, to prevent same millisecond
25-
await setTimeout(0);
2623
expect(await schema['~run']({ value: 'bar' }, {})).toBe(
2724
await schema['~run']({ value: 'bar' }, {})
2825
);
2926

30-
await setTimeout(0);
3127
expect(await schema['~run']({ value: 'baz' }, {})).toBe(
3228
await schema['~run']({ value: 'baz' }, {})
3329
);
3430

35-
await setTimeout(0);
3631
expect(await schema['~run']({ value: 'foo' }, {})).not.toBe(fooDataset);
3732
});
3833
describe('should allow custom duration', () => {

library/src/utils/_Cache/_Cache.test.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,24 @@
1-
import { setTimeout } from 'node:timers/promises';
21
import { afterAll, beforeEach, describe, expect, test, vi } from 'vitest';
32
import { _Cache } from './_Cache.ts';
43

54
describe('_Cache', () => {
6-
test('should cache values', async () => {
5+
test('should cache values', () => {
76
const cache = new _Cache<string, number>();
87

98
cache.set('foo', 123);
109
expect(cache.get('foo')).toBe(123);
1110

12-
// wait for next tick, to prevent same millisecond
13-
await setTimeout(0);
1411
cache.set('bar', 456);
1512

1613
expect(cache.get('bar')).toBe(456);
1714
// default max size is 1
1815
expect(cache.get('foo')).toBeUndefined();
1916
});
20-
test('should allow deleting', async () => {
17+
test('should allow deleting', () => {
2118
const cache = new _Cache<string, number>();
19+
2220
cache.set('foo', 123);
2321

24-
await setTimeout(0);
2522
cache.set('bar', 456);
2623

2724
cache.delete('foo');
@@ -53,15 +50,13 @@ describe('_Cache', () => {
5350

5451
expect(cache.size).toBe(2);
5552
});
56-
test('should allow custom max size', async () => {
53+
test('should allow custom max size', () => {
5754
const cache = new _Cache<string, number>({ maxSize: 2 });
5855

5956
cache.set('foo', 123);
6057

61-
await setTimeout(0);
6258
cache.set('bar', 456);
6359

64-
await setTimeout(0);
6560
cache.set('baz', 789);
6661

6762
expect(cache.get('foo')).toBeUndefined();

library/src/utils/_Cache/_Cache.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,7 @@ export class _Cache<TKey, TValue> implements BaseCache<TKey, TValue> {
3030
return this['~get'](key)?.value;
3131
}
3232
set(key: TKey, value: TValue): this {
33-
this['~cache'].set(key, {
34-
value,
35-
lastAccess: Date.now(),
36-
});
33+
this['~insert'](key, value);
3734
this['~clearExcess']();
3835
return this;
3936
}
@@ -61,16 +58,24 @@ export class _Cache<TKey, TValue> implements BaseCache<TKey, TValue> {
6158
this['~cache'].delete(key);
6259
return;
6360
}
64-
entry.lastAccess = Date.now();
61+
// move to end
62+
this['~insert'](key, entry.value);
6563
return entry;
6664
}
65+
private '~insert'(key: TKey, value: TValue): void {
66+
// make sure this is always an insertion, not an update
67+
// important for map ordering
68+
this['~cache'].delete(key);
69+
this['~cache'].set(key, {
70+
value,
71+
lastAccess: Date.now(),
72+
});
73+
}
6774
private '~clearExcess'(): void {
6875
if (this.size <= this.maxSize) return;
69-
const entries = Array.from(this['~cache']).sort(
70-
([, a], [, b]) => b.lastAccess - a.lastAccess
71-
);
76+
const keys = this['~cache'].keys();
7277
while (this.size > this.maxSize) {
73-
this['~cache'].delete(entries.pop()![0]);
78+
this['~cache'].delete(keys.next().value!);
7479
}
7580
}
7681
}

0 commit comments

Comments
 (0)