Skip to content

Commit efd760b

Browse files
committed
chore: add new benchmarks
1 parent a7ca658 commit efd760b

File tree

11 files changed

+379
-226
lines changed

11 files changed

+379
-226
lines changed

docs/content/docs/options.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -75,15 +75,15 @@ Note that event if errors are suppressed and not thrown, they will still be logg
7575

7676
### `grace`
7777

78-
Default `undefined`
78+
Default `false`
7979

8080
Levels: `global`, `store`, `operation`
8181

82-
A duration to define the [grace period](./grace_periods.md).
82+
A duration to define the [grace period](./grace_periods.md). Also can be `false` to disable grace periods.
8383

8484
### `graceBackoff`
8585

86-
Default: `undefined`
86+
Default: `10s`
8787

8888
Levels: `global`, `store`, `operation`
8989

examples/hono/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"hono": "^4.6.20"
1111
},
1212
"devDependencies": {
13-
"@types/node": "^22.12.0",
13+
"@types/node": "^22.13.0",
1414
"tsx": "^4.19.2"
1515
}
1616
}

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"@japa/runner": "^4.2.0",
2525
"@julr/tooling-configs": "^3.1.0",
2626
"@swc/core": "^1.10.12",
27-
"@types/node": "^22.12.0",
27+
"@types/node": "^22.13.0",
2828
"c8": "^10.1.3",
2929
"copyfiles": "^2.4.1",
3030
"cross-env": "^7.0.3",
+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Benchmarks
2+
3+
> [!IMPORTANT]
4+
> The benchmarks are not meant to be a definitive proof of which library is the best. They are mainly here to see if we make any performance regressions. And also for fun. Do not take them too seriously.
5+
6+
At the time of writing, every librairies seems on pair with each other when using a single tier cache. The real differences come when using a two-tier cache, only CacheManager and Bentocache support this feature.
7+
8+
- `mtier_get_key` : Just get a key from the cache stack.
9+
10+
```
11+
┌─────────┬────────────────┬──────────────────┬──────────────────┬────────────────────────┬────────────────────────┬─────────┐
12+
│ (index) │ Task name │ Latency avg (ns) │ Latency med (ns) │ Throughput avg (ops/s) │ Throughput med (ops/s) │ Samples │
13+
├─────────┼────────────────┼──────────────────┼──────────────────┼────────────────────────┼────────────────────────┼─────────┤
14+
│ 0 │ 'BentoCache' │ '854.21 ± 0.75%' │ '776.00 ± 10.00' │ '1252083 ± 0.02%' │ '1288660 ± 16823' │ 1170677 │
15+
│ 1 │ 'CacheManager' │ '1917.8 ± 2.90%' │ '1770.0 ± 27.00' │ '555669 ± 0.02%' │ '564972 ± 8752' │ 521437 │
16+
└─────────┴────────────────┴──────────────────┴──────────────────┴────────────────────────┴────────────────────────┴─────────┘
17+
```
18+
19+
- `mtier_get_or_set` : Get a key from the cache stack, if it does not exist, set it.
20+
21+
```
22+
┌─────────┬────────────────┬───────────────────┬──────────────────┬────────────────────────┬────────────────────────┬─────────┐
23+
│ (index) │ Task name │ Latency avg (ns) │ Latency med (ns) │ Throughput avg (ops/s) │ Throughput med (ops/s) │ Samples │
24+
├─────────┼────────────────┼───────────────────┼──────────────────┼────────────────────────┼────────────────────────┼─────────┤
25+
│ 0 │ 'BentoCache' │ '8766.7 ± 98.44%' │ '967.00 ± 36.00' │ '987455 ± 0.07%' │ '1034126 ± 39988' │ 157942 │
26+
│ 1 │ 'CacheManager' │ '14364 ± 97.77%' │ '1743.0 ± 36.00' │ '549195 ± 0.08%' │ '573723 ± 11610' │ 97211 │
27+
└─────────┴────────────────┴───────────────────┴──────────────────┴────────────────────────┴────────────────────────┴─────────┘
28+
```
29+
30+
Please if you see any mistake in the benchmarks, open an issue and I will correct it.
31+
32+
- Run: 02/02/2025
33+
- Node 22.11.0
34+
- WSL2, Ubuntu 24.04, 32G RAM, 8 cores

