Skip to content

Commit

Permalink
Merge pull request #1890 from belgattitude/lru-cache-next-version
Browse files Browse the repository at this point in the history
Lru cache next version
  • Loading branch information
belgattitude authored Feb 4, 2025
2 parents 8c85e7a + 58d3c90 commit 1267907
Show file tree
Hide file tree
Showing 18 changed files with 501 additions and 452 deletions.
5 changes: 5 additions & 0 deletions .changeset/clean-boxes-reflect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@httpx/lru": minor
---

Rename getOrInsert into getOrSet (BC)
5 changes: 5 additions & 0 deletions .changeset/gentle-wasps-notice.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@httpx/lru": patch
---

Add benchmarks for iterators and peek
5 changes: 5 additions & 0 deletions .changeset/lovely-crews-speak.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@httpx/lru": minor
---

LRU.clear now returns the number of cleared items
5 changes: 5 additions & 0 deletions .changeset/purple-terms-pump.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@httpx/lru": minor
---

Allow CacheKey type to be a number (still default to string)
5 changes: 5 additions & 0 deletions .changeset/tame-stingrays-attend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@httpx/lru": minor
---

Export BaseCache interface for customization
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ Fast and lightweight utility functions to check if a value is a plain object.

Fast and lightweight lru caches collection


