forked from imbytecat/fullstack-starter
feat: 使用 ttlcache 优化指纹缓存管理
- 添加指纹缓存迁移决策,选用零依赖的 ttlcache 库并配置单例缓存与十分钟过期时间。 - 使用 `@isaacs/ttlcache` 成功替换手动 TTL 缓存并保留请求去重逻辑,优化依赖管理与代码格式化流程。 - 添加缓存库选型说明,明确采用 @isaacs/ttlcache 提升硬件指纹功能的可维护性与稳定性。 - 添加 ttlcache 依赖以支持缓存功能 - 使用 TTLCache 替代手动缓存逻辑,提升缓存管理的可靠性和可维护性。 - 添加 @isaacs/ttlcache 依赖并指定版本 2.1.4,更新锁定文件以确保依赖一致性。 - 添加 ttlcache 依赖以支持缓存功能
This commit is contained in:
8
.sisyphus/notepads/fingerprint-migration/decisions.md
Normal file
8
.sisyphus/notepads/fingerprint-migration/decisions.md
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
## Migration Decisions
|
||||||
|
|
||||||
|
- **Library**: `@isaacs/ttlcache` v2.1.4
|
||||||
|
- **Rationale**: Minimal, zero-dependency, specialized for TTL, well-maintained.
|
||||||
|
- **Cache Key**: Fixed string `'fingerprint'` since we only cache one result.
|
||||||
|
- **Max Items**: 1 (singleton fingerprint).
|
||||||
|
- **Default TTL**: 10 minutes (compatible with previous implementation).
|
||||||
|
|
||||||
8
.sisyphus/notepads/fingerprint-migration/learnings.md
Normal file
8
.sisyphus/notepads/fingerprint-migration/learnings.md
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
## @isaacs/ttlcache Migration Learnings
|
||||||
|
|
||||||
|
- Successfully replaced manual TTL cache with `@isaacs/ttlcache` in `fingerprint.ts`.
|
||||||
|
- Use `catalog:` for dependency management in the Bun monorepo.
|
||||||
|
- `TTLCache` does not handle in-flight request deduplication, so the `inFlight` Promise pattern was preserved.
|
||||||
|
- Alphabetical sorting in `package.json` catalog is important for consistency.
|
||||||
|
- Biome handles import organization and formatting; `bun fix` should be run after manual edits.
|
||||||
|
|
||||||
@@ -346,6 +346,37 @@ const fingerprint = createHash('sha256')
|
|||||||
- 深入研究文档和源码再做技术决策
|
- 深入研究文档和源码再做技术决策
|
||||||
- 区分"用户输入场景"和"系统数据场景"的安全要求
|
- 区分"用户输入场景"和"系统数据场景"的安全要求
|
||||||
|
|
||||||
|
### 缓存库选择:@isaacs/ttlcache
|
||||||
|
|
||||||
|
**决策时间**: 2026-01-26
|
||||||
|
|
||||||
|
**背景**:
|
||||||
|
硬件指纹功能最初使用手动实现的 TTL 缓存(module-level 变量 + 手动过期检查)。为提高代码可维护性,迁移到专业缓存库。
|
||||||
|
|
||||||
|
**选型**:
|
||||||
|
- **选择**: `@isaacs/ttlcache` v2.1.4
|
||||||
|
- **理由**:
|
||||||
|
- 专为 TTL 场景优化,无需 LRU 追踪开销
|
||||||
|
- 零依赖,6M+ 周下载量
|
||||||
|
- 内置 TypeScript 类型
|
||||||
|
- 自动过期管理,无需手动定时器
|
||||||
|
- API 简洁: `new TTLCache({ ttl, max })`
|
||||||
|
|
||||||
|
**实现细节**:
|
||||||
|
- 保留 `inFlight` Promise 模式用于并发请求去重(TTLCache 不提供此功能)
|
||||||
|
- 使用单一缓存键 `'fingerprint'`(单服务器场景,opts 不影响输出)
|
||||||
|
- 默认 TTL: 10 分钟(可通过 `cacheTtlMs` 参数覆盖)
|
||||||
|
|
||||||
|
**对比手动实现**:
|
||||||
|
- ✅ 更少自定义代码
|
||||||
|
- ✅ 更清晰的 TTL 语义
|
||||||
|
- ✅ 经过充分测试的库
|
||||||
|
- ⚠️ 仍需手动处理并发去重
|
||||||
|
|
||||||
|
**经验教训**:
|
||||||
|
- 专业库不一定解决所有问题(如并发去重)
|
||||||
|
- 对于简单场景,手动实现 vs 库的选择主要取决于可维护性而非功能
|
||||||
|
|
||||||
### Git 工作流要求
|
### Git 工作流要求
|
||||||
|
|
||||||
**重要原则**:保持代码仓库与文档同步
|
**重要原则**:保持代码仓库与文档同步
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
"typecheck": "tsc --noEmit"
|
"typecheck": "tsc --noEmit"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@isaacs/ttlcache": "catalog:",
|
||||||
"@orpc/client": "catalog:",
|
"@orpc/client": "catalog:",
|
||||||
"@orpc/contract": "catalog:",
|
"@orpc/contract": "catalog:",
|
||||||
"@orpc/openapi": "catalog:",
|
"@orpc/openapi": "catalog:",
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { TTLCache } from '@isaacs/ttlcache'
|
||||||
import { hash } from 'ohash'
|
import { hash } from 'ohash'
|
||||||
import si from 'systeminformation'
|
import si from 'systeminformation'
|
||||||
|
|
||||||
@@ -63,10 +64,10 @@ export type HardwareFingerprintResult = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 缓存实例
|
// 缓存实例
|
||||||
let cache: {
|
const cache = new TTLCache<'fingerprint', HardwareFingerprintResult>({
|
||||||
expiresAt: number
|
ttl: 10 * 60 * 1000, // 10 minutes default
|
||||||
value: HardwareFingerprintResult
|
max: 1, // Only one fingerprint cached
|
||||||
} | null = null
|
})
|
||||||
|
|
||||||
// 防止并发重复请求
|
// 防止并发重复请求
|
||||||
let inFlight: Promise<HardwareFingerprintResult> | null = null
|
let inFlight: Promise<HardwareFingerprintResult> | null = null
|
||||||
@@ -182,8 +183,9 @@ export async function getHardwareFingerprint(
|
|||||||
const now = Date.now()
|
const now = Date.now()
|
||||||
|
|
||||||
// 返回缓存结果
|
// 返回缓存结果
|
||||||
if (cache && cache.expiresAt > now) {
|
const cached = cache.get('fingerprint')
|
||||||
return cache.value
|
if (cached) {
|
||||||
|
return cached
|
||||||
}
|
}
|
||||||
|
|
||||||
// 防止并发重复请求
|
// 防止并发重复请求
|
||||||
@@ -212,7 +214,7 @@ export async function getHardwareFingerprint(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 更新缓存
|
// 更新缓存
|
||||||
cache = { expiresAt: now + ttl, value: result }
|
cache.set('fingerprint', result, { ttl })
|
||||||
|
|
||||||
return result
|
return result
|
||||||
})().finally(() => {
|
})().finally(() => {
|
||||||
@@ -226,6 +228,6 @@ export async function getHardwareFingerprint(
|
|||||||
* 清除指纹缓存(用于测试或强制刷新)
|
* 清除指纹缓存(用于测试或强制刷新)
|
||||||
*/
|
*/
|
||||||
export function clearFingerprintCache(): void {
|
export function clearFingerprintCache(): void {
|
||||||
cache = null
|
cache.clear()
|
||||||
inFlight = null
|
inFlight = null
|
||||||
}
|
}
|
||||||
|
|||||||
4
bun.lock
4
bun.lock
@@ -13,6 +13,7 @@
|
|||||||
"name": "@furtherverse/server",
|
"name": "@furtherverse/server",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@isaacs/ttlcache": "catalog:",
|
||||||
"@orpc/client": "catalog:",
|
"@orpc/client": "catalog:",
|
||||||
"@orpc/contract": "catalog:",
|
"@orpc/contract": "catalog:",
|
||||||
"@orpc/openapi": "catalog:",
|
"@orpc/openapi": "catalog:",
|
||||||
@@ -64,6 +65,7 @@
|
|||||||
"@biomejs/biome": "^2.3.11",
|
"@biomejs/biome": "^2.3.11",
|
||||||
"@effect/platform": "^0.94.2",
|
"@effect/platform": "^0.94.2",
|
||||||
"@effect/schema": "^0.75.5",
|
"@effect/schema": "^0.75.5",
|
||||||
|
"@isaacs/ttlcache": "^2.1.4",
|
||||||
"@orpc/client": "^1.13.4",
|
"@orpc/client": "^1.13.4",
|
||||||
"@orpc/contract": "^1.13.4",
|
"@orpc/contract": "^1.13.4",
|
||||||
"@orpc/openapi": "^1.13.4",
|
"@orpc/openapi": "^1.13.4",
|
||||||
@@ -234,6 +236,8 @@
|
|||||||
|
|
||||||
"@furtherverse/tsconfig": ["@furtherverse/tsconfig@workspace:packages/tsconfig"],
|
"@furtherverse/tsconfig": ["@furtherverse/tsconfig@workspace:packages/tsconfig"],
|
||||||
|
|
||||||
|
"@isaacs/ttlcache": ["@isaacs/ttlcache@2.1.4", "", {}, "sha512-7kMz0BJpMvgAMkyglums7B2vtrn5g0a0am77JY0GjkZZNetOBCFn7AG7gKCwT0QPiXyxW7YIQSgtARknUEOcxQ=="],
|
||||||
|
|
||||||
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
|
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
|
||||||
|
|
||||||
"@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
|
"@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
"@biomejs/biome": "^2.3.11",
|
"@biomejs/biome": "^2.3.11",
|
||||||
"@effect/platform": "^0.94.2",
|
"@effect/platform": "^0.94.2",
|
||||||
"@effect/schema": "^0.75.5",
|
"@effect/schema": "^0.75.5",
|
||||||
|
"@isaacs/ttlcache": "^2.1.4",
|
||||||
"@orpc/client": "^1.13.4",
|
"@orpc/client": "^1.13.4",
|
||||||
"@orpc/contract": "^1.13.4",
|
"@orpc/contract": "^1.13.4",
|
||||||
"@orpc/openapi": "^1.13.4",
|
"@orpc/openapi": "^1.13.4",
|
||||||
|
|||||||
Reference in New Issue
Block a user