packages/bentocache/benchmarks/mtier_get_key.ts

+15-30
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@
44
import 'dotenv/config'
55

66
import Keyv from 'keyv'
7-
import { Redis } from 'ioredis'
87
import { Bench } from 'tinybench'
9-
import KeyvTiered from '@keyv/tiered'
10-
import { multiCaching, caching } from 'cache-manager'
11-
import { redisStore } from 'cache-manager-ioredis-yet'
8+
import KeyvRedis from '@keyv/redis'
9+
import { createCache } from 'cache-manager'
10+
import { CacheableMemory } from 'cacheable'
1211

1312
import { BentoCache } from '../index.js'
1413
import { bentostore } from '../src/bento_store.js'
@@ -21,47 +20,33 @@ const bench = new Bench()
2120
const bentocache = new BentoCache({
2221
default: 'tiered',
2322
stores: {
24-
redis: bentostore().useL2Layer(redisDriver({ connection: REDIS_CREDENTIALS })),
2523
tiered: bentostore()
2624
.useL1Layer(memoryDriver({}))
2725
.useL2Layer(redisDriver({ connection: REDIS_CREDENTIALS })),
2826
},
2927
})
3028

31-
const bentocacheTiered = bentocache.use('tiered')
32-
const keyvRedis = new Keyv('redis://localhost:6379')
33-
const keyv = new KeyvTiered({ remote: keyvRedis as any, local: new Keyv() })
29+
const bento = bentocache.use('tiered')
3430

35-
const cacheManagerMemory = await caching('memory')
36-
const cacheManagerRedis = await caching(await redisStore({ host: 'localhost', port: 6379 }))
37-
const multiCache = multiCaching([cacheManagerMemory, cacheManagerRedis])
38-
39-
await keyv.set('key', 'value')
40-
await bentocacheTiered.set({ key: 'key', value: 'value' })
41-
await multiCache.set('key', 'value')
31+
const cacheManager = createCache({
32+
stores: [
33+
new Keyv({ store: new CacheableMemory() }),
34+
new Keyv({ store: new KeyvRedis('redis://localhost:6379') }),
35+
],
36+
})
4237

43-
const ioredis = new Redis()
38+
await bento.set({ key: 'bento:key', value: 'value', ttl: '10s' })
39+
await cacheManager.set('cm:key', 'value', 10_000)
4440

45-
/**
46-
* Simple get benchmark
47-
*/
4841
bench
4942
.add('BentoCache', async () => {
50-
await bentocacheTiered.get({ key: 'key' })
51-
})
52-
.add('Keyv', async () => {
53-
await keyv.get('key')
43+
await bento.get({ key: 'bento:key' })
5444
})
5545
.add('CacheManager', async () => {
56-
await multiCache.get('key')
46+
await cacheManager.get('cm:key')
5747
})
5848

5949
await bench.run()
6050
console.table(bench.table())
6151