[![npm](https://img.shields.io/npm/v/@httpx/lru?style=for-the-badge&label=Npm&labelColor=444&color=informational)](https://www.npmjs.com/package/@httpx/lru)
[![changelog](https://img.shields.io/static/v1?label=&message=changelog&logo=github&style=for-the-badge&labelColor=444&color=informational)](https://github.com/belgattitude/httpx/blob/main/packages/lru/CHANGELOG.md)
[![codecov](https://img.shields.io/codecov/c/github/belgattitude/httpx?logo=codecov&label=Unit&flag=httpx-lru-unit&style=for-the-badge&labelColor=444)](https://app.codecov.io/gh/belgattitude/httpx/tree/main/packages%2Flru)
Expand Down
2 changes: 1 addition & 1 deletion devtools/vitest/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
},
"dependencies": {
"@belgattitude/eslint-config-bases": "6.21.0",
"@cloudflare/vitest-pool-workers": "0.6.11",
"@cloudflare/vitest-pool-workers": "0.6.12",
"@codspeed/vitest-plugin": "4.0.0",
"@edge-runtime/vm": "5.0.0",
"@vitest/browser": "3.0.5",
Expand Down
16 changes: 8 additions & 8 deletions examples/nextjs-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,31 +35,31 @@
"react": "19.0.0",
"react-dom": "19.0.0",
"superjson": "2.2.2",
"tailwind-merge": "2.6.0",
"tailwind-merge": "3.0.1",
"zod": "3.24.1"
},
"devDependencies": {
"@belgattitude/eslint-config-bases": "6.21.0",
"@tailwindcss/postcss": "4.0.1",
"@types/node": "22.12.0",
"@tailwindcss/postcss": "4.0.3",
"@types/node": "22.13.1",
"@types/react": "19.0.8",
"@types/react-dom": "19.0.3",
"@vitejs/plugin-react-swc": "3.7.2",
"@vitest/coverage-v8": "3.0.4",
"@vitest/ui": "3.0.4",
"@vitest/coverage-v8": "3.0.5",
"@vitest/ui": "3.0.5",
"cross-env": "7.0.3",
"eslint": "8.57.1",
"eslint-config-next": "15.1.6",
"happy-dom": "16.7.3",
"happy-dom": "16.8.1",
"postcss": "8.5.1",
"prettier": "3.4.2",
"rimraf": "6.0.1",
"tailwindcss": "4.0.1",
"tailwindcss": "4.0.3",
"typescript": "5.7.3",
"vite": "6.0.11",
"vite-plugin-svgr": "4.3.0",
"vite-tsconfig-paths": "5.1.4",
"vitest": "3.0.4",
"vitest": "3.0.5",
"webpack": "5.97.1"
}
}
2 changes: 1 addition & 1 deletion integrations/prisma-exception/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
"@size-limit/file": "11.1.6",
"@size-limit/webpack": "11.1.6",
"@size-limit/webpack-why": "11.1.6",
"@types/node": "22.13.0",
"@types/node": "22.13.1",
"@vitest/coverage-istanbul": "3.0.5",
"@vitest/ui": "3.0.5",
"browserslist": "4.24.4",
Expand Down
2 changes: 1 addition & 1 deletion packages/exception/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@
"@size-limit/file": "11.1.6",
"@size-limit/webpack": "11.1.6",
"@size-limit/webpack-why": "11.1.6",
"@types/node": "22.13.0",
"@types/node": "22.13.1",
"@types/statuses": "2.0.5",
"@vitejs/plugin-react": "4.3.4",
"browserslist": "4.24.4",
Expand Down
87 changes: 62 additions & 25 deletions packages/lru/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,20 @@ lru.delete('🦄');
lru.clear();
```


## Usage

### API

| Method | Description |
|---------------------------------|--------------------------------------------------------------------|
| `get(key): TValue \| undefined` | Retrieve a cache entry by key |
| `set(key, value): boolean` | Add a new entry and return true if entry was overwritten |
| `has(key): boolean` | Check if an entry exist |
| `delete(key): boolean` | Remove an entry, returns bool indicating if the entry was existing |
| `getOrSet(key, value): TValue` | Return the entry if exists otherwise save a new entry |
| `clear(): number` | Clear the cache and return the actual number of deleted entries |

### Iterable

```typescript
Expand Down Expand Up @@ -102,33 +114,58 @@ expect(fn).toHaveBeenCalledExactlyOnceWith('key1', 'value1');
> [![CodSpeed Badge](https://img.shields.io/endpoint?url=https://codspeed.io/badge.json)](https://codspeed.io/belgattitude/httpx)
```
RUN v3.0.4 /home/sebastien/github/httpx/packages/lru
✓ bench/compare/lru-cache/get.bench.ts > LRUCache.get() - 1000 items / maxSize: 500 2465ms
RUN v3.0.5 /home/sebastien/github/httpx/packages/lru
✓ bench/compare/lru-cache/iterate.bench.ts > LRUCache iterator - 1000 items 2442ms
name hz min max mean p75 p99 p995 p999 rme samples
· @httpx/lru - forEach - ts files (dev) 34,902.06 0.0221 0.9028 0.0287 0.0279 0.0794 0.0927 0.2815 ±0.88% 17452 fastest
· @httpx/lru - forEach - compiled (dist) 33,415.30 0.0227 1.3977 0.0299 0.0298 0.0937 0.1131 0.1902 ±0.84% 16709
· [email protected] - forEach 24,043.90 0.0310 0.6072 0.0416 0.0406 0.1177 0.1458 0.3901 ±0.93% 12022
· [email protected] - forEach 18,692.02 0.0389 0.9620 0.0535 0.0527 0.1578 0.1944 0.2975 ±0.96% 9358 slowest
✓ bench/compare/lru-cache/set.bench.ts > LRUCache.set() 1000 items / maxSize: 500 2438ms
name hz min max mean p75 p99 p995 p999 rme samples
· @httpx/lru.set() - ts files (dev) 6,505.49 0.0580 17.2624 0.1537 0.0988 0.6561 3.5929 10.5129 ±15.27% 3253 slowest
· @httpx/lru.set() - compiled (dist) 9,382.90 0.0516 1.9482 0.1066 0.1080 0.3110 0.5594 1.2600 ±2.23% 4692
· [email protected]() 30,127.38 0.0251 0.6143 0.0332 0.0330 0.0867 0.1924 0.3293 ±1.04% 15064 fastest
· [email protected]() 11,515.22 0.0574 1.8515 0.0868 0.0869 0.2429 0.3960 1.2257 ±1.84% 5759
✓ bench/compare/lru-cache/peek.bench.ts > LRUCache.peek() - 1000 items / maxSize: 500 2533ms
name hz min max mean p75 p99 p995 p999 rme samples
· @httpx/lru.peek() - ts files (dev) 113,618.01 0.0064 0.4988 0.0088 0.0089 0.0184 0.0261 0.0593 ±0.41% 56810
· @httpx/lru.peek() - compiled (dist) 125,609.48 0.0065 0.3238 0.0080 0.0082 0.0116 0.0188 0.0512 ±0.33% 62805 fastest
· [email protected]() 50,329.00 0.0151 0.7934 0.0199 0.0201 0.0392 0.0474 0.1252 ±0.62% 25165 slowest
· [email protected]() 108,351.01 0.0070 1.9459 0.0092 0.0090 0.0196 0.0203 0.0356 ±0.90% 54176
✓ bench/compare/lru-cache/get.bench.ts > LRUCache.get() - 1000 items / maxSize: 500 2464ms
name hz min max mean p75 p99 p995 p999 rme samples
· @httpx/lru.get() - ts files (dev) 34,459.29 0.0237 0.1096 0.0290 0.0312 0.0478 0.0496 0.0585 ±0.33% 17230
· @httpx/lru.get() - compiled (dist) 38,967.72 0.0229 0.2180 0.0257 0.0254 0.0488 0.0495 0.0675 ±0.23% 19484
· [email protected]() 13,271.63 0.0675 0.4073 0.0753 0.0804 0.0942 0.1766 0.2113 ±0.39% 6636 slowest
· [email protected]() 49,965.06 0.0190 0.0790 0.0200 0.0202 0.0335 0.0347 0.0374 ±0.12% 24983 fastest
✓ bench/compare/lru-cache/set.bench.ts > LRUCache.set() 1000 items / maxSize: 500 2429ms
name hz min max mean p75 p99 p995 p999 rme samples
· @httpx/lru.set() - ts files (dev) 4,309.94 0.1216 8.2000 0.2320 0.1649 1.7869 7.1022 8.1028 ±12.27% 2155 slowest
· @httpx/lru.set() - compiled (dist) 7,438.09 0.0990 1.5938 0.1344 0.1291 0.2569 0.5005 1.3142 ±1.68% 3720
· [email protected]() 16,688.86 0.0531 0.5688 0.0599 0.0624 0.0943 0.2525 0.3138 ±0.73% 8345 fastest
· [email protected]() 7,377.44 0.1132 1.2486 0.1355 0.1337 0.2536 0.3186 1.1467 ±1.07% 3689
BENCH Summary
[email protected]() - bench/compare/lru-cache/get.bench.ts > LRUCache.get() - 1000 items / maxSize: 500
1.28x faster than @httpx/lru.get() - compiled (dist)
1.45x faster than @httpx/lru.get() - ts files (dev)
3.76x faster than [email protected]()
· @httpx/lru.get() - ts files (dev) 47,992.86 0.0115 7.2249 0.0208 0.0229 0.0423 0.0519 0.1573 ±3.22% 23997
· @httpx/lru.get() - compiled (dist) 44,748.92 0.0129 1.2364 0.0223 0.0260 0.0455 0.0497 0.1212 ±0.80% 22375
· [email protected]() 16,261.07 0.0349 0.9033 0.0615 0.0820 0.1471 0.2089 0.4077 ±1.29% 8131 slowest
· [email protected]() 82,501.07 0.0079 0.7662 0.0121 0.0131 0.0275 0.0294 0.0715 ±0.68% 41251 fastest
BENCH Summary
[email protected]() - bench/compare/lru-cache/get.bench.ts > LRUCache.get() - 1000 items / maxSize: 500
1.72x faster than @httpx/lru.get() - ts files (dev)
1.84x faster than @httpx/lru.get() - compiled (dist)
5.07x faster than [email protected]()
@httpx/lru - forEach - ts files (dev) - bench/compare/lru-cache/iterate.bench.ts > LRUCache iterator - 1000 items
1.04x faster than @httpx/lru - forEach - compiled (dist)
1.45x faster than [email protected] - forEach
1.87x faster than [email protected] - forEach
@httpx/lru.peek() - compiled (dist) - bench/compare/lru-cache/peek.bench.ts > LRUCache.peek() - 1000 items / maxSize: 500
1.11x faster than @httpx/lru.peek() - ts files (dev)
1.16x faster than [email protected]()
2.50x faster than [email protected]()
[email protected]() - bench/compare/lru-cache/set.bench.ts > LRUCache.set() 1000 items / maxSize: 500
2.24x faster than @httpx/lru.set() - compiled (dist)
2.26x faster than [email protected].set()
3.87x faster than @httpx/lru.set() - ts files (dev)
2.62x faster than lru[email protected].set()
3.21x faster than @httpx/lru.set() - compiled (dist)
4.63x faster than @httpx/lru.set() - ts files (dev)
```

Expand All @@ -140,7 +177,7 @@ Bundle size is tracked by a [size-limit configuration](https://github.com/belgat

| Scenario (esm) | Size (compressed) |
|----------------------------------------------------|------------------:|
| `import { LRUCache } from '@httpx/lru` | ~ 535B |
| `import { LRUCache } from '@httpx/lru` | ~ 538B |

> For CJS usage (not recommended) track the size on [bundlephobia](https://bundlephobia.com/package/@httpx/lru@latest).
Expand Down
47 changes: 47 additions & 0 deletions packages/lru/bench/compare/lru-cache/iterate.bench.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { bench, describe } from 'vitest';

import { getLruCaches } from '../../get-lru-caches';

const SEEDS_COUNT = 1000;

describe(`LRUCache iterator - ${SEEDS_COUNT} items`, async () => {
const seeds = Array.from({ length: SEEDS_COUNT }).map((_, i) => ({
key: `key-${i}`,
value: `value-${i}`,
}));

const lrus = await getLruCaches({
maxSize: seeds.length,
prepopulate: seeds,
});

bench(`@httpx/lru - forEach - ts files (dev)`, () => {
const results: string[] = [];
for (const [key, _value] of lrus['@httpx/lru'].cache) {
results.push(key);
}
});

bench(`@httpx/lru - forEach - compiled (dist)`, () => {
if ('@httpx/lru(compiled)' in lrus) {
const results: string[] = [];
for (const [key, _value] of lrus['@httpx/lru(compiled)'].cache) {
results.push(key);
}
}
});

bench(`quick-lru@${lrus['quick-lru'].version} - forEach`, () => {
const results: string[] = [];
for (const [key, _value] of lrus['quick-lru'].cache.entriesAscending()) {
results.push(key as string);
}
});

bench(`lru-cache@${lrus['lru-cache'].version} - forEach`, () => {
const results: string[] = [];
for (const [key, _value] of lrus['lru-cache'].cache.entries()) {
results.push(key as string);
}
});
});
36 changes: 36 additions & 0 deletions packages/lru/bench/compare/lru-cache/peek.bench.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { bench, describe } from 'vitest';

import { getLruCaches } from '../../get-lru-caches';

const SEEDS_COUNT = 1000;
const MAX_SIZE = 500;

describe(`LRUCache.peek() - ${SEEDS_COUNT} items / maxSize: ${MAX_SIZE}`, async () => {
const seeds = Array.from({ length: SEEDS_COUNT }).map((_, i) => ({
key: `key-${i}`,
value: `value-${i}`,
}));

const lrus = await getLruCaches({
maxSize: MAX_SIZE,
prepopulate: seeds,
});

bench(`@httpx/lru.peek() - ts files (dev)`, () => {
seeds.forEach(({ key }) => lrus['@httpx/lru'].cache.peek(key));
});

bench(`@httpx/lru.peek() - compiled (dist)`, () => {
if ('@httpx/lru(compiled)' in lrus) {
seeds.forEach(({ key }) => lrus['@httpx/lru(compiled)']!.cache.peek(key));
}
});

bench(`quick-lru@${lrus['quick-lru'].version}.peek()`, () => {
seeds.forEach(({ key }) => lrus['quick-lru'].cache.peek(key));
});

bench(`lru-cache@${lrus['lru-cache'].version}.peek()`, () => {
seeds.forEach(({ key }) => lrus['lru-cache'].cache.peek(key));
});
});
5 changes: 5 additions & 0 deletions packages/lru/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
export { LRUCache } from './lru-cache';
export type {
BaseCache,
BaseCacheHasOptions,
BaseCacheKeyTypes,
} from './types';
9 changes: 5 additions & 4 deletions packages/lru/src/lru-cache.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ describe('LRUCache', () => {
lru.set('key1', 'value1');
lru.set('key2', 'value2');
expect(lru.size).toBe(2);
lru.clear();
const cleared = lru.clear();
expect(cleared).toBe(2);
expect(lru.size).toBe(0);
expect(lru.get('key1')).toBeUndefined();
expect(lru.get('key2')).toBeUndefined();
Expand All @@ -42,13 +43,13 @@ describe('LRUCache', () => {
});
});

describe('getOrInsert', () => {
describe('getOrSet', () => {
it('should not overwrite existing entry if exist', () => {
const lru = new LRUCache({
maxSize: 2,
});
lru.set('key', 'value');
const value = lru.getOrInsert('key', 'newValue');
const value = lru.getOrSet('key', 'newValue');
expect(value).toBe('value');
expect(lru.size).toBe(1);
});
Expand All @@ -57,7 +58,7 @@ describe('LRUCache', () => {
maxSize: 2,
});
lru.set('key', 'value');
const value = lru.getOrInsert('key2', 'value2');
const value = lru.getOrSet('key2', 'value2');
expect(value).toBe('value2');
expect(lru.size).toBe(2);
});
Expand Down
Loading

0 comments on commit 1267907

Please sign in to comment.