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',
33const useDiskCache = !!getConfigValue('performance.useDiskCache', true, 'boolean');33const useDiskCache = !!getConfigValue('performance.useDiskCache', true, 'boolean');
3434
35class DiskCache {35class 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
36 /** @type {string} */42 /** @type {string} */
37 static DIRECTORY = 'characters';43 static DIRECTORY = 'characters';
3844
@@ -47,9 +53,9 @@ class DiskCache {
4753
48 /**54 /**
49 * Queue for removal of cache entries.55 * Queue for removal of cache entries.
50 * @type {Set<string>}56 * @type {CacheRemovalQueueItem[]}
51 */57 */
52 removalQueue = new Set();58 removalQueue = [];
5359
54 /**60 /**
55 * Processes the removal queue.61 * Processes the removal queue.
@@ -57,22 +63,43 @@ class DiskCache {
57 */63 */
58 async #removeCacheEntries() {64 async #removeCacheEntries() {
59 try {65 try {
60 if (!useDiskCache || this.removalQueue.size === 0) {66 if (!useDiskCache || this.removalQueue.length === 0) {
61 return;67 return;
62 }68 }
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);
69 }97 }
70 }98 }
71 this.removalQueue.clear();
72 } catch (error) {99 } catch (error) {
73 console.error('Error while removing cache entries:', error);100 console.error('Error while removing cache entries:', error);
74 }101 }
75 };102 }
76103
77 /**104 /**
78 * Gets the disk cache instance.105 * Gets the disk cache instance.
@@ -176,7 +203,7 @@ async function writeCharacterData(inputFile, data, outputFile, request, crop = u
176 }203 }
177 }204 }
178 if (useDiskCache && !Buffer.isBuffer(inputFile)) {205 if (useDiskCache && !Buffer.isBuffer(inputFile)) {
179 diskCache.removalQueue.add(inputFile);206 diskCache.removalQueue.push({ item: inputFile, timestamp: Date.now() });
180 }207 }
181 /**208 /**
182 * Read the image, resize, and save it as a PNG into the buffer.209 * Read the image, resize, and save it as a PNG into the buffer.