62-
await Promise.all([
63-
bentocache.disconnectAll(),
64-
ioredis.quit(),
65-
cacheManagerRedis.store.client.disconnect(),
66-
keyvRedis.disconnect(),
67-
])
52+
await Promise.all([bentocache.disconnectAll(), cacheManager.disconnect()])
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/**
2+
* Benchmark a single get operation on a tiered store ( memory + redis )
3+
*/
4+
import 'dotenv/config'
5+
6+
import Keyv from 'keyv'
7+
import { Bench } from 'tinybench'
8+
import KeyvRedis from '@keyv/redis'
9+
import { createCache } from 'cache-manager'
10+
import { CacheableMemory } from 'cacheable'
11+
import { setTimeout } from 'node:timers/promises'
12+
13+
import { BentoCache } from '../index.js'
14+
import { bentostore } from '../src/bento_store.js'
15+
import { redisDriver } from '../src/drivers/redis.js'
16+
import { memoryDriver } from '../src/drivers/memory.js'
17+
import { REDIS_CREDENTIALS } from '../tests/helpers/index.js'
18+
19+
const bench = new Bench({})
20+
21+
const bentocache = new BentoCache({
22+
default: 'tiered',
23+
stores: {
24+
tiered: bentostore()
25+
.useL1Layer(memoryDriver({}))
26+
.useL2Layer(redisDriver({ connection: REDIS_CREDENTIALS })),
27+
},
28+
})
29+
30+
const bento = bentocache.use('tiered')
31+
32+
const cacheManager = createCache({
33+
stores: [
34+
new Keyv({ store: new CacheableMemory() }),
35+
new Keyv({ store: new KeyvRedis('redis://localhost:6379') }),
36+
],
37+
})
38+
39+
const getFromDb = async () => {
40+
await setTimeout(400)
41+
return 'value'
42+
}
43+
44+
bench
45+
.add('BentoCache', async () => {
46+
return await bento.getOrSet({
47+
key: 'bento:key',
48+
factory: getFromDb,
49+
ttl: 100,
50+
})
51+
})
52+
.add('CacheManager', async () => {
53+
const result = await cacheManager.get('cm:key')
54+
if (result === null) {
55+
await cacheManager.set('cm:key', await getFromDb(), 100)
56+
}
57+
58+
return result ?? 'value'
59+
})
60+
61+
await bench.run()
62+
console.table(bench.table())
63+
64+
await Promise.all([bentocache.disconnectAll(), cacheManager.disconnect()])
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
/**
2-
* Benchmarking a set key operation with a tiered cache ( memory + redis )
2+
* Benchmark a single set operation on a tiered store ( memory + redis )
33
*/
44
import 'dotenv/config'
55

66
import Keyv from 'keyv'
7-
import { Redis } from 'ioredis'
87
import { Bench } from 'tinybench'
9-
import KeyvTiered from '@keyv/tiered'
10-
import { multiCaching, caching } from 'cache-manager'
11-
import { redisStore } from 'cache-manager-ioredis-yet'
8+
import KeyvRedis from '@keyv/redis'
9+
import { createCache } from 'cache-manager'
10+
import { CacheableMemory } from 'cacheable'
1211

1312
import { BentoCache } from '../index.js'
1413
import { bentostore } from '../src/bento_store.js'
@@ -21,44 +20,38 @@ const bench = new Bench()
2120
const bentocache = new BentoCache({
2221
default: 'tiered',
2322
stores: {
24-
redis: bentostore().useL2Layer(redisDriver({ connection: REDIS_CREDENTIALS })),
2523
tiered: bentostore()
2624
.useL1Layer(memoryDriver({}))
2725
.useL2Layer(redisDriver({ connection: REDIS_CREDENTIALS })),
2826
},
2927
})
3028

31-
const bentocacheTiered = bentocache.use('tiered')
32-
const keyvRedis = new Keyv('redis://localhost:6379')
33-
const keyv = new KeyvTiered({ remote: keyvRedis as any, local: new Keyv() })
29+
const bento = bentocache.use('tiered')
3430

35-
const cacheManagerMemory = await caching('memory')
36-
const cacheManagerRedis = await caching(await redisStore({ host: 'localhost', port: 6379 }))
37-
const multiCache = multiCaching([cacheManagerMemory, cacheManagerRedis])
31+
const cacheManager = createCache({
32+
stores: [
33+
new Keyv({
34+
store: new CacheableMemory({ ttl: 60_000, lruSize: 5000 }),
35+
}),
3836

39-
await keyv.set('key', 'value')
40-
await bentocacheTiered.set({ key: 'key', value: 'value' })
41-
await multiCache.set('key', 'value')
37+
new Keyv({
38+
store: new KeyvRedis('redis://localhost:6379'),
39+
}),
40+
],
41+
})
4242

43-
const ioredis = new Redis()
43+
await bento.set({ key: 'key', value: 'value' })
44+
await cacheManager.set('key', 'value')
4445

4546
bench
4647
.add('BentoCache', async () => {
47-
await bentocacheTiered.set({ key: 'key', value: 'value' })
48-
})
49-
.add('Keyv', async () => {
50-
await keyv.set('key', 'value')
48+
await bento.set({ key: 'key', value: 10 })
5149
})
5250
.add('CacheManager', async () => {
53-
await multiCache.set('key', 'value')
51+
await cacheManager.set('key', 10)
5452
})
5553

