@@ -46,7 +46,7 @@ import { MemoryCache } from 'cache-hub';
4646
4747const cache = new MemoryCache ({
4848 maxEntries: 1000 , // 最多 1000 条
49- ttl : 60_000 , // 默认 TTL 60 秒
49+ defaultTtl : 60_000 , // 默认 TTL 60 秒
5050 enableStats: true ,
5151});
5252
@@ -63,7 +63,7 @@ console.log(stats.hitRate); // 0~1 命中率
6363import { MemoryCache } from ' cache-hub' ;
6464import { readThrough } from ' cache-hub/read-through' ;
6565
66- const cache = new MemoryCache ({ ttl : 30_000 });
66+ const cache = new MemoryCache ({ defaultTtl : 30_000 });
6767
6868// 缓存命中直返,未命中执行 fetcher 并写缓存
6969// 相同 key 的并发请求共享同一 Promise(并发去重)
@@ -102,7 +102,7 @@ import { MemoryCache } from 'cache-hub';
102102import { MultiLevelCache } from ' cache-hub/multi-level' ;
103103import { createRedisCacheAdapter } from ' cache-hub/redis' ;
104104
105- const local = new MemoryCache ({ maxEntries: 500 , ttl : 30_000 });
105+ const local = new MemoryCache ({ maxEntries: 500 , defaultTtl : 30_000 });
106106const remote = createRedisCacheAdapter (' redis://localhost:6379' );
107107
108108const cache = new MultiLevelCache ({
@@ -130,12 +130,12 @@ const local = new MemoryCache({ maxEntries: 1000 });
130130// 多个服务实例各自持有本地缓存,通过 Redis Pub/Sub 广播失效
131131const invalidator = new DistributedCacheInvalidator ({
132132 redisUrl: process .env .REDIS_URL ?? ' redis://localhost:6379' ,
133- watchedCaches: [ local ] ,
133+ cache: local ,
134134 channel: ' app:cache-invalidation' ,
135135});
136136
137- // 发布失效事件,其他实例收到后自动清除本地缓存
138- await invalidator .invalidate ([ ' user:1 ' , ' user:2 ' ] );
137+ // 广播失效事件(支持通配符 *),其他实例收到后自动对本地缓存执行 delPattern
138+ await invalidator .invalidate (' user:* ' );
139139
140140// 应用退出时关闭连接
141141await invalidator .close ();
@@ -158,11 +158,11 @@ import type { CacheLike, CacheStats, MemoryCacheOptions } from 'cache-hub';
158158
159159| 选项 | 类型 | 默认值 | 说明 |
160160| ------| ------| --------| ------|
161- | ` maxEntries ` | ` number ` | ` Infinity ` | 最大条目数,超限 LRU 淘汰 |
162- | ` maxMemory ` | ` number ` | ` Infinity ` | 最大内存(字节估算),超限 LRU 淘汰 |
163- | ` ttl ` | ` number ` | ` 0 ` (不过期)| 默认 TTL(毫秒) |
161+ | ` maxEntries ` | ` number ` | ` 10000 ` | 最大条目数,超限 LRU 淘汰 |
162+ | ` maxMemory ` | ` number ` | ` 0 ` | 最大内存(字节估算),超限 LRU 淘汰; ` 0 ` 表示无内存限制 |
163+ | ` defaultTtl ` | ` number ` | ` 0 ` (不过期)| 默认 TTL(毫秒) |
164164| ` cleanupInterval ` | ` number ` | ` 0 ` (不清理)| 周期清理间隔(毫秒) |
165- | ` enableStats ` | ` boolean ` | ` false ` | 开启命中率统计 |
165+ | ` enableStats ` | ` boolean ` | ` true ` | 开启命中率统计 |
166166| ` enableTags ` | ` boolean ` | ` false ` | 开启标签索引,支持 ` invalidateByTag ` |
167167| ` enabled ` | ` boolean ` | ` true ` | ` false ` 时禁用缓存 |
168168
@@ -171,20 +171,24 @@ import type { CacheLike, CacheStats, MemoryCacheOptions } from 'cache-hub';
171171所有缓存实现均满足此接口,可互相替换:
172172
173173``` typescript
174- interface CacheLike < V = unknown > {
175- get(key : string ): V | undefined | Promise <V | undefined >;
176- set(key : string , value : V , ttl ? : number ): void | Promise <void >;
177- del(key : string ): void | Promise <void >;
174+ interface CacheLike {
175+ get< T = any > (key : string ): T | undefined | Promise <T | undefined >;
176+ set(key : string , value : any , ttl ? : number ): void | Promise <void >;
177+ del(key : string ): boolean | Promise <boolean >;
178178 exists(key : string ): boolean | Promise <boolean >;
179179 has(key : string ): boolean | Promise <boolean >; // exists 的同步别名
180180 clear(): void | Promise <void >;
181- keys(): string [] | Promise <string []>;
182- getMany(keys : string []): Map <string , V > | Promise <Map <string , V >>;
183- setMany(entries : Map <string , V >, ttl ? : number ): void | Promise <void >;
184- delMany(keys : string []): void | Promise <void >;
185- delPattern(pattern : string ): void | Promise <void >; // 支持 * 通配符
186- getStats(): CacheStats ;
187- destroy(): void | Promise <void >;
181+ keys(pattern ? : string ): string [] | Promise <string []>;
182+ getMany(keys : string []): Record <string , any > | Promise <Record <string , any >>;
183+ setMany(entries : Record <string , any >, ttl ? : number ): boolean | Promise <boolean >;
184+ delMany(keys : string []): number | Promise <number >;
185+ delPattern(pattern : string ): number | Promise <number >; // 支持 * 通配符
186+ // 可选扩展
187+ invalidateByTag? (tag : string ): void | Promise <void >;
188+ getStats? (): CacheStats ;
189+ resetStats? (): void ;
190+ destroy? (): void ;
191+ setLockManager? (lm : LockManager ): void ;
188192}
189193```
190194
@@ -196,7 +200,7 @@ interface CacheLike<V = unknown> {
196200import { readThrough } from ' cache-hub/read-through' ;
197201
198202function readThrough<V >(
199- cache : CacheLike < V > ,
203+ cache : CacheLike ,
200204 ttl : number ,
201205 key : string ,
202206 fetcher : () => Promise <V >
@@ -220,11 +224,11 @@ new MultiLevelCache(options: MultiLevelCacheOptions)
220224| 选项 | 类型 | 默认值 | 说明 |
221225| ------ | ------ | -------- | ------ |
222226| ` local ` | ` CacheLike ` | 必填 | L1 本地缓存 |
223- | ` remote ` | ` CacheLike ` | 必填 | L2 远端缓存 |
227+ | ` remote ` | ` CacheLike ` | — | L2 远端缓存(可选,未传时作为单级本地缓存运行) |
224228| ` writePolicy ` | ` 'both' \| 'local-first-async-remote' ` | ` 'both' ` | 写策略 |
225229| ` backfillOnRemoteHit ` | ` boolean ` | ` true ` | L2 命中时回填 L1 |
226230| ` remoteTimeoutMs ` | ` number ` | ` 50 ` | 远端超时(毫秒),超时降级不报错 |
227- | ` publish ` | ` (keys: string[] ) => void ` | — | 分布式失效广播回调 |
231+ | ` publish ` | ` (msg: { type: string; pattern: string; ts: number } ) => void ` | — | ` delPattern ` 时触发的分布式失效广播回调 |
228232
229233-- -
230234
@@ -270,13 +274,13 @@ const cachedFn = withCache(asyncFn, {
270274#### ` FunctionCache ` 类
271275
272276` ` ` typescript
273- const fc = new FunctionCache({ cache, ttl: 30_000 });
277+ const fc = new FunctionCache(cache, { ttl: 30_000 });
274278
275279fc.register('getUser', async (id: number) => db.findUser(id));
276280fc.register('getProduct', async (id: number) => db.findProduct(id), { ttl: 10_000 });
277281
278- const user = await fc.execute('getUser', [1] );
279- await fc.invalidate('getUser', [1] ); // 使指定参数的缓存失效
282+ const user = await fc.execute('getUser', 1 );
283+ await fc.invalidate('getUser', 1 ); // 使指定参数的缓存失效
280284
281285const stats = fc.getStats(); // { getUser: { hits, misses, ... } }
282286` ` `
@@ -293,19 +297,19 @@ new DistributedCacheInvalidator(options: DistributedInvalidatorOptions)
293297
294298| 选项 | 类型 | 说明 |
295299| ------ | ------ | ------ |
296- | ` redisUrl ` | ` string ` | Redis URL ,与 ` redis ` 二选一 |
300+ | ` cache ` | ` CacheLike ` | 必填,接收失效消息时对该实例执行 ` delPattern ` |
301+ | ` redisUrl ` | ` string ` | Redis URL ,与 ` redis ` 二选一,均未提供时默认 ` redis://localhost:6379 ` |
297302| ` redis ` | ` ioredis ` | 已有 Redis 连接,与 ` redisUrl ` 二选一 |
298- | ` watchedCaches ` | ` CacheLike[] ` | 接收到失效消息时清除这些缓存 |
299- | ` channel ` | ` string ` | Pub / Sub 频道名,默认 ` 'cache-hub:invalidation' ` |
303+ | ` channel ` | ` string ` | Pub / Sub 频道名,默认 ` 'cache-hub:invalidate' ` |
300304| ` instanceId ` | ` string ` | 实例唯一 ID ,用于过滤自身消息(默认随机生成) |
301305
302306` ` ` typescript
303- // 发布失效事件
304- await invalidator.invalidate(['key1', 'key2'] );
307+ // 广播失效事件(支持通配符 *)
308+ await invalidator.invalidate('user:*' );
305309
306310// 查看统计
307311const stats = invalidator.getStats();
308- // { published : 5, received : 12, selfFiltered: 5 }
312+ // { messagesSent : 5, messagesReceived : 12, invalidationsTriggered: 7, errors: 0, instanceId: '...', channel: '...' }
309313
310314// 关闭连接
311315await invalidator.close();
@@ -341,7 +345,7 @@ stableStringify(value, {
341345## 测试
342346
343347` ` ` bash
344- # 单元测试(440 个,无需外部依赖 )
348+ # 全量测试(470 个,集成测试在无 Redis 时自动跳过 )
345349npm test
346350
347351# 单元测试 + 覆盖率报告
0 commit comments