@claude-flow/memory 3.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agentic-flow/intelligence.json +16 -0
- package/README.md +249 -0
- package/__tests__/coverage/base.css +224 -0
- package/__tests__/coverage/block-navigation.js +87 -0
- package/__tests__/coverage/coverage-final.json +19 -0
- package/__tests__/coverage/favicon.png +0 -0
- package/__tests__/coverage/index.html +206 -0
- package/__tests__/coverage/lcov-report/base.css +224 -0
- package/__tests__/coverage/lcov-report/block-navigation.js +87 -0
- package/__tests__/coverage/lcov-report/favicon.png +0 -0
- package/__tests__/coverage/lcov-report/index.html +206 -0
- package/__tests__/coverage/lcov-report/prettify.css +1 -0
- package/__tests__/coverage/lcov-report/prettify.js +2 -0
- package/__tests__/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/__tests__/coverage/lcov-report/sorter.js +210 -0
- package/__tests__/coverage/lcov-report/src/agentdb-adapter.ts.html +2737 -0
- package/__tests__/coverage/lcov-report/src/agentdb-backend.ts.html +3130 -0
- package/__tests__/coverage/lcov-report/src/application/commands/delete-memory.command.ts.html +601 -0
- package/__tests__/coverage/lcov-report/src/application/commands/index.html +131 -0
- package/__tests__/coverage/lcov-report/src/application/commands/store-memory.command.ts.html +394 -0
- package/__tests__/coverage/lcov-report/src/application/queries/index.html +116 -0
- package/__tests__/coverage/lcov-report/src/application/queries/search-memory.query.ts.html +796 -0
- package/__tests__/coverage/lcov-report/src/application/services/index.html +116 -0
- package/__tests__/coverage/lcov-report/src/application/services/memory-application-service.ts.html +793 -0
- package/__tests__/coverage/lcov-report/src/cache-manager.ts.html +1633 -0
- package/__tests__/coverage/lcov-report/src/database-provider.ts.html +1618 -0
- package/__tests__/coverage/lcov-report/src/domain/entities/index.html +116 -0
- package/__tests__/coverage/lcov-report/src/domain/entities/memory-entry.ts.html +952 -0
- package/__tests__/coverage/lcov-report/src/domain/services/index.html +116 -0
- package/__tests__/coverage/lcov-report/src/domain/services/memory-domain-service.ts.html +1294 -0
- package/__tests__/coverage/lcov-report/src/hnsw-index.ts.html +3124 -0
- package/__tests__/coverage/lcov-report/src/hybrid-backend.ts.html +2167 -0
- package/__tests__/coverage/lcov-report/src/index.html +266 -0
- package/__tests__/coverage/lcov-report/src/infrastructure/repositories/hybrid-memory-repository.ts.html +1633 -0
- package/__tests__/coverage/lcov-report/src/infrastructure/repositories/index.html +116 -0
- package/__tests__/coverage/lcov-report/src/migration.ts.html +2092 -0
- package/__tests__/coverage/lcov-report/src/query-builder.ts.html +1711 -0
- package/__tests__/coverage/lcov-report/src/sqlite-backend.ts.html +2281 -0
- package/__tests__/coverage/lcov-report/src/sqljs-backend.ts.html +2374 -0
- package/__tests__/coverage/lcov-report/src/types.ts.html +2266 -0
- package/__tests__/coverage/lcov.info +10238 -0
- package/__tests__/coverage/prettify.css +1 -0
- package/__tests__/coverage/prettify.js +2 -0
- package/__tests__/coverage/sort-arrow-sprite.png +0 -0
- package/__tests__/coverage/sorter.js +210 -0
- package/__tests__/coverage/src/agentdb-adapter.ts.html +2737 -0
- package/__tests__/coverage/src/agentdb-backend.ts.html +3130 -0
- package/__tests__/coverage/src/application/commands/delete-memory.command.ts.html +601 -0
- package/__tests__/coverage/src/application/commands/index.html +131 -0
- package/__tests__/coverage/src/application/commands/store-memory.command.ts.html +394 -0
- package/__tests__/coverage/src/application/queries/index.html +116 -0
- package/__tests__/coverage/src/application/queries/search-memory.query.ts.html +796 -0
- package/__tests__/coverage/src/application/services/index.html +116 -0
- package/__tests__/coverage/src/application/services/memory-application-service.ts.html +793 -0
- package/__tests__/coverage/src/cache-manager.ts.html +1633 -0
- package/__tests__/coverage/src/database-provider.ts.html +1618 -0
- package/__tests__/coverage/src/domain/entities/index.html +116 -0
- package/__tests__/coverage/src/domain/entities/memory-entry.ts.html +952 -0
- package/__tests__/coverage/src/domain/services/index.html +116 -0
- package/__tests__/coverage/src/domain/services/memory-domain-service.ts.html +1294 -0
- package/__tests__/coverage/src/hnsw-index.ts.html +3124 -0
- package/__tests__/coverage/src/hybrid-backend.ts.html +2167 -0
- package/__tests__/coverage/src/index.html +266 -0
- package/__tests__/coverage/src/infrastructure/repositories/hybrid-memory-repository.ts.html +1633 -0
- package/__tests__/coverage/src/infrastructure/repositories/index.html +116 -0
- package/__tests__/coverage/src/migration.ts.html +2092 -0
- package/__tests__/coverage/src/query-builder.ts.html +1711 -0
- package/__tests__/coverage/src/sqlite-backend.ts.html +2281 -0
- package/__tests__/coverage/src/sqljs-backend.ts.html +2374 -0
- package/__tests__/coverage/src/types.ts.html +2266 -0
- package/benchmarks/cache-hit-rate.bench.ts +535 -0
- package/benchmarks/hnsw-indexing.bench.ts +552 -0
- package/benchmarks/memory-write.bench.ts +469 -0
- package/benchmarks/vector-search.bench.ts +449 -0
- package/dist/agentdb-adapter.d.ts +146 -0
- package/dist/agentdb-adapter.d.ts.map +1 -0
- package/dist/agentdb-adapter.js +679 -0
- package/dist/agentdb-adapter.js.map +1 -0
- package/dist/agentdb-backend.d.ts +214 -0
- package/dist/agentdb-backend.d.ts.map +1 -0
- package/dist/agentdb-backend.js +827 -0
- package/dist/agentdb-backend.js.map +1 -0
- package/dist/agentdb-backend.test.d.ts +7 -0
- package/dist/agentdb-backend.test.d.ts.map +1 -0
- package/dist/agentdb-backend.test.js +258 -0
- package/dist/agentdb-backend.test.js.map +1 -0
- package/dist/application/commands/delete-memory.command.d.ts +65 -0
- package/dist/application/commands/delete-memory.command.d.ts.map +1 -0
- package/dist/application/commands/delete-memory.command.js +129 -0
- package/dist/application/commands/delete-memory.command.js.map +1 -0
- package/dist/application/commands/store-memory.command.d.ts +48 -0
- package/dist/application/commands/store-memory.command.d.ts.map +1 -0
- package/dist/application/commands/store-memory.command.js +72 -0
- package/dist/application/commands/store-memory.command.js.map +1 -0
- package/dist/application/index.d.ts +12 -0
- package/dist/application/index.d.ts.map +1 -0
- package/dist/application/index.js +15 -0
- package/dist/application/index.js.map +1 -0
- package/dist/application/queries/search-memory.query.d.ts +72 -0
- package/dist/application/queries/search-memory.query.d.ts.map +1 -0
- package/dist/application/queries/search-memory.query.js +143 -0
- package/dist/application/queries/search-memory.query.js.map +1 -0
- package/dist/application/services/memory-application-service.d.ts +121 -0
- package/dist/application/services/memory-application-service.d.ts.map +1 -0
- package/dist/application/services/memory-application-service.js +190 -0
- package/dist/application/services/memory-application-service.js.map +1 -0
- package/dist/cache-manager.d.ts +134 -0
- package/dist/cache-manager.d.ts.map +1 -0
- package/dist/cache-manager.js +407 -0
- package/dist/cache-manager.js.map +1 -0
- package/dist/database-provider.d.ts +86 -0
- package/dist/database-provider.d.ts.map +1 -0
- package/dist/database-provider.js +385 -0
- package/dist/database-provider.js.map +1 -0
- package/dist/database-provider.test.d.ts +7 -0
- package/dist/database-provider.test.d.ts.map +1 -0
- package/dist/database-provider.test.js +285 -0
- package/dist/database-provider.test.js.map +1 -0
- package/dist/domain/entities/memory-entry.d.ts +143 -0
- package/dist/domain/entities/memory-entry.d.ts.map +1 -0
- package/dist/domain/entities/memory-entry.js +226 -0
- package/dist/domain/entities/memory-entry.js.map +1 -0
- package/dist/domain/index.d.ts +11 -0
- package/dist/domain/index.d.ts.map +1 -0
- package/dist/domain/index.js +12 -0
- package/dist/domain/index.js.map +1 -0
- package/dist/domain/repositories/memory-repository.interface.d.ts +102 -0
- package/dist/domain/repositories/memory-repository.interface.d.ts.map +1 -0
- package/dist/domain/repositories/memory-repository.interface.js +11 -0
- package/dist/domain/repositories/memory-repository.interface.js.map +1 -0
- package/dist/domain/services/memory-domain-service.d.ts +105 -0
- package/dist/domain/services/memory-domain-service.d.ts.map +1 -0
- package/dist/domain/services/memory-domain-service.js +297 -0
- package/dist/domain/services/memory-domain-service.js.map +1 -0
- package/dist/hnsw-index.d.ts +111 -0
- package/dist/hnsw-index.d.ts.map +1 -0
- package/dist/hnsw-index.js +781 -0
- package/dist/hnsw-index.js.map +1 -0
- package/dist/hybrid-backend.d.ts +217 -0
- package/dist/hybrid-backend.d.ts.map +1 -0
- package/dist/hybrid-backend.js +491 -0
- package/dist/hybrid-backend.js.map +1 -0
- package/dist/hybrid-backend.test.d.ts +8 -0
- package/dist/hybrid-backend.test.d.ts.map +1 -0
- package/dist/hybrid-backend.test.js +320 -0
- package/dist/hybrid-backend.test.js.map +1 -0
- package/dist/index.d.ts +188 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +345 -0
- package/dist/index.js.map +1 -0
- package/dist/infrastructure/index.d.ts +17 -0
- package/dist/infrastructure/index.d.ts.map +1 -0
- package/dist/infrastructure/index.js +16 -0
- package/dist/infrastructure/index.js.map +1 -0
- package/dist/infrastructure/repositories/hybrid-memory-repository.d.ts +66 -0
- package/dist/infrastructure/repositories/hybrid-memory-repository.d.ts.map +1 -0
- package/dist/infrastructure/repositories/hybrid-memory-repository.js +409 -0
- package/dist/infrastructure/repositories/hybrid-memory-repository.js.map +1 -0
- package/dist/migration.d.ts +68 -0
- package/dist/migration.d.ts.map +1 -0
- package/dist/migration.js +513 -0
- package/dist/migration.js.map +1 -0
- package/dist/query-builder.d.ts +211 -0
- package/dist/query-builder.d.ts.map +1 -0
- package/dist/query-builder.js +438 -0
- package/dist/query-builder.js.map +1 -0
- package/dist/sqlite-backend.d.ts +121 -0
- package/dist/sqlite-backend.d.ts.map +1 -0
- package/dist/sqlite-backend.js +564 -0
- package/dist/sqlite-backend.js.map +1 -0
- package/dist/sqljs-backend.d.ts +128 -0
- package/dist/sqljs-backend.d.ts.map +1 -0
- package/dist/sqljs-backend.js +598 -0
- package/dist/sqljs-backend.js.map +1 -0
- package/dist/types.d.ts +481 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +58 -0
- package/dist/types.js.map +1 -0
- package/docs/AGENTDB-INTEGRATION.md +388 -0
- package/docs/CROSS_PLATFORM.md +505 -0
- package/docs/WINDOWS_SUPPORT.md +422 -0
- package/examples/agentdb-example.ts +345 -0
- package/examples/cross-platform-usage.ts +326 -0
- package/framework/benchmark.ts +112 -0
- package/package.json +31 -0
- package/src/agentdb-adapter.ts +884 -0
- package/src/agentdb-backend.test.ts +339 -0
- package/src/agentdb-backend.ts +1016 -0
- package/src/application/commands/delete-memory.command.ts +172 -0
- package/src/application/commands/store-memory.command.ts +103 -0
- package/src/application/index.ts +36 -0
- package/src/application/queries/search-memory.query.ts +237 -0
- package/src/application/services/memory-application-service.ts +236 -0
- package/src/cache-manager.ts +516 -0
- package/src/database-provider.test.ts +364 -0
- package/src/database-provider.ts +511 -0
- package/src/domain/entities/memory-entry.ts +289 -0
- package/src/domain/index.ts +35 -0
- package/src/domain/repositories/memory-repository.interface.ts +120 -0
- package/src/domain/services/memory-domain-service.ts +403 -0
- package/src/hnsw-index.ts +1013 -0
- package/src/hybrid-backend.test.ts +399 -0
- package/src/hybrid-backend.ts +694 -0
- package/src/index.ts +515 -0
- package/src/infrastructure/index.ts +23 -0
- package/src/infrastructure/repositories/hybrid-memory-repository.ts +516 -0
- package/src/migration.ts +669 -0
- package/src/query-builder.ts +542 -0
- package/src/sqlite-backend.ts +732 -0
- package/src/sqljs-backend.ts +763 -0
- package/src/types.ts +727 -0
- package/tsconfig.json +9 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/verify-cross-platform.ts +170 -0
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache Hit Rate Benchmark
|
|
3
|
+
*
|
|
4
|
+
* Target: <0.1ms for cache hits
|
|
5
|
+
*
|
|
6
|
+
* Measures cache performance including hit rate,
|
|
7
|
+
* lookup time, and eviction strategies.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { benchmark, BenchmarkRunner, formatTime, meetsTarget } from '../framework/benchmark.js';
|
|
11
|
+
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// Cache Implementations
|
|
14
|
+
// ============================================================================
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Simple LRU Cache implementation
|
|
18
|
+
*/
|
|
19
|
+
class LRUCache<K, V> {
|
|
20
|
+
private cache = new Map<K, V>();
|
|
21
|
+
private maxSize: number;
|
|
22
|
+
private hits = 0;
|
|
23
|
+
private misses = 0;
|
|
24
|
+
|
|
25
|
+
constructor(maxSize: number) {
|
|
26
|
+
this.maxSize = maxSize;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
get(key: K): V | undefined {
|
|
30
|
+
const value = this.cache.get(key);
|
|
31
|
+
if (value !== undefined) {
|
|
32
|
+
// Move to end (most recently used)
|
|
33
|
+
this.cache.delete(key);
|
|
34
|
+
this.cache.set(key, value);
|
|
35
|
+
this.hits++;
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
this.misses++;
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
set(key: K, value: V): void {
|
|
43
|
+
if (this.cache.has(key)) {
|
|
44
|
+
this.cache.delete(key);
|
|
45
|
+
} else if (this.cache.size >= this.maxSize) {
|
|
46
|
+
// Evict oldest (first) entry
|
|
47
|
+
const firstKey = this.cache.keys().next().value;
|
|
48
|
+
this.cache.delete(firstKey);
|
|
49
|
+
}
|
|
50
|
+
this.cache.set(key, value);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
get hitRate(): number {
|
|
54
|
+
const total = this.hits + this.misses;
|
|
55
|
+
return total > 0 ? this.hits / total : 0;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
get size(): number {
|
|
59
|
+
return this.cache.size;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
clear(): void {
|
|
63
|
+
this.cache.clear();
|
|
64
|
+
this.hits = 0;
|
|
65
|
+
this.misses = 0;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* TTL Cache with expiration
|
|
71
|
+
*/
|
|
72
|
+
class TTLCache<K, V> {
|
|
73
|
+
private cache = new Map<K, { value: V; expiry: number }>();
|
|
74
|
+
private defaultTTL: number;
|
|
75
|
+
private hits = 0;
|
|
76
|
+
private misses = 0;
|
|
77
|
+
|
|
78
|
+
constructor(defaultTTL: number = 60000) {
|
|
79
|
+
this.defaultTTL = defaultTTL;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
get(key: K): V | undefined {
|
|
83
|
+
const entry = this.cache.get(key);
|
|
84
|
+
if (entry !== undefined) {
|
|
85
|
+
if (Date.now() < entry.expiry) {
|
|
86
|
+
this.hits++;
|
|
87
|
+
return entry.value;
|
|
88
|
+
}
|
|
89
|
+
// Expired
|
|
90
|
+
this.cache.delete(key);
|
|
91
|
+
}
|
|
92
|
+
this.misses++;
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
set(key: K, value: V, ttl?: number): void {
|
|
97
|
+
const expiry = Date.now() + (ttl || this.defaultTTL);
|
|
98
|
+
this.cache.set(key, { value, expiry });
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
get hitRate(): number {
|
|
102
|
+
const total = this.hits + this.misses;
|
|
103
|
+
return total > 0 ? this.hits / total : 0;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
get size(): number {
|
|
107
|
+
return this.cache.size;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
cleanup(): number {
|
|
111
|
+
const now = Date.now();
|
|
112
|
+
let cleaned = 0;
|
|
113
|
+
for (const [key, entry] of this.cache) {
|
|
114
|
+
if (now >= entry.expiry) {
|
|
115
|
+
this.cache.delete(key);
|
|
116
|
+
cleaned++;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return cleaned;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
clear(): void {
|
|
123
|
+
this.cache.clear();
|
|
124
|
+
this.hits = 0;
|
|
125
|
+
this.misses = 0;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Two-level cache (L1 fast, L2 larger)
|
|
131
|
+
*/
|
|
132
|
+
class TwoLevelCache<K, V> {
|
|
133
|
+
private l1: LRUCache<K, V>;
|
|
134
|
+
private l2: LRUCache<K, V>;
|
|
135
|
+
private l1Hits = 0;
|
|
136
|
+
private l2Hits = 0;
|
|
137
|
+
private misses = 0;
|
|
138
|
+
|
|
139
|
+
constructor(l1Size: number = 100, l2Size: number = 1000) {
|
|
140
|
+
this.l1 = new LRUCache<K, V>(l1Size);
|
|
141
|
+
this.l2 = new LRUCache<K, V>(l2Size);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
get(key: K): V | undefined {
|
|
145
|
+
// Check L1 first
|
|
146
|
+
let value = this.l1.get(key);
|
|
147
|
+
if (value !== undefined) {
|
|
148
|
+
this.l1Hits++;
|
|
149
|
+
return value;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Check L2
|
|
153
|
+
value = this.l2.get(key);
|
|
154
|
+
if (value !== undefined) {
|
|
155
|
+
this.l2Hits++;
|
|
156
|
+
// Promote to L1
|
|
157
|
+
this.l1.set(key, value);
|
|
158
|
+
return value;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
this.misses++;
|
|
162
|
+
return undefined;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
set(key: K, value: V): void {
|
|
166
|
+
this.l1.set(key, value);
|
|
167
|
+
this.l2.set(key, value);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
get stats(): { l1HitRate: number; l2HitRate: number; missRate: number } {
|
|
171
|
+
const total = this.l1Hits + this.l2Hits + this.misses;
|
|
172
|
+
return {
|
|
173
|
+
l1HitRate: total > 0 ? this.l1Hits / total : 0,
|
|
174
|
+
l2HitRate: total > 0 ? this.l2Hits / total : 0,
|
|
175
|
+
missRate: total > 0 ? this.misses / total : 0,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
clear(): void {
|
|
180
|
+
this.l1.clear();
|
|
181
|
+
this.l2.clear();
|
|
182
|
+
this.l1Hits = 0;
|
|
183
|
+
this.l2Hits = 0;
|
|
184
|
+
this.misses = 0;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Memoization cache for function results
|
|
190
|
+
*/
|
|
191
|
+
class MemoCache<T extends (...args: unknown[]) => unknown> {
|
|
192
|
+
private cache = new Map<string, ReturnType<T>>();
|
|
193
|
+
private fn: T;
|
|
194
|
+
|
|
195
|
+
constructor(fn: T) {
|
|
196
|
+
this.fn = fn;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
call(...args: Parameters<T>): ReturnType<T> {
|
|
200
|
+
const key = JSON.stringify(args);
|
|
201
|
+
if (this.cache.has(key)) {
|
|
202
|
+
return this.cache.get(key)!;
|
|
203
|
+
}
|
|
204
|
+
const result = this.fn(...args) as ReturnType<T>;
|
|
205
|
+
this.cache.set(key, result);
|
|
206
|
+
return result;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
get size(): number {
|
|
210
|
+
return this.cache.size;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
clear(): void {
|
|
214
|
+
this.cache.clear();
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// ============================================================================
|
|
219
|
+
// Benchmark Suite
|
|
220
|
+
// ============================================================================
|
|
221
|
+
|
|
222
|
+
export async function runCacheHitRateBenchmarks(): Promise<void> {
|
|
223
|
+
const runner = new BenchmarkRunner('Cache Hit Rate');
|
|
224
|
+
|
|
225
|
+
console.log('\n--- Cache Hit Rate Benchmarks ---\n');
|
|
226
|
+
|
|
227
|
+
// Prepare test data
|
|
228
|
+
const testKeys = Array.from({ length: 10000 }, (_, i) => `key-${i}`);
|
|
229
|
+
const testValues = testKeys.map((k) => ({ key: k, data: Math.random() }));
|
|
230
|
+
|
|
231
|
+
// Benchmark 1: LRU Cache - Cache Hit
|
|
232
|
+
const lruCache = new LRUCache<string, object>(1000);
|
|
233
|
+
|
|
234
|
+
// Pre-populate cache
|
|
235
|
+
for (let i = 0; i < 1000; i++) {
|
|
236
|
+
lruCache.set(testKeys[i]!, testValues[i]!);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const lruHitResult = await runner.run(
|
|
240
|
+
'lru-cache-hit',
|
|
241
|
+
async () => {
|
|
242
|
+
lruCache.get(testKeys[500]!);
|
|
243
|
+
},
|
|
244
|
+
{ iterations: 100000 }
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
console.log(`LRU Cache Hit: ${formatTime(lruHitResult.mean)}`);
|
|
248
|
+
const hitTarget = meetsTarget('cache-hit', lruHitResult.mean);
|
|
249
|
+
console.log(` Target (<0.1ms): ${hitTarget.met ? 'PASS' : 'FAIL'}`);
|
|
250
|
+
|
|
251
|
+
// Benchmark 2: LRU Cache - Cache Miss
|
|
252
|
+
const lruMissResult = await runner.run(
|
|
253
|
+
'lru-cache-miss',
|
|
254
|
+
async () => {
|
|
255
|
+
lruCache.get('non-existent-key');
|
|
256
|
+
},
|
|
257
|
+
{ iterations: 100000 }
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
console.log(`LRU Cache Miss: ${formatTime(lruMissResult.mean)}`);
|
|
261
|
+
|
|
262
|
+
// Benchmark 3: LRU Cache - Set Operation
|
|
263
|
+
const lruSetResult = await runner.run(
|
|
264
|
+
'lru-cache-set',
|
|
265
|
+
async () => {
|
|
266
|
+
lruCache.set(`new-key-${Date.now()}`, { data: 'test' });
|
|
267
|
+
},
|
|
268
|
+
{ iterations: 10000 }
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
console.log(`LRU Cache Set: ${formatTime(lruSetResult.mean)}`);
|
|
272
|
+
|
|
273
|
+
// Benchmark 4: TTL Cache - Hit
|
|
274
|
+
const ttlCache = new TTLCache<string, object>(60000);
|
|
275
|
+
|
|
276
|
+
for (let i = 0; i < 1000; i++) {
|
|
277
|
+
ttlCache.set(testKeys[i]!, testValues[i]!);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const ttlHitResult = await runner.run(
|
|
281
|
+
'ttl-cache-hit',
|
|
282
|
+
async () => {
|
|
283
|
+
ttlCache.get(testKeys[500]!);
|
|
284
|
+
},
|
|
285
|
+
{ iterations: 100000 }
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
console.log(`TTL Cache Hit: ${formatTime(ttlHitResult.mean)}`);
|
|
289
|
+
|
|
290
|
+
// Benchmark 5: TTL Cache - Cleanup
|
|
291
|
+
// Add some expired entries
|
|
292
|
+
for (let i = 0; i < 100; i++) {
|
|
293
|
+
ttlCache.set(`expired-${i}`, { data: i }, -1); // Already expired
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const ttlCleanupResult = await runner.run(
|
|
297
|
+
'ttl-cache-cleanup',
|
|
298
|
+
async () => {
|
|
299
|
+
ttlCache.cleanup();
|
|
300
|
+
},
|
|
301
|
+
{ iterations: 1000 }
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
console.log(`TTL Cache Cleanup: ${formatTime(ttlCleanupResult.mean)}`);
|
|
305
|
+
|
|
306
|
+
// Benchmark 6: Two-Level Cache - L1 Hit
|
|
307
|
+
const twoLevelCache = new TwoLevelCache<string, object>(100, 1000);
|
|
308
|
+
|
|
309
|
+
for (let i = 0; i < 1000; i++) {
|
|
310
|
+
twoLevelCache.set(testKeys[i]!, testValues[i]!);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Warm up L1
|
|
314
|
+
for (let i = 0; i < 50; i++) {
|
|
315
|
+
twoLevelCache.get(testKeys[i]!);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const l1HitResult = await runner.run(
|
|
319
|
+
'two-level-cache-l1-hit',
|
|
320
|
+
async () => {
|
|
321
|
+
twoLevelCache.get(testKeys[25]!);
|
|
322
|
+
},
|
|
323
|
+
{ iterations: 100000 }
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
console.log(`Two-Level Cache L1 Hit: ${formatTime(l1HitResult.mean)}`);
|
|
327
|
+
|
|
328
|
+
// Benchmark 7: Two-Level Cache - L2 Hit (promotes to L1)
|
|
329
|
+
const l2HitResult = await runner.run(
|
|
330
|
+
'two-level-cache-l2-hit',
|
|
331
|
+
async () => {
|
|
332
|
+
twoLevelCache.get(testKeys[500]!);
|
|
333
|
+
},
|
|
334
|
+
{ iterations: 50000 }
|
|
335
|
+
);
|
|
336
|
+
|
|
337
|
+
console.log(`Two-Level Cache L2 Hit: ${formatTime(l2HitResult.mean)}`);
|
|
338
|
+
|
|
339
|
+
// Benchmark 8: Memoization Cache
|
|
340
|
+
const expensiveFn = (n: number): number => {
|
|
341
|
+
let result = 0;
|
|
342
|
+
for (let i = 0; i < n; i++) {
|
|
343
|
+
result += Math.sqrt(i);
|
|
344
|
+
}
|
|
345
|
+
return result;
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
const memoCache = new MemoCache(expensiveFn);
|
|
349
|
+
|
|
350
|
+
// Prime the cache
|
|
351
|
+
memoCache.call(1000);
|
|
352
|
+
|
|
353
|
+
const memoHitResult = await runner.run(
|
|
354
|
+
'memo-cache-hit',
|
|
355
|
+
async () => {
|
|
356
|
+
memoCache.call(1000);
|
|
357
|
+
},
|
|
358
|
+
{ iterations: 100000 }
|
|
359
|
+
);
|
|
360
|
+
|
|
361
|
+
console.log(`Memo Cache Hit: ${formatTime(memoHitResult.mean)}`);
|
|
362
|
+
|
|
363
|
+
const memoMissResult = await runner.run(
|
|
364
|
+
'memo-cache-miss',
|
|
365
|
+
async () => {
|
|
366
|
+
memoCache.call(Math.floor(Math.random() * 10000000));
|
|
367
|
+
},
|
|
368
|
+
{ iterations: 100 }
|
|
369
|
+
);
|
|
370
|
+
|
|
371
|
+
console.log(`Memo Cache Miss (compute): ${formatTime(memoMissResult.mean)}`);
|
|
372
|
+
|
|
373
|
+
// Benchmark 9: Cache with Different Hit Rates
|
|
374
|
+
const cache90 = new LRUCache<string, object>(1000);
|
|
375
|
+
for (let i = 0; i < 1000; i++) {
|
|
376
|
+
cache90.set(testKeys[i]!, testValues[i]!);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const hitRate90Result = await runner.run(
|
|
380
|
+
'workload-90-percent-hits',
|
|
381
|
+
async () => {
|
|
382
|
+
const isHit = Math.random() < 0.9;
|
|
383
|
+
if (isHit) {
|
|
384
|
+
cache90.get(testKeys[Math.floor(Math.random() * 1000)]!);
|
|
385
|
+
} else {
|
|
386
|
+
cache90.get(`miss-${Date.now()}`);
|
|
387
|
+
}
|
|
388
|
+
},
|
|
389
|
+
{ iterations: 10000 }
|
|
390
|
+
);
|
|
391
|
+
|
|
392
|
+
console.log(`90% Hit Rate Workload: ${formatTime(hitRate90Result.mean)}`);
|
|
393
|
+
|
|
394
|
+
// Benchmark 10: Cache Eviction Under Pressure
|
|
395
|
+
const smallCache = new LRUCache<string, object>(100);
|
|
396
|
+
|
|
397
|
+
const evictionResult = await runner.run(
|
|
398
|
+
'cache-with-eviction',
|
|
399
|
+
async () => {
|
|
400
|
+
// Write 200 items to a cache of size 100
|
|
401
|
+
for (let i = 0; i < 200; i++) {
|
|
402
|
+
smallCache.set(`evict-key-${i}`, { data: i });
|
|
403
|
+
}
|
|
404
|
+
},
|
|
405
|
+
{ iterations: 100 }
|
|
406
|
+
);
|
|
407
|
+
|
|
408
|
+
console.log(`Cache with Eviction (200 to 100): ${formatTime(evictionResult.mean)}`);
|
|
409
|
+
|
|
410
|
+
// Summary
|
|
411
|
+
console.log('\n--- Summary ---');
|
|
412
|
+
console.log(`LRU hit: ${formatTime(lruHitResult.mean)} (${lruHitResult.opsPerSecond.toFixed(0)} ops/sec)`);
|
|
413
|
+
console.log(`LRU miss: ${formatTime(lruMissResult.mean)}`);
|
|
414
|
+
console.log(`TTL hit: ${formatTime(ttlHitResult.mean)}`);
|
|
415
|
+
console.log(`L1 hit: ${formatTime(l1HitResult.mean)}`);
|
|
416
|
+
console.log(`L2 hit: ${formatTime(l2HitResult.mean)}`);
|
|
417
|
+
console.log(`Memo hit: ${formatTime(memoHitResult.mean)}`);
|
|
418
|
+
console.log(`\nTwo-Level Cache Stats:`, twoLevelCache.stats);
|
|
419
|
+
|
|
420
|
+
// Print full results
|
|
421
|
+
runner.printResults();
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// ============================================================================
|
|
425
|
+
// Cache Optimization Strategies
|
|
426
|
+
// ============================================================================
|
|
427
|
+
|
|
428
|
+
export const cacheOptimizations = {
|
|
429
|
+
/**
|
|
430
|
+
* Optimal cache sizing
|
|
431
|
+
*/
|
|
432
|
+
optimalSizing: {
|
|
433
|
+
description: 'Size cache based on working set and memory constraints',
|
|
434
|
+
expectedImprovement: '20-50% hit rate',
|
|
435
|
+
implementation: `
|
|
436
|
+
function calculateOptimalCacheSize(workingSetSize: number, memoryLimit: number): number {
|
|
437
|
+
const avgItemSize = estimateAverageItemSize();
|
|
438
|
+
const maxItems = Math.floor(memoryLimit / avgItemSize);
|
|
439
|
+
return Math.min(workingSetSize * 0.8, maxItems);
|
|
440
|
+
}
|
|
441
|
+
`,
|
|
442
|
+
},
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Adaptive TTL
|
|
446
|
+
*/
|
|
447
|
+
adaptiveTTL: {
|
|
448
|
+
description: 'Adjust TTL based on access patterns',
|
|
449
|
+
expectedImprovement: '10-30% hit rate',
|
|
450
|
+
implementation: `
|
|
451
|
+
class AdaptiveTTLCache {
|
|
452
|
+
private accessCounts = new Map<string, number>();
|
|
453
|
+
|
|
454
|
+
getTTL(key: string): number {
|
|
455
|
+
const count = this.accessCounts.get(key) || 0;
|
|
456
|
+
// Hot keys get longer TTL
|
|
457
|
+
return this.baseTTL * Math.min(10, 1 + Math.log2(count + 1));
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
`,
|
|
461
|
+
},
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Probabilistic early expiration
|
|
465
|
+
*/
|
|
466
|
+
probabilisticExpiry: {
|
|
467
|
+
description: 'Refresh items before expiry to avoid thundering herd',
|
|
468
|
+
expectedImprovement: 'Reduces load spikes by 80%',
|
|
469
|
+
implementation: `
|
|
470
|
+
function shouldRefresh(ttl: number, remaining: number): boolean {
|
|
471
|
+
const beta = 1.0;
|
|
472
|
+
const delta = ttl - remaining;
|
|
473
|
+
const xFetch = Date.now() + delta * beta * Math.log(Math.random());
|
|
474
|
+
return xFetch > Date.now() + remaining;
|
|
475
|
+
}
|
|
476
|
+
`,
|
|
477
|
+
},
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* Segmented cache
|
|
481
|
+
*/
|
|
482
|
+
segmentedCache: {
|
|
483
|
+
description: 'Reduce lock contention with segmented caches',
|
|
484
|
+
expectedImprovement: '2-4x concurrent performance',
|
|
485
|
+
implementation: `
|
|
486
|
+
class SegmentedCache<K, V> {
|
|
487
|
+
private segments: LRUCache<K, V>[];
|
|
488
|
+
|
|
489
|
+
constructor(numSegments: number, sizePerSegment: number) {
|
|
490
|
+
this.segments = Array.from(
|
|
491
|
+
{ length: numSegments },
|
|
492
|
+
() => new LRUCache<K, V>(sizePerSegment)
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
private getSegment(key: K): LRUCache<K, V> {
|
|
497
|
+
const hash = this.hash(key);
|
|
498
|
+
return this.segments[hash % this.segments.length]!;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
`,
|
|
502
|
+
},
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Read-through caching
|
|
506
|
+
*/
|
|
507
|
+
readThrough: {
|
|
508
|
+
description: 'Automatically fetch missing items',
|
|
509
|
+
expectedImprovement: 'Simplifies code, consistent performance',
|
|
510
|
+
implementation: `
|
|
511
|
+
class ReadThroughCache<K, V> {
|
|
512
|
+
constructor(
|
|
513
|
+
private cache: LRUCache<K, V>,
|
|
514
|
+
private loader: (key: K) => Promise<V>
|
|
515
|
+
) {}
|
|
516
|
+
|
|
517
|
+
async get(key: K): Promise<V> {
|
|
518
|
+
let value = this.cache.get(key);
|
|
519
|
+
if (value === undefined) {
|
|
520
|
+
value = await this.loader(key);
|
|
521
|
+
this.cache.set(key, value);
|
|
522
|
+
}
|
|
523
|
+
return value;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
`,
|
|
527
|
+
},
|
|
528
|
+
};
|
|
529
|
+
|
|
530
|
+
// Run if executed directly
|
|
531
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
532
|
+
runCacheHitRateBenchmarks().catch(console.error);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
export default runCacheHitRateBenchmarks;
|