5654
await bench.run()
5755
console.table(bench.table())
5856

59-
await Promise.all([
60-
bentocache.disconnectAll(),
61-
ioredis.quit(),
62-
cacheManagerRedis.store.client.disconnect(),
63-
keyvRedis.disconnect(),
64-
])
57+
await Promise.all([bentocache.disconnectAll(), cacheManager.disconnect()])

packages/bentocache/benchmarks/onetier_get_key.ts

+7-6
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import 'dotenv/config'
66
import Keyv from 'keyv'
77
import { Redis } from 'ioredis'
88
import { Bench } from 'tinybench'
9-
import { caching } from 'cache-manager'
10-
import { redisStore } from 'cache-manager-ioredis-yet'
9+
import KeyvRedis from '@keyv/redis'
10+
import { createCache } from 'cache-manager'
1111

1212
import { BentoCache } from '../index.js'
1313
import { bentostore } from '../src/bento_store.js'
@@ -23,9 +23,10 @@ const bentocache = new BentoCache({
2323
},
2424
})
2525

26-
const keyv = new Keyv('redis://localhost:6379')
27-
28-
const cacheManager = await caching(await redisStore({ host: 'localhost', port: 6379 }))
26+
const keyv = new Keyv(new KeyvRedis('redis://localhost:6379'))
27+
const cacheManager = await createCache({
28+
stores: [new Keyv(new KeyvRedis('redis://localhost:6379'))],
29+
})
2930

3031
await keyv.set('key', 'value')
3132
await bentocache.set({ key: 'key', value: 'value' })
@@ -53,6 +54,6 @@ console.table(bench.table())
5354
await Promise.all([
5455
bentocache.disconnectAll(),
5556
ioredis.quit(),
56-
cacheManager.store.client.disconnect(),
57+
cacheManager.disconnect(),
5758
keyv.disconnect(),
5859
])
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,48 @@
11
/**
2-
* Benchmark a single set operation on a memory store
2+
* Benchmark a single set operation on a redis store
33
*/
44
import 'dotenv/config'
55

66
import Keyv from 'keyv'
77
import { Bench } from 'tinybench'
8-
import { caching } from 'cache-manager'
8+
import KeyvRedis from '@keyv/redis'
9+
import { createCache } from 'cache-manager'
910

1011
import { BentoCache } from '../index.js'
1112
import { bentostore } from '../src/bento_store.js'
12-
import { memoryDriver } from '../src/drivers/memory.js'
13+
import { redisDriver } from '../src/drivers/redis.js'
14+
import { REDIS_CREDENTIALS } from '../tests/helpers/index.js'
1315

1416
const bench = new Bench()
1517

1618
const bentocache = new BentoCache({
17-
default: 'memory',
18-
stores: { memory: bentostore().useL1Layer(memoryDriver()) },
19-
}).use('memory')
19+
default: 'redis',
20+
stores: {
21+
redis: bentostore().useL2Layer(redisDriver({ connection: REDIS_CREDENTIALS })),
22+
},
23+
})
2024

21-
const keyv = new Keyv()
22-
const cacheManager = await caching('memory')
25+
const keyv = new Keyv(new KeyvRedis('redis://localhost:6379'))
26+
const cacheManager = await createCache({
27+
stores: [new Keyv(new KeyvRedis('redis://localhost:6379'))],
28+
})
2329

2430
await keyv.set('key', 'value')
2531
await bentocache.set({ key: 'key', value: 'value' })
2632
await cacheManager.set('key', 'value')
2733

2834
bench
2935
.add('BentoCache', async () => {
30-
await bentocache.get({ key: 'key' })
36+
await bentocache.set({ key: 'key', value: 'foo' })
3137
})
3238
.add('Keyv', async () => {
33-
await keyv.get('key')
39+
await keyv.set('key', 'foo')
3440
})
3541
.add('CacheManager', async () => {
36-
await cacheManager.get('key')
42+
await cacheManager.set('key', 'foo')
3743
})
3844

3945
await bench.run()
4046
console.table(bench.table())
47+
48+
await Promise.all([bentocache.disconnect(), cacheManager.disconnect(), keyv.disconnect()])

0 commit comments

Comments
 (0)