Skip to content

Commit ce86f16

Browse files
committed
reset
1 parent 2c34ba5 commit ce86f16

2 files changed

Lines changed: 141 additions & 8 deletions

File tree

cache/index.js

Lines changed: 131 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,11 @@ class ClusterCache {
4141
this.keySizes = new Map() // Track size of each cached value in bytes
4242
this.totalBytes = 0 // Track total cache size in bytes
4343
this.localCache = new Map()
44+
this.clearGeneration = 0 // Track clear operations to coordinate across workers
4445

4546
// Background stats sync every 5 seconds
4647
this.statsInterval = setInterval(() => {
48+
this._checkClearSignal().catch(() => {})
4749
this._syncStats().catch(() => {})
4850
}, 5000)
4951
}
@@ -216,43 +218,89 @@ class ClusterCache {
216218
/**
217219
* Clear all cache entries and reset stats
218220
*/
221+
/**
222+
* Clear all cache entries and reset stats across all workers
223+
*/
219224
async clear() {
220225
try {
221226
clearInterval(this.statsInterval)
222227

228+
// Increment clear generation to signal all workers
229+
this.clearGeneration++
230+
231+
// Broadcast clear signal to all workers via cluster cache
232+
await this.clusterCache.set('_clear_signal', {
233+
generation: this.clearGeneration,
234+
timestamp: Date.now()
235+
}, 60000) // 1 minute TTL
236+
237+
// Flush all cache data
223238
await this.clusterCache.flush()
239+
240+
// Reset local state
224241
this.allKeys.clear()
225-
this.keyAccessTimes.clear() // Clear access time tracking
226-
this.keySizes.clear() // Clear size tracking
242+
this.keyAccessTimes.clear()
243+
this.keySizes.clear()
227244
this.totalBytes = 0
228245
this.localCache.clear()
229246

230247
this.stats = {
231248
hits: 0,
232249
misses: 0,
233-
evictions: 1,
250+
evictions: 0,
234251
sets: 0,
235252
invalidations: 0
236253
}
237254

238-
await new Promise(resolve => setTimeout(resolve, 100))
239-
255+
// Restart stats sync interval
240256
this.statsInterval = setInterval(() => {
257+
this._checkClearSignal().catch(() => {})
241258
this._syncStats().catch(() => {})
242259
}, 5000)
260+
261+
// Immediately sync our fresh stats
262+
await this._syncStats()
263+
264+
// Wait for all workers to see the clear signal and reset
265+
// Workers check every 5 seconds, so wait 6 seconds to be safe
266+
await new Promise(resolve => setTimeout(resolve, 6000))
267+
268+
// Delete all old worker stats keys
269+
const keysMap = await this.clusterCache.keys()
270+
const deletePromises = []
271+
for (const instanceKeys of Object.values(keysMap)) {
272+
if (Array.isArray(instanceKeys)) {
273+
for (const key of instanceKeys) {
274+
if (key.startsWith('_stats_worker_')) {
275+
deletePromises.push(this.clusterCache.delete(key))
276+
}
277+
}
278+
}
279+
}
280+
await Promise.all(deletePromises)
281+
282+
// Final sync after cleanup
283+
await this._syncStats()
243284
} catch (err) {
244285
console.error('Cache clear error:', err)
245286
this.localCache.clear()
246287
this.allKeys.clear()
247-
this.keyAccessTimes.clear() // Clear access time tracking
248-
this.keySizes.clear() // Clear size tracking
288+
this.keyAccessTimes.clear()
289+
this.keySizes.clear()
249290
this.totalBytes = 0
250-
this.stats.evictions++
291+
this.stats = {
292+
hits: 0,
293+
misses: 0,
294+
evictions: 0,
295+
sets: 0,
296+
invalidations: 0
297+
}
251298

252299
if (!this.statsInterval._destroyed) {
253300
clearInterval(this.statsInterval)
254301
}
255302
this.statsInterval = setInterval(() => {
303+
this._checkClearSignal().catch(() => {})
256304
this._syncStats().catch(() => {})
257305
}, 5000)
258306
}
@@ -427,6 +475,81 @@ class ClusterCache {
427475
}
428476
}
429477

478+
/**
479+
* Get detailed list of all cache entries
480+
* @returns {Promise<Array>} Array of cache entry details
481+
*/
482+
async getDetails() {
483+
try {
484+
const keysMap = await this.clusterCache.keys()
485+
const allKeys = new Set()
486+
487+
for (const instanceKeys of Object.values(keysMap)) {
488+
if (Array.isArray(instanceKeys)) {
489+
instanceKeys.forEach(key => {
490+
if (!key.startsWith('_stats_worker_') && !key.startsWith('_clear_signal')) {
491+
allKeys.add(key)
492+
}
493+
})
494+
}
495+
}
496+
497+
const details = []
498+
let position = 0
499+
for (const key of allKeys) {
500+
const value = await this.clusterCache.get(key, undefined)
501+
const size = this._calculateSize(value)
502+
503+
details.push({
504+
position,
505+
key,
506+
bytes: size
507+
})
508+
position++
509+
}
510+
511+
return details
512+
} catch (err) {
513+
console.error('Cache getDetails error:', err)
514+
return []
515+
}
516+
}
517+
518+
/**
519+
* Check for clear signal from other workers
520+
* @private
521+
*/
522+
async _checkClearSignal() {
523+
try {
524+
const signal = await this.clusterCache.get('_clear_signal', undefined)
525+
if (signal && signal.generation > this.clearGeneration) {
526+
// Another worker initiated a clear - reset our local state
527+
this.clearGeneration = signal.generation
528+
529+
this.allKeys.clear()
530+
this.keyAccessTimes.clear()
531+
this.keySizes.clear()
532+
this.totalBytes = 0
533+
this.localCache.clear()
534+
535+
this.stats = {
536+
hits: 0,
537+
misses: 0,
538+
evictions: 0,
539+
sets: 0,
540+
invalidations: 0
541+
}
542+
543+
// Delete our worker stats key immediately
544+
const workerId = process.env.pm_id || process.pid
545+
const statsKey = `_stats_worker_${workerId}`
546+
await this.clusterCache.delete(statsKey)
547+
}
548+
} catch (err) {
549+
// Silently fail
550+
}
551+
}
552+
430553
/**
431554
* Sync current worker stats to cluster cache (called by background interval)
432555
* @private

cache/middleware.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,17 @@ const invalidateCache = (req, res, next) => {
383383
* Expose cache statistics at /cache/stats endpoint
384384
*/
385385
const cacheStats = async (req, res) => {
386+
const includeDetails = req.query.details === 'true'
386387
const stats = await cache.getStats()
388+
389+
if (includeDetails) {
390+
try {
391+
stats.details = await cache.getDetails()
392+
} catch (err) {
393+
stats.detailsError = err.message
394+
}
395+
}
396+
387397
res.status(200).json(stats)
388398
}
389399

0 commit comments

Comments
 (0)