@fjell/cache 4.7.43 → 4.7.44
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/coverage_output.txt +94 -94
- package/dist/cache/layers/TwoLayerCacheMap.d.ts.map +1 -1
- package/dist/eviction/EvictionManager.d.ts.map +1 -1
- package/dist/index.js +845 -106
- package/dist/ops/retrieve.d.ts.map +1 -1
- package/dist/ops/set.d.ts.map +1 -1
- package/dist/ttl/TTLManager.d.ts.map +1 -1
- package/package.json +6 -6
- package/test_output.txt +96 -96
package/dist/index.js
CHANGED
|
@@ -94,48 +94,78 @@ var TwoLayerCacheMap = class _TwoLayerCacheMap extends CacheMap {
|
|
|
94
94
|
* Set a query result with rich metadata for two-layer architecture
|
|
95
95
|
*/
|
|
96
96
|
async setQueryResult(queryHash, itemKeys) {
|
|
97
|
+
logger.debug("QUERY_CACHE: TwoLayerCacheMap.setQueryResult() called", {
|
|
98
|
+
queryHash,
|
|
99
|
+
itemKeyCount: itemKeys.length,
|
|
100
|
+
itemKeys: itemKeys.map((k) => JSON.stringify(k))
|
|
101
|
+
});
|
|
97
102
|
await this.underlyingCache.setQueryResult(queryHash, itemKeys);
|
|
103
|
+
logger.debug("QUERY_CACHE: Stored query result in underlying cache", { queryHash });
|
|
98
104
|
const now = /* @__PURE__ */ new Date();
|
|
99
105
|
const isComplete = this.determineQueryCompleteness(queryHash, itemKeys);
|
|
100
106
|
const ttlSeconds = isComplete ? this.options.queryTTL : this.options.facetTTL;
|
|
107
|
+
const expiresAt = new Date(now.getTime() + ttlSeconds * 1e3);
|
|
101
108
|
const metadata = {
|
|
102
109
|
queryType: this.extractQueryType(queryHash),
|
|
103
110
|
isComplete,
|
|
104
111
|
createdAt: now,
|
|
105
|
-
expiresAt
|
|
112
|
+
expiresAt,
|
|
106
113
|
filter: this.extractFilter(queryHash),
|
|
107
114
|
params: this.extractParams(queryHash)
|
|
108
115
|
};
|
|
109
116
|
this.queryMetadataMap.set(queryHash, metadata);
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
logger.debug("QUERY_CACHE: Set query result with metadata", {
|
|
118
|
+
queryHash,
|
|
119
|
+
itemCount: itemKeys.length,
|
|
120
|
+
isComplete,
|
|
121
|
+
ttlSeconds,
|
|
122
|
+
queryType: metadata.queryType,
|
|
123
|
+
createdAt: metadata.createdAt.toISOString(),
|
|
124
|
+
expiresAt: metadata.expiresAt.toISOString(),
|
|
125
|
+
filter: metadata.filter,
|
|
126
|
+
params: metadata.params
|
|
127
|
+
});
|
|
119
128
|
}
|
|
120
129
|
/**
|
|
121
130
|
* Get a query result with expiration checking
|
|
122
131
|
*/
|
|
123
132
|
async getQueryResult(queryHash) {
|
|
133
|
+
logger.debug("QUERY_CACHE: TwoLayerCacheMap.getQueryResult() called", { queryHash });
|
|
124
134
|
const metadata = this.queryMetadataMap.get(queryHash);
|
|
125
|
-
if (metadata
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
135
|
+
if (metadata) {
|
|
136
|
+
const now = /* @__PURE__ */ new Date();
|
|
137
|
+
const isExpired = metadata.expiresAt < now;
|
|
138
|
+
logger.debug("QUERY_CACHE: Query metadata found", {
|
|
139
|
+
queryHash,
|
|
140
|
+
isExpired,
|
|
141
|
+
expiresAt: metadata.expiresAt.toISOString(),
|
|
142
|
+
now: now.toISOString(),
|
|
143
|
+
isComplete: metadata.isComplete,
|
|
144
|
+
queryType: metadata.queryType
|
|
145
|
+
});
|
|
146
|
+
if (isExpired) {
|
|
147
|
+
logger.debug("QUERY_CACHE: Query result EXPIRED, removing", {
|
|
148
|
+
queryHash,
|
|
149
|
+
expiresAt: metadata.expiresAt.toISOString(),
|
|
150
|
+
now: now.toISOString()
|
|
151
|
+
});
|
|
152
|
+
await this.deleteQueryResult(queryHash);
|
|
153
|
+
return null;
|
|
129
154
|
}
|
|
130
|
-
|
|
155
|
+
} else {
|
|
156
|
+
logger.debug("QUERY_CACHE: No metadata found for query hash", { queryHash });
|
|
131
157
|
}
|
|
158
|
+
logger.debug("QUERY_CACHE: Fetching query result from underlying cache", { queryHash });
|
|
132
159
|
const result = await this.underlyingCache.getQueryResult(queryHash);
|
|
133
|
-
if (result
|
|
134
|
-
logger.debug("Query result cache
|
|
160
|
+
if (result) {
|
|
161
|
+
logger.debug("QUERY_CACHE: Query result retrieved from underlying cache", {
|
|
135
162
|
queryHash,
|
|
136
163
|
itemCount: result.length,
|
|
137
|
-
isComplete: metadata?.isComplete
|
|
164
|
+
isComplete: metadata?.isComplete,
|
|
165
|
+
itemKeys: result.map((k) => JSON.stringify(k))
|
|
138
166
|
});
|
|
167
|
+
} else {
|
|
168
|
+
logger.debug("QUERY_CACHE: No query result found in underlying cache", { queryHash });
|
|
139
169
|
}
|
|
140
170
|
return result;
|
|
141
171
|
}
|
|
@@ -150,25 +180,44 @@ var TwoLayerCacheMap = class _TwoLayerCacheMap extends CacheMap {
|
|
|
150
180
|
* Delete a query result and its metadata
|
|
151
181
|
*/
|
|
152
182
|
async deleteQueryResult(queryHash) {
|
|
183
|
+
logger.debug("QUERY_CACHE: TwoLayerCacheMap.deleteQueryResult() called", { queryHash });
|
|
184
|
+
const hadMetadata = this.queryMetadataMap.has(queryHash);
|
|
185
|
+
const metadata = this.queryMetadataMap.get(queryHash);
|
|
153
186
|
await this.underlyingCache.deleteQueryResult(queryHash);
|
|
154
187
|
this.queryMetadataMap.delete(queryHash);
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
188
|
+
logger.debug("QUERY_CACHE: Deleted query result", {
|
|
189
|
+
queryHash,
|
|
190
|
+
hadMetadata,
|
|
191
|
+
wasComplete: metadata?.isComplete,
|
|
192
|
+
queryType: metadata?.queryType
|
|
193
|
+
});
|
|
158
194
|
}
|
|
159
195
|
// ===== QUERY INVALIDATION (CRITICAL FOR TWO-LAYER ARCHITECTURE) =====
|
|
160
196
|
/**
|
|
161
197
|
* Invalidate queries that are affected by item changes
|
|
162
198
|
*/
|
|
163
199
|
async invalidateQueriesForItem(itemKey) {
|
|
200
|
+
logger.debug("QUERY_CACHE: Invalidating queries for item change", {
|
|
201
|
+
itemKey: JSON.stringify(itemKey)
|
|
202
|
+
});
|
|
164
203
|
const affectedQueries = await this.findQueriesContainingItem(itemKey);
|
|
204
|
+
logger.debug("QUERY_CACHE: Found queries containing item", {
|
|
205
|
+
itemKey: JSON.stringify(itemKey),
|
|
206
|
+
affectedQueryCount: affectedQueries.length,
|
|
207
|
+
affectedQueries
|
|
208
|
+
});
|
|
165
209
|
for (const queryHash of affectedQueries) {
|
|
166
210
|
await this.deleteQueryResult(queryHash);
|
|
167
211
|
}
|
|
168
|
-
if (
|
|
169
|
-
logger.debug("Invalidated queries for item change", {
|
|
212
|
+
if (affectedQueries.length > 0) {
|
|
213
|
+
logger.debug("QUERY_CACHE: Invalidated queries for item change", {
|
|
170
214
|
itemKey: JSON.stringify(itemKey),
|
|
171
|
-
queriesInvalidated: affectedQueries.length
|
|
215
|
+
queriesInvalidated: affectedQueries.length,
|
|
216
|
+
queryHashes: affectedQueries
|
|
217
|
+
});
|
|
218
|
+
} else {
|
|
219
|
+
logger.debug("QUERY_CACHE: No queries found containing item", {
|
|
220
|
+
itemKey: JSON.stringify(itemKey)
|
|
172
221
|
});
|
|
173
222
|
}
|
|
174
223
|
}
|
|
@@ -307,18 +356,40 @@ var TwoLayerCacheMap = class _TwoLayerCacheMap extends CacheMap {
|
|
|
307
356
|
* Clean up expired queries
|
|
308
357
|
*/
|
|
309
358
|
async cleanup() {
|
|
359
|
+
const startTime = Date.now();
|
|
310
360
|
const now = /* @__PURE__ */ new Date();
|
|
311
361
|
const expiredQueries = [];
|
|
362
|
+
logger.debug("TWO_LAYER: Starting query cleanup", {
|
|
363
|
+
totalQueries: this.queryMetadataMap.size,
|
|
364
|
+
now: now.toISOString()
|
|
365
|
+
});
|
|
312
366
|
for (const [queryHash, metadata] of this.queryMetadataMap.entries()) {
|
|
313
367
|
if (metadata.expiresAt < now) {
|
|
314
368
|
expiredQueries.push(queryHash);
|
|
369
|
+
logger.debug("TWO_LAYER: Found expired query", {
|
|
370
|
+
queryHash,
|
|
371
|
+
queryType: metadata.queryType,
|
|
372
|
+
isComplete: metadata.isComplete,
|
|
373
|
+
expiresAt: metadata.expiresAt.toISOString(),
|
|
374
|
+
expiredByMs: now.getTime() - metadata.expiresAt.getTime()
|
|
375
|
+
});
|
|
315
376
|
}
|
|
316
377
|
}
|
|
317
378
|
for (const queryHash of expiredQueries) {
|
|
318
379
|
await this.deleteQueryResult(queryHash);
|
|
319
380
|
}
|
|
320
|
-
|
|
321
|
-
|
|
381
|
+
const duration = Date.now() - startTime;
|
|
382
|
+
if (expiredQueries.length > 0) {
|
|
383
|
+
logger.debug("TWO_LAYER: Query cleanup completed", {
|
|
384
|
+
expiredCount: expiredQueries.length,
|
|
385
|
+
totalQueries: this.queryMetadataMap.size,
|
|
386
|
+
duration
|
|
387
|
+
});
|
|
388
|
+
} else {
|
|
389
|
+
logger.debug("TWO_LAYER: Query cleanup - no expired queries", {
|
|
390
|
+
totalQueries: this.queryMetadataMap.size,
|
|
391
|
+
duration
|
|
392
|
+
});
|
|
322
393
|
}
|
|
323
394
|
return expiredQueries.length;
|
|
324
395
|
}
|
|
@@ -330,24 +401,68 @@ var TwoLayerCacheMap = class _TwoLayerCacheMap extends CacheMap {
|
|
|
330
401
|
// The types are accessible through super.types
|
|
331
402
|
// ===== MISSING ABSTRACT METHODS FROM CacheMap =====
|
|
332
403
|
async invalidateItemKeys(keys) {
|
|
404
|
+
const startTime = Date.now();
|
|
405
|
+
logger.debug("TWO_LAYER: Invalidating item keys", {
|
|
406
|
+
keyCount: keys.length,
|
|
407
|
+
keys: keys.map((k) => JSON.stringify(k))
|
|
408
|
+
});
|
|
333
409
|
if ("invalidateItemKeys" in this.underlyingCache && typeof this.underlyingCache.invalidateItemKeys === "function") {
|
|
334
410
|
await this.underlyingCache.invalidateItemKeys(keys);
|
|
335
411
|
}
|
|
412
|
+
let totalInvalidatedQueries = 0;
|
|
336
413
|
for (const key of keys) {
|
|
414
|
+
const beforeCount = this.queryMetadataMap.size;
|
|
337
415
|
await this.invalidateQueriesForItem(key);
|
|
416
|
+
const afterCount = this.queryMetadataMap.size;
|
|
417
|
+
const invalidated = beforeCount - afterCount;
|
|
418
|
+
totalInvalidatedQueries += invalidated;
|
|
419
|
+
if (invalidated > 0) {
|
|
420
|
+
logger.debug("TWO_LAYER: Invalidated queries for item", {
|
|
421
|
+
key: JSON.stringify(key),
|
|
422
|
+
queriesInvalidated: invalidated
|
|
423
|
+
});
|
|
424
|
+
}
|
|
338
425
|
}
|
|
426
|
+
const duration = Date.now() - startTime;
|
|
427
|
+
logger.debug("TWO_LAYER: Item key invalidation completed", {
|
|
428
|
+
keyCount: keys.length,
|
|
429
|
+
totalQueriesInvalidated: totalInvalidatedQueries,
|
|
430
|
+
duration
|
|
431
|
+
});
|
|
339
432
|
}
|
|
340
433
|
async invalidateLocation(locations) {
|
|
434
|
+
const startTime = Date.now();
|
|
435
|
+
const queryCountBefore = this.queryMetadataMap.size;
|
|
436
|
+
logger.debug("TWO_LAYER: Invalidating location", {
|
|
437
|
+
locations: JSON.stringify(locations),
|
|
438
|
+
queryCountBefore
|
|
439
|
+
});
|
|
341
440
|
if ("invalidateLocation" in this.underlyingCache && typeof this.underlyingCache.invalidateLocation === "function") {
|
|
342
441
|
await this.underlyingCache.invalidateLocation(locations);
|
|
343
442
|
}
|
|
344
443
|
this.queryMetadataMap.clear();
|
|
444
|
+
const duration = Date.now() - startTime;
|
|
445
|
+
logger.debug("TWO_LAYER: Location invalidation completed", {
|
|
446
|
+
locations: JSON.stringify(locations),
|
|
447
|
+
queriesCleared: queryCountBefore,
|
|
448
|
+
duration
|
|
449
|
+
});
|
|
345
450
|
}
|
|
346
451
|
async clearQueryResults() {
|
|
452
|
+
const startTime = Date.now();
|
|
453
|
+
const queryCountBefore = this.queryMetadataMap.size;
|
|
454
|
+
logger.debug("TWO_LAYER: Clearing all query results", {
|
|
455
|
+
queryCountBefore
|
|
456
|
+
});
|
|
347
457
|
if ("clearQueryResults" in this.underlyingCache && typeof this.underlyingCache.clearQueryResults === "function") {
|
|
348
458
|
await this.underlyingCache.clearQueryResults();
|
|
349
459
|
}
|
|
350
460
|
this.queryMetadataMap.clear();
|
|
461
|
+
const duration = Date.now() - startTime;
|
|
462
|
+
logger.debug("TWO_LAYER: Cleared all query results", {
|
|
463
|
+
queriesCleared: queryCountBefore,
|
|
464
|
+
duration
|
|
465
|
+
});
|
|
351
466
|
}
|
|
352
467
|
/**
|
|
353
468
|
* Check if two-layer mode is enabled
|
|
@@ -702,66 +817,151 @@ async function executeAllLogic(query, locations, context) {
|
|
|
702
817
|
}
|
|
703
818
|
}
|
|
704
819
|
const queryHash = createQueryHash(pkType, query, locations);
|
|
705
|
-
logger2.debug("Generated query hash for all", {
|
|
820
|
+
logger2.debug("QUERY_CACHE: Generated query hash for all()", {
|
|
821
|
+
queryHash,
|
|
822
|
+
query: JSON.stringify(query),
|
|
823
|
+
locations: JSON.stringify(locations),
|
|
824
|
+
pkType
|
|
825
|
+
});
|
|
826
|
+
logger2.debug("QUERY_CACHE: Checking query cache for hash", { queryHash });
|
|
706
827
|
const cachedItemKeys = await cacheMap.getQueryResult(queryHash);
|
|
707
828
|
if (cachedItemKeys) {
|
|
708
|
-
logger2.debug("
|
|
829
|
+
logger2.debug("QUERY_CACHE: Cache HIT - Found cached query result", {
|
|
830
|
+
queryHash,
|
|
831
|
+
cachedKeyCount: cachedItemKeys.length,
|
|
832
|
+
itemKeys: cachedItemKeys.map((k) => JSON.stringify(k))
|
|
833
|
+
});
|
|
709
834
|
const cachedItems = [];
|
|
710
835
|
let allItemsAvailable = true;
|
|
836
|
+
const missingKeys = [];
|
|
711
837
|
for (const itemKey of cachedItemKeys) {
|
|
712
838
|
const item = await cacheMap.get(itemKey);
|
|
713
839
|
if (item) {
|
|
714
840
|
cachedItems.push(item);
|
|
841
|
+
logger2.debug("QUERY_CACHE: Retrieved cached item", {
|
|
842
|
+
itemKey: JSON.stringify(itemKey),
|
|
843
|
+
itemKeyStr: JSON.stringify(item.key)
|
|
844
|
+
});
|
|
715
845
|
} else {
|
|
716
846
|
allItemsAvailable = false;
|
|
847
|
+
missingKeys.push(itemKey);
|
|
848
|
+
logger2.debug("QUERY_CACHE: Cached item MISSING from item cache", {
|
|
849
|
+
itemKey: JSON.stringify(itemKey),
|
|
850
|
+
queryHash
|
|
851
|
+
});
|
|
717
852
|
break;
|
|
718
853
|
}
|
|
719
854
|
}
|
|
720
855
|
if (allItemsAvailable) {
|
|
856
|
+
logger2.debug("QUERY_CACHE: All cached items available, returning from cache", {
|
|
857
|
+
queryHash,
|
|
858
|
+
itemCount: cachedItems.length
|
|
859
|
+
});
|
|
721
860
|
return cachedItems;
|
|
722
861
|
} else {
|
|
723
|
-
logger2.debug("Some cached items missing, invalidating query cache"
|
|
862
|
+
logger2.debug("QUERY_CACHE: Some cached items missing, invalidating query cache", {
|
|
863
|
+
queryHash,
|
|
864
|
+
missingKeys: missingKeys.map((k) => JSON.stringify(k)),
|
|
865
|
+
foundCount: cachedItems.length,
|
|
866
|
+
expectedCount: cachedItemKeys.length
|
|
867
|
+
});
|
|
724
868
|
cacheMap.deleteQueryResult(queryHash);
|
|
725
869
|
}
|
|
870
|
+
} else {
|
|
871
|
+
logger2.debug("QUERY_CACHE: Cache MISS - No cached query result found", { queryHash });
|
|
726
872
|
}
|
|
873
|
+
logger2.debug("QUERY_CACHE: Attempting direct cache query using queryIn()", {
|
|
874
|
+
queryHash,
|
|
875
|
+
query: JSON.stringify(query),
|
|
876
|
+
locations: JSON.stringify(locations)
|
|
877
|
+
});
|
|
727
878
|
try {
|
|
728
879
|
const directCachedItems = await cacheMap.queryIn(query, locations);
|
|
729
880
|
if (directCachedItems && directCachedItems.length > 0) {
|
|
730
|
-
logger2.debug("Found items
|
|
881
|
+
logger2.debug("QUERY_CACHE: Direct cache query SUCCESS - Found items in item cache", {
|
|
882
|
+
queryHash,
|
|
883
|
+
itemCount: directCachedItems.length,
|
|
884
|
+
itemKeys: directCachedItems.map((item) => JSON.stringify(item.key))
|
|
885
|
+
});
|
|
731
886
|
const itemKeys = directCachedItems.map((item) => item.key);
|
|
732
887
|
await cacheMap.setQueryResult(queryHash, itemKeys);
|
|
733
|
-
logger2.debug("
|
|
888
|
+
logger2.debug("QUERY_CACHE: Stored query result from direct cache hit", {
|
|
889
|
+
queryHash,
|
|
890
|
+
itemKeyCount: itemKeys.length,
|
|
891
|
+
itemKeys: itemKeys.map((k) => JSON.stringify(k))
|
|
892
|
+
});
|
|
734
893
|
return directCachedItems;
|
|
894
|
+
} else {
|
|
895
|
+
logger2.debug("QUERY_CACHE: Direct cache query returned no items", { queryHash });
|
|
735
896
|
}
|
|
736
897
|
} catch (error) {
|
|
737
|
-
logger2.debug("Error querying cache directly, proceeding to API", {
|
|
898
|
+
logger2.debug("QUERY_CACHE: Error querying cache directly, proceeding to API", {
|
|
899
|
+
queryHash,
|
|
900
|
+
error: error instanceof Error ? error.message : String(error)
|
|
901
|
+
});
|
|
738
902
|
}
|
|
903
|
+
logger2.debug("QUERY_CACHE: Fetching from API (cache miss or invalid)", {
|
|
904
|
+
queryHash,
|
|
905
|
+
query: JSON.stringify(query),
|
|
906
|
+
locations: JSON.stringify(locations)
|
|
907
|
+
});
|
|
739
908
|
let ret = [];
|
|
740
909
|
try {
|
|
741
910
|
ret = await api.all(query, locations);
|
|
911
|
+
logger2.debug("QUERY_CACHE: API response received", {
|
|
912
|
+
queryHash,
|
|
913
|
+
itemCount: ret.length,
|
|
914
|
+
itemKeys: ret.map((item) => JSON.stringify(item.key))
|
|
915
|
+
});
|
|
916
|
+
logger2.debug("QUERY_CACHE: Storing items in item cache", {
|
|
917
|
+
queryHash,
|
|
918
|
+
itemCount: ret.length
|
|
919
|
+
});
|
|
742
920
|
for (const v of ret) {
|
|
743
921
|
await cacheMap.set(v.key, v);
|
|
922
|
+
logger2.debug("QUERY_CACHE: Stored item in cache", {
|
|
923
|
+
itemKey: JSON.stringify(v.key),
|
|
924
|
+
queryHash
|
|
925
|
+
});
|
|
744
926
|
const keyStr = JSON.stringify(v.key);
|
|
745
927
|
ttlManager.onItemAdded(keyStr, cacheMap);
|
|
746
928
|
const evictedKeys = await context.evictionManager.onItemAdded(keyStr, v, cacheMap);
|
|
747
929
|
for (const evictedKey of evictedKeys) {
|
|
748
930
|
const parsedKey = JSON.parse(evictedKey);
|
|
749
931
|
await cacheMap.delete(parsedKey);
|
|
932
|
+
logger2.debug("QUERY_CACHE: Evicted item due to cache limits", {
|
|
933
|
+
evictedKey,
|
|
934
|
+
queryHash
|
|
935
|
+
});
|
|
750
936
|
}
|
|
751
937
|
}
|
|
752
938
|
const itemKeys = ret.map((item) => item.key);
|
|
753
|
-
cacheMap.setQueryResult(queryHash, itemKeys);
|
|
754
|
-
logger2.debug("
|
|
939
|
+
await cacheMap.setQueryResult(queryHash, itemKeys);
|
|
940
|
+
logger2.debug("QUERY_CACHE: Stored query result in query cache", {
|
|
941
|
+
queryHash,
|
|
942
|
+
itemKeyCount: itemKeys.length,
|
|
943
|
+
itemKeys: itemKeys.map((k) => JSON.stringify(k))
|
|
944
|
+
});
|
|
755
945
|
const event = CacheEventFactory.createQueryEvent(query, locations, ret);
|
|
756
946
|
context.eventEmitter.emit(event);
|
|
947
|
+
logger2.debug("QUERY_CACHE: Emitted query event", { queryHash });
|
|
757
948
|
} catch (e) {
|
|
758
949
|
if (e instanceof NotFoundError) {
|
|
759
|
-
|
|
760
|
-
|
|
950
|
+
logger2.debug("QUERY_CACHE: API returned NotFoundError, caching empty result", { queryHash });
|
|
951
|
+
await cacheMap.setQueryResult(queryHash, []);
|
|
952
|
+
logger2.debug("QUERY_CACHE: Cached empty query result for not found", { queryHash });
|
|
761
953
|
} else {
|
|
954
|
+
logger2.debug("QUERY_CACHE: API error occurred", {
|
|
955
|
+
queryHash,
|
|
956
|
+
error: e instanceof Error ? e.message : String(e)
|
|
957
|
+
});
|
|
762
958
|
throw e;
|
|
763
959
|
}
|
|
764
960
|
}
|
|
961
|
+
logger2.debug("QUERY_CACHE: all() operation completed", {
|
|
962
|
+
queryHash,
|
|
963
|
+
resultCount: ret.length
|
|
964
|
+
});
|
|
765
965
|
return ret;
|
|
766
966
|
}
|
|
767
967
|
|
|
@@ -964,37 +1164,93 @@ async function executeOneLogic(query, locations, context) {
|
|
|
964
1164
|
}
|
|
965
1165
|
}
|
|
966
1166
|
const queryHash = createQueryHash(pkType, query, locations);
|
|
967
|
-
logger3.debug("Generated query hash for one", {
|
|
1167
|
+
logger3.debug("QUERY_CACHE: Generated query hash for one()", {
|
|
1168
|
+
queryHash,
|
|
1169
|
+
query: JSON.stringify(query),
|
|
1170
|
+
locations: JSON.stringify(locations),
|
|
1171
|
+
pkType
|
|
1172
|
+
});
|
|
1173
|
+
logger3.debug("QUERY_CACHE: Checking query cache for hash", { queryHash });
|
|
968
1174
|
const cachedItemKeys = await cacheMap.getQueryResult(queryHash);
|
|
969
1175
|
if (cachedItemKeys) {
|
|
970
|
-
logger3.debug("
|
|
1176
|
+
logger3.debug("QUERY_CACHE: Cache HIT - Found cached query result", {
|
|
1177
|
+
queryHash,
|
|
1178
|
+
cachedKeyCount: cachedItemKeys.length,
|
|
1179
|
+
itemKeys: cachedItemKeys.map((k) => JSON.stringify(k))
|
|
1180
|
+
});
|
|
971
1181
|
if (cachedItemKeys.length === 0) {
|
|
1182
|
+
logger3.debug("QUERY_CACHE: Cached empty result (not found)", { queryHash });
|
|
972
1183
|
return null;
|
|
973
1184
|
}
|
|
974
|
-
const
|
|
1185
|
+
const itemKey = cachedItemKeys[0];
|
|
1186
|
+
logger3.debug("QUERY_CACHE: Retrieving first cached item", {
|
|
1187
|
+
queryHash,
|
|
1188
|
+
itemKey: JSON.stringify(itemKey)
|
|
1189
|
+
});
|
|
1190
|
+
const item = await cacheMap.get(itemKey);
|
|
975
1191
|
if (item) {
|
|
1192
|
+
logger3.debug("QUERY_CACHE: Retrieved cached item successfully", {
|
|
1193
|
+
queryHash,
|
|
1194
|
+
itemKey: JSON.stringify(itemKey),
|
|
1195
|
+
itemKeyStr: JSON.stringify(item.key)
|
|
1196
|
+
});
|
|
976
1197
|
return item;
|
|
977
1198
|
} else {
|
|
978
|
-
logger3.debug("Cached item
|
|
1199
|
+
logger3.debug("QUERY_CACHE: Cached item MISSING from item cache, invalidating query cache", {
|
|
1200
|
+
queryHash,
|
|
1201
|
+
itemKey: JSON.stringify(itemKey)
|
|
1202
|
+
});
|
|
979
1203
|
cacheMap.deleteQueryResult(queryHash);
|
|
980
1204
|
}
|
|
1205
|
+
} else {
|
|
1206
|
+
logger3.debug("QUERY_CACHE: Cache MISS - No cached query result found", { queryHash });
|
|
981
1207
|
}
|
|
1208
|
+
logger3.debug("QUERY_CACHE: Attempting direct cache query using queryIn()", {
|
|
1209
|
+
queryHash,
|
|
1210
|
+
query: JSON.stringify(query),
|
|
1211
|
+
locations: JSON.stringify(locations)
|
|
1212
|
+
});
|
|
982
1213
|
try {
|
|
983
1214
|
const directCachedItems = await cacheMap.queryIn(query, locations);
|
|
984
1215
|
if (directCachedItems && directCachedItems.length > 0) {
|
|
985
|
-
logger3.debug("Found item
|
|
1216
|
+
logger3.debug("QUERY_CACHE: Direct cache query SUCCESS - Found item in item cache", {
|
|
1217
|
+
queryHash,
|
|
1218
|
+
itemCount: directCachedItems.length,
|
|
1219
|
+
itemKeys: directCachedItems.map((item) => JSON.stringify(item.key))
|
|
1220
|
+
});
|
|
986
1221
|
const foundItem = directCachedItems[0];
|
|
987
1222
|
await cacheMap.setQueryResult(queryHash, [foundItem.key]);
|
|
988
|
-
logger3.debug("
|
|
1223
|
+
logger3.debug("QUERY_CACHE: Stored query result from direct cache hit", {
|
|
1224
|
+
queryHash,
|
|
1225
|
+
itemKey: JSON.stringify(foundItem.key)
|
|
1226
|
+
});
|
|
989
1227
|
return foundItem;
|
|
1228
|
+
} else {
|
|
1229
|
+
logger3.debug("QUERY_CACHE: Direct cache query returned no items", { queryHash });
|
|
990
1230
|
}
|
|
991
1231
|
} catch (error) {
|
|
992
|
-
logger3.debug("Error querying cache directly, proceeding to API", {
|
|
1232
|
+
logger3.debug("QUERY_CACHE: Error querying cache directly, proceeding to API", {
|
|
1233
|
+
queryHash,
|
|
1234
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1235
|
+
});
|
|
993
1236
|
}
|
|
1237
|
+
logger3.debug("QUERY_CACHE: Fetching from API (cache miss or invalid)", {
|
|
1238
|
+
queryHash,
|
|
1239
|
+
query: JSON.stringify(query),
|
|
1240
|
+
locations: JSON.stringify(locations)
|
|
1241
|
+
});
|
|
994
1242
|
let retItem = null;
|
|
995
1243
|
try {
|
|
996
1244
|
retItem = await api.one(query, locations);
|
|
997
1245
|
if (retItem) {
|
|
1246
|
+
logger3.debug("QUERY_CACHE: API response received", {
|
|
1247
|
+
queryHash,
|
|
1248
|
+
itemKey: JSON.stringify(retItem.key)
|
|
1249
|
+
});
|
|
1250
|
+
logger3.debug("QUERY_CACHE: Storing item in item cache", {
|
|
1251
|
+
queryHash,
|
|
1252
|
+
itemKey: JSON.stringify(retItem.key)
|
|
1253
|
+
});
|
|
998
1254
|
await cacheMap.set(retItem.key, retItem);
|
|
999
1255
|
const keyStr = JSON.stringify(retItem.key);
|
|
1000
1256
|
const metadata = await cacheMap.getMetadata(keyStr);
|
|
@@ -1014,21 +1270,38 @@ async function executeOneLogic(query, locations, context) {
|
|
|
1014
1270
|
for (const evictedKey of evictedKeys) {
|
|
1015
1271
|
const parsedKey = JSON.parse(evictedKey);
|
|
1016
1272
|
await cacheMap.delete(parsedKey);
|
|
1273
|
+
logger3.debug("QUERY_CACHE: Evicted item due to cache limits", {
|
|
1274
|
+
evictedKey,
|
|
1275
|
+
queryHash
|
|
1276
|
+
});
|
|
1017
1277
|
}
|
|
1018
1278
|
await cacheMap.setQueryResult(queryHash, [retItem.key]);
|
|
1019
|
-
logger3.debug("
|
|
1279
|
+
logger3.debug("QUERY_CACHE: Stored query result in query cache", {
|
|
1280
|
+
queryHash,
|
|
1281
|
+
itemKey: JSON.stringify(retItem.key)
|
|
1282
|
+
});
|
|
1020
1283
|
} else {
|
|
1284
|
+
logger3.debug("QUERY_CACHE: API returned null, caching empty result", { queryHash });
|
|
1021
1285
|
await cacheMap.setQueryResult(queryHash, []);
|
|
1022
|
-
logger3.debug("Cached empty query result", { queryHash });
|
|
1286
|
+
logger3.debug("QUERY_CACHE: Cached empty query result", { queryHash });
|
|
1023
1287
|
}
|
|
1024
1288
|
} catch (e) {
|
|
1025
1289
|
if (e instanceof NotFoundError2) {
|
|
1026
|
-
|
|
1027
|
-
|
|
1290
|
+
logger3.debug("QUERY_CACHE: API returned NotFoundError, caching empty result", { queryHash });
|
|
1291
|
+
await cacheMap.setQueryResult(queryHash, []);
|
|
1292
|
+
logger3.debug("QUERY_CACHE: Cached empty query result for not found", { queryHash });
|
|
1028
1293
|
} else {
|
|
1294
|
+
logger3.debug("QUERY_CACHE: API error occurred", {
|
|
1295
|
+
queryHash,
|
|
1296
|
+
error: e instanceof Error ? e.message : String(e)
|
|
1297
|
+
});
|
|
1029
1298
|
throw e;
|
|
1030
1299
|
}
|
|
1031
1300
|
}
|
|
1301
|
+
logger3.debug("QUERY_CACHE: one() operation completed", {
|
|
1302
|
+
queryHash,
|
|
1303
|
+
result: retItem ? JSON.stringify(retItem.key) : null
|
|
1304
|
+
});
|
|
1032
1305
|
return retItem || null;
|
|
1033
1306
|
}
|
|
1034
1307
|
|
|
@@ -1110,68 +1383,128 @@ var get = async (key, context) => {
|
|
|
1110
1383
|
return [context, result];
|
|
1111
1384
|
};
|
|
1112
1385
|
async function executeGetLogic(key, context) {
|
|
1386
|
+
const startTime = Date.now();
|
|
1113
1387
|
const { api, cacheMap, pkType, ttlManager, statsManager } = context;
|
|
1388
|
+
const keyStr = JSON.stringify(key);
|
|
1389
|
+
logger5.debug("CACHE_OP: get() started", {
|
|
1390
|
+
key: keyStr,
|
|
1391
|
+
ttlEnabled: ttlManager.isTTLEnabled(),
|
|
1392
|
+
defaultTTL: ttlManager.getDefaultTTL(),
|
|
1393
|
+
cacheType: cacheMap.implementationType
|
|
1394
|
+
});
|
|
1114
1395
|
statsManager.incrementRequests();
|
|
1115
1396
|
if (!isValidItemKey(key)) {
|
|
1116
|
-
logger5.error("
|
|
1397
|
+
logger5.error("CACHE_OP: Invalid key for get", { key: keyStr });
|
|
1117
1398
|
throw new Error("Key for Get is not a valid ItemKey");
|
|
1118
1399
|
}
|
|
1119
1400
|
if (context.options?.bypassCache) {
|
|
1120
|
-
logger5.debug("Cache bypass enabled, fetching directly from API", { key });
|
|
1401
|
+
logger5.debug("CACHE_OP: Cache bypass enabled, fetching directly from API", { key: keyStr });
|
|
1121
1402
|
statsManager.incrementMisses();
|
|
1122
1403
|
try {
|
|
1404
|
+
const apiStartTime = Date.now();
|
|
1123
1405
|
const ret2 = await api.get(key);
|
|
1406
|
+
const apiDuration = Date.now() - apiStartTime;
|
|
1124
1407
|
if (ret2) {
|
|
1125
|
-
logger5.debug("API response received (not cached
|
|
1408
|
+
logger5.debug("CACHE_OP: API response received (bypass mode, not cached)", {
|
|
1409
|
+
key: keyStr,
|
|
1410
|
+
apiDuration,
|
|
1411
|
+
totalDuration: Date.now() - startTime
|
|
1412
|
+
});
|
|
1126
1413
|
return ret2;
|
|
1127
1414
|
} else {
|
|
1128
|
-
logger5.debug("API returned null", {
|
|
1415
|
+
logger5.debug("CACHE_OP: API returned null (bypass mode)", {
|
|
1416
|
+
key: keyStr,
|
|
1417
|
+
apiDuration
|
|
1418
|
+
});
|
|
1129
1419
|
return null;
|
|
1130
1420
|
}
|
|
1131
1421
|
} catch (error) {
|
|
1132
|
-
logger5.error("API request failed", {
|
|
1422
|
+
logger5.error("CACHE_OP: API request failed in bypass mode", {
|
|
1423
|
+
key: keyStr,
|
|
1424
|
+
duration: Date.now() - startTime,
|
|
1425
|
+
error
|
|
1426
|
+
});
|
|
1133
1427
|
throw error;
|
|
1134
1428
|
}
|
|
1135
1429
|
}
|
|
1136
1430
|
if (ttlManager.isTTLEnabled()) {
|
|
1137
|
-
const
|
|
1431
|
+
const cacheCheckStart = Date.now();
|
|
1138
1432
|
const cachedItem = await cacheMap.get(key);
|
|
1433
|
+
const cacheCheckDuration = Date.now() - cacheCheckStart;
|
|
1139
1434
|
if (cachedItem) {
|
|
1140
|
-
|
|
1435
|
+
logger5.debug("CACHE_OP: Item found in cache, checking TTL validity", {
|
|
1436
|
+
key: keyStr,
|
|
1437
|
+
cacheCheckDuration,
|
|
1438
|
+
defaultTTL: ttlManager.getDefaultTTL()
|
|
1439
|
+
});
|
|
1440
|
+
const ttlCheckStart = Date.now();
|
|
1441
|
+
const isValid = await ttlManager.validateItem(keyStr, cacheMap);
|
|
1442
|
+
const ttlCheckDuration = Date.now() - ttlCheckStart;
|
|
1141
1443
|
if (isValid) {
|
|
1142
|
-
|
|
1444
|
+
const totalDuration = Date.now() - startTime;
|
|
1445
|
+
logger5.debug("CACHE_OP: Cache HIT with valid TTL", {
|
|
1446
|
+
key: keyStr,
|
|
1447
|
+
cacheCheckDuration,
|
|
1448
|
+
ttlCheckDuration,
|
|
1449
|
+
totalDuration,
|
|
1450
|
+
defaultTTL: ttlManager.getDefaultTTL()
|
|
1451
|
+
});
|
|
1143
1452
|
statsManager.incrementHits();
|
|
1144
1453
|
return cachedItem;
|
|
1145
1454
|
} else {
|
|
1146
|
-
logger5.debug("Cache item
|
|
1455
|
+
logger5.debug("CACHE_OP: Cache item EXPIRED, removing from cache", {
|
|
1456
|
+
key: keyStr,
|
|
1457
|
+
cacheCheckDuration,
|
|
1458
|
+
ttlCheckDuration
|
|
1459
|
+
});
|
|
1147
1460
|
cacheMap.delete(key);
|
|
1148
1461
|
statsManager.incrementMisses();
|
|
1149
1462
|
}
|
|
1150
1463
|
} else {
|
|
1464
|
+
logger5.debug("CACHE_OP: Cache MISS (no item found)", {
|
|
1465
|
+
key: keyStr,
|
|
1466
|
+
cacheCheckDuration
|
|
1467
|
+
});
|
|
1151
1468
|
statsManager.incrementMisses();
|
|
1152
1469
|
}
|
|
1153
|
-
logger5.debug("
|
|
1470
|
+
logger5.debug("CACHE_OP: Proceeding to API fetch (TTL-enabled cache miss or expired)", {
|
|
1471
|
+
key: keyStr,
|
|
1472
|
+
defaultTTL: ttlManager.getDefaultTTL()
|
|
1473
|
+
});
|
|
1154
1474
|
} else {
|
|
1475
|
+
const cacheCheckStart = Date.now();
|
|
1155
1476
|
const cachedItem = await cacheMap.get(key);
|
|
1477
|
+
const cacheCheckDuration = Date.now() - cacheCheckStart;
|
|
1156
1478
|
if (cachedItem) {
|
|
1157
|
-
|
|
1479
|
+
const totalDuration = Date.now() - startTime;
|
|
1480
|
+
logger5.debug("CACHE_OP: Cache HIT (TTL disabled)", {
|
|
1481
|
+
key: keyStr,
|
|
1482
|
+
cacheCheckDuration,
|
|
1483
|
+
totalDuration
|
|
1484
|
+
});
|
|
1158
1485
|
statsManager.incrementHits();
|
|
1159
1486
|
return cachedItem;
|
|
1160
1487
|
} else {
|
|
1488
|
+
logger5.debug("CACHE_OP: Cache MISS (TTL disabled)", {
|
|
1489
|
+
key: keyStr,
|
|
1490
|
+
cacheCheckDuration
|
|
1491
|
+
});
|
|
1161
1492
|
statsManager.incrementMisses();
|
|
1162
1493
|
}
|
|
1163
1494
|
}
|
|
1164
1495
|
let ret;
|
|
1165
|
-
const
|
|
1496
|
+
const requestKeyStr = keyToString(key);
|
|
1166
1497
|
try {
|
|
1167
|
-
const requestEntry = inFlightRequests.get(
|
|
1498
|
+
const requestEntry = inFlightRequests.get(requestKeyStr);
|
|
1168
1499
|
let apiRequest;
|
|
1500
|
+
const apiStartTime = Date.now();
|
|
1169
1501
|
if (!requestEntry) {
|
|
1502
|
+
logger5.debug("CACHE_OP: Creating new API request", { key: keyStr });
|
|
1170
1503
|
apiRequest = api.get(key);
|
|
1171
1504
|
if (apiRequest && typeof apiRequest.then === "function") {
|
|
1172
1505
|
const timestamp = Date.now();
|
|
1173
|
-
inFlightRequests.set(
|
|
1174
|
-
const cleanup = () => inFlightRequests.delete(
|
|
1506
|
+
inFlightRequests.set(requestKeyStr, { promise: apiRequest, timestamp });
|
|
1507
|
+
const cleanup = () => inFlightRequests.delete(requestKeyStr);
|
|
1175
1508
|
if (typeof apiRequest.finally === "function") {
|
|
1176
1509
|
apiRequest.finally(cleanup);
|
|
1177
1510
|
} else {
|
|
@@ -1179,37 +1512,90 @@ async function executeGetLogic(key, context) {
|
|
|
1179
1512
|
}
|
|
1180
1513
|
}
|
|
1181
1514
|
} else {
|
|
1182
|
-
logger5.debug("Using in-flight request
|
|
1515
|
+
logger5.debug("CACHE_OP: Using existing in-flight request", {
|
|
1516
|
+
key: keyStr,
|
|
1517
|
+
requestAge: Date.now() - requestEntry.timestamp
|
|
1518
|
+
});
|
|
1183
1519
|
apiRequest = requestEntry.promise;
|
|
1184
1520
|
}
|
|
1185
1521
|
ret = await apiRequest;
|
|
1522
|
+
const apiDuration = Date.now() - apiStartTime;
|
|
1186
1523
|
if (ret) {
|
|
1524
|
+
logger5.debug("CACHE_OP: API request successful, caching result", {
|
|
1525
|
+
key: keyStr,
|
|
1526
|
+
apiDuration,
|
|
1527
|
+
itemKeyMatches: JSON.stringify(ret.key) === keyStr
|
|
1528
|
+
});
|
|
1529
|
+
const cacheSetStart = Date.now();
|
|
1187
1530
|
await cacheMap.set(ret.key, ret);
|
|
1188
|
-
const
|
|
1189
|
-
const
|
|
1531
|
+
const cacheSetDuration = Date.now() - cacheSetStart;
|
|
1532
|
+
const itemKeyStr = JSON.stringify(ret.key);
|
|
1533
|
+
const metadataStart = Date.now();
|
|
1534
|
+
const metadata = await cacheMap.getMetadata(itemKeyStr);
|
|
1190
1535
|
if (!metadata) {
|
|
1191
1536
|
const now = Date.now();
|
|
1537
|
+
const estimatedSize = estimateValueSize(ret);
|
|
1192
1538
|
const baseMetadata = {
|
|
1193
|
-
key:
|
|
1539
|
+
key: itemKeyStr,
|
|
1194
1540
|
addedAt: now,
|
|
1195
1541
|
lastAccessedAt: now,
|
|
1196
1542
|
accessCount: 1,
|
|
1197
|
-
estimatedSize
|
|
1543
|
+
estimatedSize
|
|
1198
1544
|
};
|
|
1199
|
-
await cacheMap.setMetadata(
|
|
1545
|
+
await cacheMap.setMetadata(itemKeyStr, baseMetadata);
|
|
1546
|
+
logger5.debug("CACHE_OP: Created base metadata for cached item", {
|
|
1547
|
+
key: itemKeyStr,
|
|
1548
|
+
estimatedSize
|
|
1549
|
+
});
|
|
1200
1550
|
}
|
|
1201
|
-
const
|
|
1202
|
-
|
|
1551
|
+
const metadataDuration = Date.now() - metadataStart;
|
|
1552
|
+
const evictionStart = Date.now();
|
|
1553
|
+
const evictedKeys = await context.evictionManager.onItemAdded(itemKeyStr, ret, cacheMap);
|
|
1554
|
+
const evictionDuration = Date.now() - evictionStart;
|
|
1555
|
+
if (evictedKeys.length > 0) {
|
|
1556
|
+
logger5.debug("CACHE_OP: Eviction triggered by new item", {
|
|
1557
|
+
key: itemKeyStr,
|
|
1558
|
+
evictedCount: evictedKeys.length,
|
|
1559
|
+
evictedKeys
|
|
1560
|
+
});
|
|
1561
|
+
}
|
|
1562
|
+
const ttlStart = Date.now();
|
|
1563
|
+
await ttlManager.onItemAdded(itemKeyStr, cacheMap);
|
|
1564
|
+
const ttlDuration = Date.now() - ttlStart;
|
|
1203
1565
|
for (const evictedKey of evictedKeys) {
|
|
1204
1566
|
const parsedKey = JSON.parse(evictedKey);
|
|
1205
1567
|
await cacheMap.delete(parsedKey);
|
|
1568
|
+
logger5.debug("CACHE_OP: Removed evicted item", { evictedKey });
|
|
1206
1569
|
}
|
|
1207
1570
|
const event = CacheEventFactory.itemRetrieved(ret.key, ret, "api");
|
|
1208
1571
|
context.eventEmitter.emit(event);
|
|
1572
|
+
const totalDuration = Date.now() - startTime;
|
|
1573
|
+
logger5.debug("CACHE_OP: get() completed successfully (cache miss)", {
|
|
1574
|
+
key: keyStr,
|
|
1575
|
+
apiDuration,
|
|
1576
|
+
cacheSetDuration,
|
|
1577
|
+
metadataDuration,
|
|
1578
|
+
evictionDuration,
|
|
1579
|
+
ttlDuration,
|
|
1580
|
+
totalDuration,
|
|
1581
|
+
evictedCount: evictedKeys.length
|
|
1582
|
+
});
|
|
1583
|
+
} else {
|
|
1584
|
+
logger5.debug("CACHE_OP: API returned null", {
|
|
1585
|
+
key: keyStr,
|
|
1586
|
+
apiDuration,
|
|
1587
|
+
totalDuration: Date.now() - startTime
|
|
1588
|
+
});
|
|
1209
1589
|
}
|
|
1210
1590
|
} catch (e) {
|
|
1211
|
-
inFlightRequests.delete(
|
|
1212
|
-
|
|
1591
|
+
inFlightRequests.delete(requestKeyStr);
|
|
1592
|
+
const duration = Date.now() - startTime;
|
|
1593
|
+
logger5.error("CACHE_OP: Error in get() operation", {
|
|
1594
|
+
key: keyStr,
|
|
1595
|
+
duration,
|
|
1596
|
+
message: e.message,
|
|
1597
|
+
stack: e.stack
|
|
1598
|
+
});
|
|
1213
1599
|
throw e;
|
|
1214
1600
|
}
|
|
1215
1601
|
return ret || null;
|
|
@@ -1221,43 +1607,88 @@ import {
|
|
|
1221
1607
|
} from "@fjell/core";
|
|
1222
1608
|
var logger6 = logger_default.get("retrieve");
|
|
1223
1609
|
var retrieve = async (key, context) => {
|
|
1610
|
+
const startTime = Date.now();
|
|
1224
1611
|
const { cacheMap, pkType, statsManager } = context;
|
|
1612
|
+
const keyStr = JSON.stringify(key);
|
|
1225
1613
|
logger6.default("retrieve", { key });
|
|
1614
|
+
logger6.debug("CACHE_OP: retrieve() started", {
|
|
1615
|
+
key: keyStr,
|
|
1616
|
+
cacheType: cacheMap.implementationType,
|
|
1617
|
+
bypassEnabled: !!context.options?.bypassCache
|
|
1618
|
+
});
|
|
1226
1619
|
statsManager.incrementRequests();
|
|
1227
1620
|
if (!isValidItemKey2(key)) {
|
|
1228
|
-
logger6.error("
|
|
1621
|
+
logger6.error("CACHE_OP: Invalid key for retrieve", { key: keyStr });
|
|
1229
1622
|
throw new Error("Key for Retrieve is not a valid ItemKey");
|
|
1230
1623
|
}
|
|
1231
1624
|
if (context.options?.bypassCache) {
|
|
1232
|
-
logger6.debug("Cache bypass enabled, fetching directly from API", { key });
|
|
1625
|
+
logger6.debug("CACHE_OP: Cache bypass enabled, fetching directly from API", { key: keyStr });
|
|
1233
1626
|
statsManager.incrementMisses();
|
|
1234
1627
|
try {
|
|
1628
|
+
const apiStartTime = Date.now();
|
|
1235
1629
|
const { api } = context;
|
|
1236
1630
|
const retrieved2 = await api.get(key);
|
|
1631
|
+
const apiDuration = Date.now() - apiStartTime;
|
|
1237
1632
|
if (retrieved2) {
|
|
1238
|
-
logger6.debug("API response received (
|
|
1633
|
+
logger6.debug("CACHE_OP: API response received (bypass mode)", {
|
|
1634
|
+
key: keyStr,
|
|
1635
|
+
apiDuration,
|
|
1636
|
+
hasValue: true
|
|
1637
|
+
});
|
|
1239
1638
|
return [null, retrieved2];
|
|
1240
1639
|
} else {
|
|
1241
|
-
logger6.debug("API returned null", {
|
|
1640
|
+
logger6.debug("CACHE_OP: API returned null (bypass mode)", {
|
|
1641
|
+
key: keyStr,
|
|
1642
|
+
apiDuration
|
|
1643
|
+
});
|
|
1242
1644
|
return [null, null];
|
|
1243
1645
|
}
|
|
1244
1646
|
} catch (error) {
|
|
1245
|
-
|
|
1647
|
+
const duration = Date.now() - startTime;
|
|
1648
|
+
logger6.error("CACHE_OP: API request failed in bypass mode", {
|
|
1649
|
+
key: keyStr,
|
|
1650
|
+
duration,
|
|
1651
|
+
error
|
|
1652
|
+
});
|
|
1246
1653
|
throw error;
|
|
1247
1654
|
}
|
|
1248
1655
|
}
|
|
1249
1656
|
const containsItemKey = await cacheMap.includesKey(key);
|
|
1657
|
+
logger6.debug("CACHE_OP: Cache key check completed", {
|
|
1658
|
+
key: keyStr,
|
|
1659
|
+
exists: containsItemKey
|
|
1660
|
+
});
|
|
1250
1661
|
let retrieved;
|
|
1251
1662
|
let contextToReturn;
|
|
1252
1663
|
if (containsItemKey) {
|
|
1253
1664
|
logger6.default("Looking for Object in Cache", key);
|
|
1665
|
+
logger6.debug("CACHE_OP: Cache HIT - retrieving from cache", { key: keyStr });
|
|
1666
|
+
const getStartTime = Date.now();
|
|
1254
1667
|
retrieved = await cacheMap.get(key);
|
|
1668
|
+
const getDuration = Date.now() - getStartTime;
|
|
1255
1669
|
contextToReturn = null;
|
|
1256
1670
|
statsManager.incrementHits();
|
|
1671
|
+
const totalDuration = Date.now() - startTime;
|
|
1672
|
+
logger6.debug("CACHE_OP: retrieve() completed (cache hit)", {
|
|
1673
|
+
key: keyStr,
|
|
1674
|
+
getDuration,
|
|
1675
|
+
totalDuration,
|
|
1676
|
+
hasValue: !!retrieved
|
|
1677
|
+
});
|
|
1257
1678
|
} else {
|
|
1258
1679
|
logger6.default("Object Not Found in Cache, Retrieving from Server API", { key });
|
|
1680
|
+
logger6.debug("CACHE_OP: Cache MISS - fetching from API", { key: keyStr });
|
|
1259
1681
|
statsManager.incrementMisses();
|
|
1682
|
+
const apiStartTime = Date.now();
|
|
1260
1683
|
[contextToReturn, retrieved] = await get(key, context);
|
|
1684
|
+
const apiDuration = Date.now() - apiStartTime;
|
|
1685
|
+
const totalDuration = Date.now() - startTime;
|
|
1686
|
+
logger6.debug("CACHE_OP: retrieve() completed (cache miss)", {
|
|
1687
|
+
key: keyStr,
|
|
1688
|
+
apiDuration,
|
|
1689
|
+
totalDuration,
|
|
1690
|
+
hasValue: !!retrieved
|
|
1691
|
+
});
|
|
1261
1692
|
}
|
|
1262
1693
|
const retValue = [
|
|
1263
1694
|
contextToReturn,
|
|
@@ -1752,44 +2183,107 @@ async function executeFindLogic(finder, params, locations, context) {
|
|
|
1752
2183
|
}
|
|
1753
2184
|
}
|
|
1754
2185
|
const queryHash = createFinderHash(finder, params, locations);
|
|
1755
|
-
logger14.debug("Generated query hash for find", {
|
|
2186
|
+
logger14.debug("QUERY_CACHE: Generated query hash for find()", {
|
|
2187
|
+
queryHash,
|
|
2188
|
+
finder,
|
|
2189
|
+
params: JSON.stringify(params),
|
|
2190
|
+
locations: JSON.stringify(locations)
|
|
2191
|
+
});
|
|
2192
|
+
logger14.debug("QUERY_CACHE: Checking query cache for hash", { queryHash });
|
|
1756
2193
|
const cachedItemKeys = await cacheMap.getQueryResult(queryHash);
|
|
1757
2194
|
if (cachedItemKeys) {
|
|
1758
|
-
logger14.debug("
|
|
2195
|
+
logger14.debug("QUERY_CACHE: Cache HIT - Found cached query result", {
|
|
2196
|
+
queryHash,
|
|
2197
|
+
cachedKeyCount: cachedItemKeys.length,
|
|
2198
|
+
itemKeys: cachedItemKeys.map((k) => JSON.stringify(k))
|
|
2199
|
+
});
|
|
1759
2200
|
const cachedItems = [];
|
|
1760
2201
|
let allItemsAvailable = true;
|
|
2202
|
+
const missingKeys = [];
|
|
1761
2203
|
for (const itemKey of cachedItemKeys) {
|
|
1762
2204
|
const item = await cacheMap.get(itemKey);
|
|
1763
2205
|
if (item) {
|
|
1764
2206
|
cachedItems.push(item);
|
|
2207
|
+
logger14.debug("QUERY_CACHE: Retrieved cached item", {
|
|
2208
|
+
itemKey: JSON.stringify(itemKey),
|
|
2209
|
+
itemKeyStr: JSON.stringify(item.key)
|
|
2210
|
+
});
|
|
1765
2211
|
} else {
|
|
1766
2212
|
allItemsAvailable = false;
|
|
2213
|
+
missingKeys.push(itemKey);
|
|
2214
|
+
logger14.debug("QUERY_CACHE: Cached item MISSING from item cache", {
|
|
2215
|
+
itemKey: JSON.stringify(itemKey),
|
|
2216
|
+
queryHash
|
|
2217
|
+
});
|
|
1767
2218
|
break;
|
|
1768
2219
|
}
|
|
1769
2220
|
}
|
|
1770
2221
|
if (allItemsAvailable) {
|
|
2222
|
+
logger14.debug("QUERY_CACHE: All cached items available, returning from cache", {
|
|
2223
|
+
queryHash,
|
|
2224
|
+
itemCount: cachedItems.length
|
|
2225
|
+
});
|
|
1771
2226
|
return cachedItems;
|
|
1772
2227
|
} else {
|
|
1773
|
-
logger14.debug("Some cached items missing, invalidating query cache"
|
|
2228
|
+
logger14.debug("QUERY_CACHE: Some cached items missing, invalidating query cache", {
|
|
2229
|
+
queryHash,
|
|
2230
|
+
missingKeys: missingKeys.map((k) => JSON.stringify(k)),
|
|
2231
|
+
foundCount: cachedItems.length,
|
|
2232
|
+
expectedCount: cachedItemKeys.length
|
|
2233
|
+
});
|
|
1774
2234
|
cacheMap.deleteQueryResult(queryHash);
|
|
1775
2235
|
}
|
|
2236
|
+
} else {
|
|
2237
|
+
logger14.debug("QUERY_CACHE: Cache MISS - No cached query result found", { queryHash });
|
|
1776
2238
|
}
|
|
2239
|
+
logger14.debug("QUERY_CACHE: Fetching from API (cache miss or invalid)", {
|
|
2240
|
+
queryHash,
|
|
2241
|
+
finder,
|
|
2242
|
+
params: JSON.stringify(params),
|
|
2243
|
+
locations: JSON.stringify(locations)
|
|
2244
|
+
});
|
|
1777
2245
|
const ret = await api.find(finder, params, locations);
|
|
2246
|
+
logger14.debug("QUERY_CACHE: API response received", {
|
|
2247
|
+
queryHash,
|
|
2248
|
+
itemCount: ret.length,
|
|
2249
|
+
itemKeys: ret.map((item) => JSON.stringify(item.key))
|
|
2250
|
+
});
|
|
2251
|
+
logger14.debug("QUERY_CACHE: Storing items in item cache", {
|
|
2252
|
+
queryHash,
|
|
2253
|
+
itemCount: ret.length
|
|
2254
|
+
});
|
|
1778
2255
|
for (const v of ret) {
|
|
1779
2256
|
await cacheMap.set(v.key, v);
|
|
2257
|
+
logger14.debug("QUERY_CACHE: Stored item in cache", {
|
|
2258
|
+
itemKey: JSON.stringify(v.key),
|
|
2259
|
+
queryHash
|
|
2260
|
+
});
|
|
1780
2261
|
const keyStr = JSON.stringify(v.key);
|
|
1781
2262
|
ttlManager.onItemAdded(keyStr, cacheMap);
|
|
1782
2263
|
const evictedKeys = await context.evictionManager.onItemAdded(keyStr, v, cacheMap);
|
|
1783
2264
|
for (const evictedKey of evictedKeys) {
|
|
1784
2265
|
const parsedKey = JSON.parse(evictedKey);
|
|
1785
2266
|
await cacheMap.delete(parsedKey);
|
|
2267
|
+
logger14.debug("QUERY_CACHE: Evicted item due to cache limits", {
|
|
2268
|
+
evictedKey,
|
|
2269
|
+
queryHash
|
|
2270
|
+
});
|
|
1786
2271
|
}
|
|
1787
2272
|
}
|
|
1788
2273
|
const itemKeys = ret.map((item) => item.key);
|
|
1789
|
-
cacheMap.setQueryResult(queryHash, itemKeys);
|
|
1790
|
-
logger14.debug("
|
|
2274
|
+
await cacheMap.setQueryResult(queryHash, itemKeys);
|
|
2275
|
+
logger14.debug("QUERY_CACHE: Stored query result in query cache", {
|
|
2276
|
+
queryHash,
|
|
2277
|
+
itemKeyCount: itemKeys.length,
|
|
2278
|
+
itemKeys: itemKeys.map((k) => JSON.stringify(k))
|
|
2279
|
+
});
|
|
1791
2280
|
const event = CacheEventFactory.createQueryEvent(params, locations, ret);
|
|
1792
2281
|
eventEmitter.emit(event);
|
|
2282
|
+
logger14.debug("QUERY_CACHE: Emitted query event", { queryHash });
|
|
2283
|
+
logger14.debug("QUERY_CACHE: find() operation completed", {
|
|
2284
|
+
queryHash,
|
|
2285
|
+
resultCount: ret.length
|
|
2286
|
+
});
|
|
1793
2287
|
return ret;
|
|
1794
2288
|
}
|
|
1795
2289
|
|
|
@@ -1827,34 +2321,86 @@ async function executeFindOneLogic(finder, finderParams, locations, context) {
|
|
|
1827
2321
|
}
|
|
1828
2322
|
}
|
|
1829
2323
|
const queryHash = createFinderHash(finder, finderParams, locations);
|
|
1830
|
-
logger15.debug("Generated query hash for findOne", {
|
|
2324
|
+
logger15.debug("QUERY_CACHE: Generated query hash for findOne()", {
|
|
2325
|
+
queryHash,
|
|
2326
|
+
finder,
|
|
2327
|
+
finderParams: JSON.stringify(finderParams),
|
|
2328
|
+
locations: JSON.stringify(locations)
|
|
2329
|
+
});
|
|
2330
|
+
logger15.debug("QUERY_CACHE: Checking query cache for hash", { queryHash });
|
|
1831
2331
|
const cachedItemKeys = await cacheMap.getQueryResult(queryHash);
|
|
1832
2332
|
if (cachedItemKeys && cachedItemKeys.length > 0) {
|
|
1833
|
-
logger15.debug("
|
|
1834
|
-
|
|
2333
|
+
logger15.debug("QUERY_CACHE: Cache HIT - Found cached query result", {
|
|
2334
|
+
queryHash,
|
|
2335
|
+
cachedKeyCount: cachedItemKeys.length,
|
|
2336
|
+
itemKeys: cachedItemKeys.map((k) => JSON.stringify(k))
|
|
2337
|
+
});
|
|
2338
|
+
const itemKey = cachedItemKeys[0];
|
|
2339
|
+
logger15.debug("QUERY_CACHE: Retrieving first cached item", {
|
|
2340
|
+
queryHash,
|
|
2341
|
+
itemKey: JSON.stringify(itemKey)
|
|
2342
|
+
});
|
|
2343
|
+
const item = await cacheMap.get(itemKey);
|
|
1835
2344
|
if (item) {
|
|
2345
|
+
logger15.debug("QUERY_CACHE: Retrieved cached item successfully", {
|
|
2346
|
+
queryHash,
|
|
2347
|
+
itemKey: JSON.stringify(itemKey),
|
|
2348
|
+
itemKeyStr: JSON.stringify(item.key)
|
|
2349
|
+
});
|
|
1836
2350
|
return item;
|
|
1837
2351
|
} else {
|
|
1838
|
-
logger15.debug("Cached item
|
|
2352
|
+
logger15.debug("QUERY_CACHE: Cached item MISSING from item cache, invalidating query cache", {
|
|
2353
|
+
queryHash,
|
|
2354
|
+
itemKey: JSON.stringify(itemKey)
|
|
2355
|
+
});
|
|
1839
2356
|
cacheMap.deleteQueryResult(queryHash);
|
|
1840
2357
|
}
|
|
2358
|
+
} else {
|
|
2359
|
+
logger15.debug("QUERY_CACHE: Cache MISS - No cached query result found", { queryHash });
|
|
1841
2360
|
}
|
|
2361
|
+
logger15.debug("QUERY_CACHE: Fetching from API (cache miss or invalid)", {
|
|
2362
|
+
queryHash,
|
|
2363
|
+
finder,
|
|
2364
|
+
finderParams: JSON.stringify(finderParams),
|
|
2365
|
+
locations: JSON.stringify(locations)
|
|
2366
|
+
});
|
|
1842
2367
|
const ret = await api.findOne(finder, finderParams, locations);
|
|
1843
2368
|
if (ret === null) {
|
|
2369
|
+
logger15.debug("QUERY_CACHE: API returned null, throwing error", { queryHash, finder });
|
|
1844
2370
|
throw new Error(`findOne returned null for finder: ${finder}`);
|
|
1845
2371
|
}
|
|
1846
|
-
|
|
2372
|
+
logger15.debug("QUERY_CACHE: API response received", {
|
|
2373
|
+
queryHash,
|
|
2374
|
+
itemKey: JSON.stringify(ret.key)
|
|
2375
|
+
});
|
|
2376
|
+
logger15.debug("QUERY_CACHE: Storing item in item cache", {
|
|
2377
|
+
queryHash,
|
|
2378
|
+
itemKey: JSON.stringify(ret.key)
|
|
2379
|
+
});
|
|
2380
|
+
await cacheMap.set(ret.key, ret);
|
|
1847
2381
|
const keyStr = JSON.stringify(ret.key);
|
|
1848
2382
|
ttlManager.onItemAdded(keyStr, cacheMap);
|
|
1849
2383
|
const evictedKeys = await context.evictionManager.onItemAdded(keyStr, ret, cacheMap);
|
|
1850
2384
|
for (const evictedKey of evictedKeys) {
|
|
1851
2385
|
const parsedKey = JSON.parse(evictedKey);
|
|
1852
2386
|
await cacheMap.delete(parsedKey);
|
|
2387
|
+
logger15.debug("QUERY_CACHE: Evicted item due to cache limits", {
|
|
2388
|
+
evictedKey,
|
|
2389
|
+
queryHash
|
|
2390
|
+
});
|
|
1853
2391
|
}
|
|
1854
|
-
cacheMap.setQueryResult(queryHash, [ret.key]);
|
|
1855
|
-
logger15.debug("
|
|
2392
|
+
await cacheMap.setQueryResult(queryHash, [ret.key]);
|
|
2393
|
+
logger15.debug("QUERY_CACHE: Stored query result in query cache", {
|
|
2394
|
+
queryHash,
|
|
2395
|
+
itemKey: JSON.stringify(ret.key)
|
|
2396
|
+
});
|
|
1856
2397
|
const event = CacheEventFactory.createQueryEvent(finderParams, locations, [ret]);
|
|
1857
2398
|
eventEmitter.emit(event);
|
|
2399
|
+
logger15.debug("QUERY_CACHE: Emitted query event", { queryHash });
|
|
2400
|
+
logger15.debug("QUERY_CACHE: findOne() operation completed", {
|
|
2401
|
+
queryHash,
|
|
2402
|
+
itemKey: JSON.stringify(ret.key)
|
|
2403
|
+
});
|
|
1858
2404
|
return ret;
|
|
1859
2405
|
}
|
|
1860
2406
|
|
|
@@ -1912,39 +2458,93 @@ var normalizeKey = (key) => {
|
|
|
1912
2458
|
return key;
|
|
1913
2459
|
};
|
|
1914
2460
|
var set = async (key, v, context) => {
|
|
2461
|
+
const startTime = Date.now();
|
|
1915
2462
|
const { cacheMap, pkType, ttlManager, evictionManager, eventEmitter } = context;
|
|
2463
|
+
const keyStr = JSON.stringify(key);
|
|
1916
2464
|
logger16.default("set", { key, v });
|
|
2465
|
+
logger16.debug("CACHE_OP: set() started", {
|
|
2466
|
+
key: keyStr,
|
|
2467
|
+
cacheType: cacheMap.implementationType
|
|
2468
|
+
});
|
|
1917
2469
|
if (!isValidItemKey6(key)) {
|
|
1918
|
-
logger16.error("
|
|
2470
|
+
logger16.error("CACHE_OP: Invalid key for set", { key: keyStr });
|
|
1919
2471
|
throw new Error("Key for Set is not a valid ItemKey");
|
|
1920
2472
|
}
|
|
1921
2473
|
if (!isItemKeyEqualNormalized(key, v.key)) {
|
|
1922
|
-
logger16.error("Key
|
|
2474
|
+
logger16.error("CACHE_OP: Key mismatch in set", {
|
|
2475
|
+
providedKey: keyStr,
|
|
2476
|
+
itemKey: JSON.stringify(v.key)
|
|
2477
|
+
});
|
|
1923
2478
|
throw new Error("Key does not match item key");
|
|
1924
2479
|
}
|
|
2480
|
+
const checkStartTime = Date.now();
|
|
1925
2481
|
const previousItem = await cacheMap.get(key);
|
|
2482
|
+
const checkDuration = Date.now() - checkStartTime;
|
|
2483
|
+
logger16.debug("CACHE_OP: Previous item check", {
|
|
2484
|
+
key: keyStr,
|
|
2485
|
+
hadPreviousItem: !!previousItem,
|
|
2486
|
+
checkDuration
|
|
2487
|
+
});
|
|
2488
|
+
const setStartTime = Date.now();
|
|
1926
2489
|
await cacheMap.set(key, v);
|
|
1927
|
-
const
|
|
2490
|
+
const setDuration = Date.now() - setStartTime;
|
|
2491
|
+
const metadataStartTime = Date.now();
|
|
1928
2492
|
const metadata = await cacheMap.getMetadata(keyStr);
|
|
1929
2493
|
if (!metadata) {
|
|
1930
2494
|
const now = Date.now();
|
|
2495
|
+
const estimatedSize = estimateValueSize(v);
|
|
1931
2496
|
const baseMetadata = {
|
|
1932
2497
|
key: keyStr,
|
|
1933
2498
|
addedAt: now,
|
|
1934
2499
|
lastAccessedAt: now,
|
|
1935
2500
|
accessCount: 1,
|
|
1936
|
-
estimatedSize
|
|
2501
|
+
estimatedSize
|
|
1937
2502
|
};
|
|
1938
2503
|
await cacheMap.setMetadata(keyStr, baseMetadata);
|
|
2504
|
+
logger16.debug("CACHE_OP: Created base metadata", {
|
|
2505
|
+
key: keyStr,
|
|
2506
|
+
estimatedSize
|
|
2507
|
+
});
|
|
2508
|
+
} else {
|
|
2509
|
+
logger16.debug("CACHE_OP: Metadata already exists", {
|
|
2510
|
+
key: keyStr,
|
|
2511
|
+
addedAt: new Date(metadata.addedAt).toISOString(),
|
|
2512
|
+
accessCount: metadata.accessCount
|
|
2513
|
+
});
|
|
1939
2514
|
}
|
|
2515
|
+
const metadataDuration = Date.now() - metadataStartTime;
|
|
2516
|
+
const ttlStartTime = Date.now();
|
|
1940
2517
|
await ttlManager.onItemAdded(keyStr, cacheMap);
|
|
2518
|
+
const ttlDuration = Date.now() - ttlStartTime;
|
|
2519
|
+
const evictionStartTime = Date.now();
|
|
1941
2520
|
const evictedKeys = await evictionManager.onItemAdded(keyStr, v, cacheMap);
|
|
2521
|
+
const evictionDuration = Date.now() - evictionStartTime;
|
|
2522
|
+
if (evictedKeys.length > 0) {
|
|
2523
|
+
logger16.debug("CACHE_OP: Eviction triggered by set", {
|
|
2524
|
+
key: keyStr,
|
|
2525
|
+
evictedCount: evictedKeys.length,
|
|
2526
|
+
evictedKeys
|
|
2527
|
+
});
|
|
2528
|
+
}
|
|
1942
2529
|
for (const evictedKey of evictedKeys) {
|
|
1943
2530
|
const parsedKey = JSON.parse(evictedKey);
|
|
1944
2531
|
await cacheMap.delete(parsedKey);
|
|
2532
|
+
logger16.debug("CACHE_OP: Removed evicted item", { evictedKey });
|
|
1945
2533
|
}
|
|
1946
2534
|
const event = CacheEventFactory.itemSet(key, v, previousItem);
|
|
1947
2535
|
eventEmitter.emit(event);
|
|
2536
|
+
const totalDuration = Date.now() - startTime;
|
|
2537
|
+
logger16.debug("CACHE_OP: set() completed", {
|
|
2538
|
+
key: keyStr,
|
|
2539
|
+
checkDuration,
|
|
2540
|
+
setDuration,
|
|
2541
|
+
metadataDuration,
|
|
2542
|
+
ttlDuration,
|
|
2543
|
+
evictionDuration,
|
|
2544
|
+
totalDuration,
|
|
2545
|
+
evictedCount: evictedKeys.length,
|
|
2546
|
+
wasUpdate: !!previousItem
|
|
2547
|
+
});
|
|
1948
2548
|
return [context, v];
|
|
1949
2549
|
};
|
|
1950
2550
|
|
|
@@ -5122,9 +5722,17 @@ var EvictionManager = class {
|
|
|
5122
5722
|
return;
|
|
5123
5723
|
}
|
|
5124
5724
|
try {
|
|
5725
|
+
logger24.debug("EVICTION: Item accessed, updating metadata", {
|
|
5726
|
+
key,
|
|
5727
|
+
strategy: this.evictionStrategy.getStrategyName()
|
|
5728
|
+
});
|
|
5125
5729
|
await this.evictionStrategy.onItemAccessed(key, metadataProvider);
|
|
5126
5730
|
} catch (error) {
|
|
5127
|
-
logger24.error("Error in eviction strategy onItemAccessed", {
|
|
5731
|
+
logger24.error("EVICTION: Error in eviction strategy onItemAccessed", {
|
|
5732
|
+
key,
|
|
5733
|
+
error,
|
|
5734
|
+
strategy: this.evictionStrategy?.getStrategyName()
|
|
5735
|
+
});
|
|
5128
5736
|
}
|
|
5129
5737
|
}
|
|
5130
5738
|
/**
|
|
@@ -5135,28 +5743,81 @@ var EvictionManager = class {
|
|
|
5135
5743
|
* @returns Array of keys that were evicted
|
|
5136
5744
|
*/
|
|
5137
5745
|
async onItemAdded(key, value, metadataProvider) {
|
|
5746
|
+
const startTime = Date.now();
|
|
5138
5747
|
const evictedKeys = [];
|
|
5139
5748
|
if (!this.evictionStrategy) {
|
|
5749
|
+
logger24.debug("EVICTION: No eviction strategy configured", { key });
|
|
5140
5750
|
return evictedKeys;
|
|
5141
5751
|
}
|
|
5142
5752
|
try {
|
|
5143
5753
|
const estimatedSize = estimateValueSize(value);
|
|
5754
|
+
logger24.debug("EVICTION: Item addition started", {
|
|
5755
|
+
key,
|
|
5756
|
+
estimatedSize,
|
|
5757
|
+
strategy: this.evictionStrategy.getStrategyName()
|
|
5758
|
+
});
|
|
5759
|
+
const contextStartTime = Date.now();
|
|
5144
5760
|
const context = await this.createEvictionContext(metadataProvider, estimatedSize);
|
|
5761
|
+
const contextDuration = Date.now() - contextStartTime;
|
|
5762
|
+
logger24.debug("EVICTION: Current cache state", {
|
|
5763
|
+
key,
|
|
5764
|
+
currentItemCount: context.currentSize.itemCount,
|
|
5765
|
+
currentSizeBytes: context.currentSize.sizeBytes,
|
|
5766
|
+
maxItems: context.limits.maxItems,
|
|
5767
|
+
maxSizeBytes: context.limits.maxSizeBytes,
|
|
5768
|
+
newItemSize: estimatedSize,
|
|
5769
|
+
contextDuration
|
|
5770
|
+
});
|
|
5771
|
+
const selectionStartTime = Date.now();
|
|
5145
5772
|
const keysToEvict = await this.evictionStrategy.selectForEviction(metadataProvider, context);
|
|
5773
|
+
const selectionDuration = Date.now() - selectionStartTime;
|
|
5774
|
+
if (keysToEvict.length > 0) {
|
|
5775
|
+
logger24.debug("EVICTION: Items selected for eviction", {
|
|
5776
|
+
key,
|
|
5777
|
+
evictCount: keysToEvict.length,
|
|
5778
|
+
keysToEvict,
|
|
5779
|
+
selectionDuration,
|
|
5780
|
+
strategy: this.evictionStrategy.getStrategyName()
|
|
5781
|
+
});
|
|
5782
|
+
}
|
|
5783
|
+
const removalStartTime = Date.now();
|
|
5146
5784
|
for (const evictKey of keysToEvict) {
|
|
5147
5785
|
await this.evictionStrategy.onItemRemoved(evictKey, metadataProvider);
|
|
5148
5786
|
evictedKeys.push(evictKey);
|
|
5787
|
+
logger24.debug("EVICTION: Marked item for eviction", {
|
|
5788
|
+
evictedKey: evictKey,
|
|
5789
|
+
newKey: key
|
|
5790
|
+
});
|
|
5149
5791
|
}
|
|
5792
|
+
const removalDuration = Date.now() - removalStartTime;
|
|
5793
|
+
const addMetadataStart = Date.now();
|
|
5150
5794
|
await this.evictionStrategy.onItemAdded(key, estimatedSize, metadataProvider);
|
|
5795
|
+
const addMetadataDuration = Date.now() - addMetadataStart;
|
|
5796
|
+
const totalDuration = Date.now() - startTime;
|
|
5151
5797
|
if (evictedKeys.length > 0) {
|
|
5152
|
-
logger24.debug("
|
|
5798
|
+
logger24.debug("EVICTION: Eviction completed", {
|
|
5153
5799
|
newKey: key,
|
|
5800
|
+
evictedCount: evictedKeys.length,
|
|
5154
5801
|
evictedKeys,
|
|
5155
|
-
strategy: this.evictionStrategy.getStrategyName()
|
|
5802
|
+
strategy: this.evictionStrategy.getStrategyName(),
|
|
5803
|
+
selectionDuration,
|
|
5804
|
+
removalDuration,
|
|
5805
|
+
addMetadataDuration,
|
|
5806
|
+
totalDuration
|
|
5807
|
+
});
|
|
5808
|
+
} else {
|
|
5809
|
+
logger24.debug("EVICTION: No eviction needed", {
|
|
5810
|
+
newKey: key,
|
|
5811
|
+
estimatedSize,
|
|
5812
|
+
totalDuration
|
|
5156
5813
|
});
|
|
5157
5814
|
}
|
|
5158
5815
|
} catch (error) {
|
|
5159
|
-
logger24.error("Error in eviction strategy onItemAdded", {
|
|
5816
|
+
logger24.error("EVICTION: Error in eviction strategy onItemAdded", {
|
|
5817
|
+
key,
|
|
5818
|
+
error,
|
|
5819
|
+
strategy: this.evictionStrategy?.getStrategyName()
|
|
5820
|
+
});
|
|
5160
5821
|
}
|
|
5161
5822
|
return evictedKeys;
|
|
5162
5823
|
}
|
|
@@ -5181,25 +5842,47 @@ var EvictionManager = class {
|
|
|
5181
5842
|
* @returns Array of keys that were evicted
|
|
5182
5843
|
*/
|
|
5183
5844
|
async performEviction(metadataProvider) {
|
|
5845
|
+
const startTime = Date.now();
|
|
5184
5846
|
const evictedKeys = [];
|
|
5185
5847
|
if (!this.evictionStrategy) {
|
|
5848
|
+
logger24.debug("EVICTION: No eviction strategy configured for manual eviction");
|
|
5186
5849
|
return evictedKeys;
|
|
5187
5850
|
}
|
|
5188
5851
|
try {
|
|
5852
|
+
logger24.debug("EVICTION: Manual eviction started", {
|
|
5853
|
+
strategy: this.evictionStrategy.getStrategyName()
|
|
5854
|
+
});
|
|
5189
5855
|
const context = await this.createEvictionContext(metadataProvider);
|
|
5856
|
+
logger24.debug("EVICTION: Manual eviction - current cache state", {
|
|
5857
|
+
currentItemCount: context.currentSize.itemCount,
|
|
5858
|
+
currentSizeBytes: context.currentSize.sizeBytes,
|
|
5859
|
+
maxItems: context.limits.maxItems,
|
|
5860
|
+
maxSizeBytes: context.limits.maxSizeBytes
|
|
5861
|
+
});
|
|
5190
5862
|
const keysToEvict = await this.evictionStrategy.selectForEviction(metadataProvider, context);
|
|
5191
5863
|
for (const evictKey of keysToEvict) {
|
|
5192
5864
|
await this.evictionStrategy.onItemRemoved(evictKey, metadataProvider);
|
|
5193
5865
|
evictedKeys.push(evictKey);
|
|
5194
5866
|
}
|
|
5867
|
+
const duration = Date.now() - startTime;
|
|
5195
5868
|
if (evictedKeys.length > 0) {
|
|
5196
|
-
logger24.debug("Manual eviction
|
|
5869
|
+
logger24.debug("EVICTION: Manual eviction completed", {
|
|
5870
|
+
evictedCount: evictedKeys.length,
|
|
5197
5871
|
evictedKeys,
|
|
5198
|
-
strategy: this.evictionStrategy.getStrategyName()
|
|
5872
|
+
strategy: this.evictionStrategy.getStrategyName(),
|
|
5873
|
+
duration
|
|
5874
|
+
});
|
|
5875
|
+
} else {
|
|
5876
|
+
logger24.debug("EVICTION: Manual eviction - no items to evict", {
|
|
5877
|
+
strategy: this.evictionStrategy.getStrategyName(),
|
|
5878
|
+
duration
|
|
5199
5879
|
});
|
|
5200
5880
|
}
|
|
5201
5881
|
} catch (error) {
|
|
5202
|
-
logger24.error("Error in manual eviction", {
|
|
5882
|
+
logger24.error("EVICTION: Error in manual eviction", {
|
|
5883
|
+
error,
|
|
5884
|
+
strategy: this.evictionStrategy?.getStrategyName()
|
|
5885
|
+
});
|
|
5203
5886
|
}
|
|
5204
5887
|
return evictedKeys;
|
|
5205
5888
|
}
|
|
@@ -6628,7 +7311,7 @@ var TTLManager = class {
|
|
|
6628
7311
|
metadata
|
|
6629
7312
|
});
|
|
6630
7313
|
if (!metadata) {
|
|
6631
|
-
logger25.
|
|
7314
|
+
logger25.debug("TTL_DEBUG: No metadata found for item when setting TTL", {
|
|
6632
7315
|
key,
|
|
6633
7316
|
metadataProviderType: metadataProvider?.constructor?.name,
|
|
6634
7317
|
metadataProviderMethods: metadataProvider ? Object.getOwnPropertyNames(Object.getPrototypeOf(metadataProvider)) : null
|
|
@@ -6668,12 +7351,28 @@ var TTLManager = class {
|
|
|
6668
7351
|
async isExpired(key, metadataProvider) {
|
|
6669
7352
|
const metadata = await metadataProvider.getMetadata(key);
|
|
6670
7353
|
if (!metadata || !metadata.expiresAt) {
|
|
7354
|
+
logger25.debug("TTL_CHECK: No TTL set for item", { key, hasMetadata: !!metadata });
|
|
6671
7355
|
return false;
|
|
6672
7356
|
}
|
|
6673
7357
|
const now = Date.now();
|
|
6674
7358
|
const expired = now >= metadata.expiresAt;
|
|
7359
|
+
const remainingMs = metadata.expiresAt - now;
|
|
6675
7360
|
if (expired) {
|
|
6676
|
-
logger25.
|
|
7361
|
+
logger25.debug("TTL_CHECK: Item EXPIRED", {
|
|
7362
|
+
key,
|
|
7363
|
+
expiresAt: new Date(metadata.expiresAt).toISOString(),
|
|
7364
|
+
now: new Date(now).toISOString(),
|
|
7365
|
+
expiredByMs: now - metadata.expiresAt,
|
|
7366
|
+
ttl: metadata.ttl
|
|
7367
|
+
});
|
|
7368
|
+
} else {
|
|
7369
|
+
logger25.debug("TTL_CHECK: Item still valid", {
|
|
7370
|
+
key,
|
|
7371
|
+
expiresAt: new Date(metadata.expiresAt).toISOString(),
|
|
7372
|
+
remainingMs,
|
|
7373
|
+
remainingSec: Math.floor(remainingMs / 1e3),
|
|
7374
|
+
ttl: metadata.ttl
|
|
7375
|
+
});
|
|
6677
7376
|
}
|
|
6678
7377
|
return expired;
|
|
6679
7378
|
}
|
|
@@ -6683,9 +7382,22 @@ var TTLManager = class {
|
|
|
6683
7382
|
*/
|
|
6684
7383
|
async validateItem(key, metadataProvider) {
|
|
6685
7384
|
if (!this.config.validateOnAccess) {
|
|
7385
|
+
logger25.debug("TTL_VALIDATE: Validation disabled, skipping check", { key });
|
|
6686
7386
|
return true;
|
|
6687
7387
|
}
|
|
6688
|
-
|
|
7388
|
+
logger25.debug("TTL_VALIDATE: Validating item", {
|
|
7389
|
+
key,
|
|
7390
|
+
ttlEnabled: this.isTTLEnabled(),
|
|
7391
|
+
defaultTTL: this.config.defaultTTL
|
|
7392
|
+
});
|
|
7393
|
+
const isExpired = await this.isExpired(key, metadataProvider);
|
|
7394
|
+
const isValid = !isExpired;
|
|
7395
|
+
logger25.debug("TTL_VALIDATE: Validation result", {
|
|
7396
|
+
key,
|
|
7397
|
+
isValid,
|
|
7398
|
+
isExpired
|
|
7399
|
+
});
|
|
7400
|
+
return isValid;
|
|
6689
7401
|
}
|
|
6690
7402
|
/**
|
|
6691
7403
|
* Get TTL information for an item
|
|
@@ -6710,17 +7422,44 @@ var TTLManager = class {
|
|
|
6710
7422
|
* Find all expired items
|
|
6711
7423
|
*/
|
|
6712
7424
|
async findExpiredItems(metadataProvider) {
|
|
7425
|
+
const startTime = Date.now();
|
|
6713
7426
|
const expiredKeys = [];
|
|
6714
7427
|
const allMetadata = await metadataProvider.getAllMetadata();
|
|
6715
7428
|
const now = Date.now();
|
|
7429
|
+
logger25.debug("TTL_CLEANUP: Scanning for expired items", {
|
|
7430
|
+
totalItems: allMetadata.size,
|
|
7431
|
+
now: new Date(now).toISOString()
|
|
7432
|
+
});
|
|
7433
|
+
let itemsWithTTL = 0;
|
|
6716
7434
|
for (const [key, metadata] of allMetadata) {
|
|
6717
7435
|
const ttlMetadata = metadata;
|
|
6718
|
-
if (ttlMetadata.expiresAt
|
|
6719
|
-
|
|
7436
|
+
if (ttlMetadata.expiresAt) {
|
|
7437
|
+
itemsWithTTL++;
|
|
7438
|
+
if (now >= ttlMetadata.expiresAt) {
|
|
7439
|
+
expiredKeys.push(key);
|
|
7440
|
+
logger25.debug("TTL_CLEANUP: Found expired item", {
|
|
7441
|
+
key,
|
|
7442
|
+
expiresAt: new Date(ttlMetadata.expiresAt).toISOString(),
|
|
7443
|
+
expiredByMs: now - ttlMetadata.expiresAt
|
|
7444
|
+
});
|
|
7445
|
+
}
|
|
6720
7446
|
}
|
|
6721
7447
|
}
|
|
7448
|
+
const duration = Date.now() - startTime;
|
|
6722
7449
|
if (expiredKeys.length > 0) {
|
|
6723
|
-
logger25.debug("
|
|
7450
|
+
logger25.debug("TTL_CLEANUP: Expired items found", {
|
|
7451
|
+
expiredCount: expiredKeys.length,
|
|
7452
|
+
totalItems: allMetadata.size,
|
|
7453
|
+
itemsWithTTL,
|
|
7454
|
+
keys: expiredKeys,
|
|
7455
|
+
duration
|
|
7456
|
+
});
|
|
7457
|
+
} else {
|
|
7458
|
+
logger25.debug("TTL_CLEANUP: No expired items found", {
|
|
7459
|
+
totalItems: allMetadata.size,
|
|
7460
|
+
itemsWithTTL,
|
|
7461
|
+
duration
|
|
7462
|
+
});
|
|
6724
7463
|
}
|
|
6725
7464
|
return expiredKeys;
|
|
6726
7465
|
}
|