Fix cache removal queue processing

8e66ab4d5122ff3cb0b438fb31694e0c2a9e34ef

Cohee <18619528+Cohee1207@users.noreply.github.com>

1 files changed, +38 -11Ignore whitespace
src/endpoints/characters.js+38 -11
@@ -33,6 +33,12 @@ const useShallowCharacters = !!getConfigValue('performance.lazyLoadCharacters',
3333const useDiskCache = !!getConfigValue('performance.useDiskCache', true, 'boolean');
3434
3535class DiskCache {
36+ /**
37+ * @typedef {object} CacheRemovalQueueItem
38+ * @property {string} item Path to the character file
39+ * @property {number} timestamp Timestamp of the last access
40+ */
41+
3642 /** @type {string} */
3743 static DIRECTORY = 'characters';
3844
@@ -47,9 +53,9 @@ class DiskCache {
4753
4854 /**
4955 * Queue for removal of cache entries.
5056 * @type {Set<string>CacheRemovalQueueItem[]}
5157 */
5258 removalQueue = new Set()[];
5359
5460 /**
5561 * Processes the removal queue.
@@ -57,22 +63,43 @@ class DiskCache {
5763 */
5864 async #removeCacheEntries() {
5965 try {
6066 if (!useDiskCache || this.removalQueue.sizelength === 0) {
6167 return;
6268 }
6369
64- const keys = await this.instance().then(i => i.keys());
70+ /** @type {Map<string, number>} */
65- for (const item of this.removalQueue) {
71+ const latestTimestamps = new Map();
66- const key = keys.find(k => k.startsWith(item));
72+ for (const { item, timestamp } of this.removalQueue) {
67- if (key) {
73+ if (!latestTimestamps.has(item) || timestamp > (latestTimestamps.get(item) ?? 0)) {
68- await this.instance().then(i => i.removeItem(key));
74+ latestTimestamps.set(item, timestamp);
75+ }
76+ }
77+ this.removalQueue.length = 0;
78+
79+ const cache = await this.instance();
80+ const keys = await cache.keys();
81+
82+ for (const [item, timestamp] of latestTimestamps.entries()) {
83+ const itemKeys = keys.filter(k => k.startsWith(item));
84+ if (!itemKeys.length) {
85+ continue;
86+ }
87+ for (const key of itemKeys) {
88+ const datumPath = cache.getDatumPath(key);
89+ if (!fs.existsSync(datumPath)) {
90+ continue;
91+ }
92+ const stat = fs.statSync(datumPath);
93+ if (stat.mtimeMs > timestamp) {
94+ continue;
95+ }
96+ await cache.removeItem(key);
6997 }
7098 }
71- this.removalQueue.clear();
7299 } catch (error) {
73100 console.error('Error while removing cache entries:', error);
74101 }
75102 };
76103
77104 /**
78105 * Gets the disk cache instance.
@@ -176,7 +203,7 @@ async function writeCharacterData(inputFile, data, outputFile, request, crop = u
176203 }
177204 }
178205 if (useDiskCache && !Buffer.isBuffer(inputFile)) {
179- diskCache.removalQueue.add(inputFile);
206+ diskCache.removalQueue.push({ item: inputFile, timestamp: Date.now() });
180207 }
181208 /**
182209 * Read the image, resize, and save it as a PNG into the buffer.