Implement disk caching for character data using node-persist
| @@ -11,6 +11,7 @@ import yaml from 'yaml'; | ||
| 11 | 11 | import _ from 'lodash'; |
| 12 | 12 | import mime from 'mime-types'; |
| 13 | 13 | import jimp from 'jimp'; |
| 14 | +import storage from 'node-persist'; | |
| 14 | 15 | |
| 15 | 16 | import { AVATAR_WIDTH, AVATAR_HEIGHT } from '../constants.js'; |
| 16 | 17 | import { jsonParser, urlencodedParser } from '../express-common.js'; |
| @@ -30,6 +31,29 @@ const memoryCache = new MemoryLimitedMap(memoryCacheCapacity); | ||
| 30 | 31 | const isAndroid = process.platform === 'android'; |
| 31 | 32 | // Use shallow character data for the character list |
| 32 | 33 | const useShallowCharacters = !!getConfigValue('performance.lazyLoadCharacters', false, 'boolean'); |
| 34 | +const useDiskCache = true; // !!getConfigValue('performance.useDiskCache', false, 'boolean'); | |
| 35 | + | |
| 36 | +const diskCache = { | |
| 37 | + /** | |
| 38 | + * @type {import('node-persist').LocalStorage?} | |
| 39 | + * @private | |
| 40 | + */ | |
| 41 | + _instance: null, | |
| 42 | + /** | |
| 43 | + * Gets the disk cache instance. | |
| 44 | + * @returns {Promise<import('node-persist').LocalStorage>} | |
| 45 | + */ | |
| 46 | + instance: async function() { | |
| 47 | + if (this._instance) { | |
| 48 | + return this._instance; | |
| 49 | + } | |
| 50 | + | |
| 51 | + const cacheDir = path.join(globalThis.DATA_ROOT, '_cache', 'characters'); | |
| 52 | + this._instance = storage.create({ dir: cacheDir, ttl: true }); | |
| 53 | + await this._instance.init(); | |
| 54 | + return this._instance; | |
| 55 | + }, | |
| 56 | +}; | |
| 33 | 57 | |
| 34 | 58 | /** |
| 35 | 59 | * Reads the character card from the specified image file. |
| @@ -43,9 +67,18 @@ async function readCharacterData(inputFile, inputFormat = 'png') { | ||
| 43 | 67 | if (memoryCache.has(cacheKey)) { |
| 44 | 68 | return memoryCache.get(cacheKey); |
| 45 | 69 | } |
| 70 | + if (useDiskCache) { | |
| 71 | + const cachedData = await diskCache.instance().then(i => i.getItem(cacheKey)); | |
| 72 | + if (cachedData) { | |
| 73 | + return cachedData; | |
| 74 | + } | |
| 75 | + } | |
| 46 | 76 | |
| 47 | 77 | const result = parse(inputFile, inputFormat); |
| 48 | 78 | !isAndroid && memoryCache.set(cacheKey, result); |
| 79 | + if (useDiskCache) { | |
| 80 | + await diskCache.instance().then(i => i.setItem(cacheKey, result)); | |
| 81 | + } | |
| 49 | 82 | return result; |
| 50 | 83 | } |
| 51 | 84 | |
| @@ -70,6 +103,15 @@ async function writeCharacterData(inputFile, data, outputFile, request, crop = u | ||
| 70 | 103 | break; |
| 71 | 104 | } |
| 72 | 105 | } |
| 106 | + if (useDiskCache && !Buffer.isBuffer(inputFile)) { | |
| 107 | + const keys = await diskCache.instance().then(i => i.keys()); | |
| 108 | + for (const key of keys) { | |
| 109 | + if (key.startsWith(inputFile)) { | |
| 110 | + await diskCache.instance().then(i => i.removeItem(key)); | |
| 111 | + break; | |
| 112 | + } | |
| 113 | + } | |
| 114 | + } | |
| 73 | 115 | |
| 74 | 116 | /** |
| 75 | 117 | * Read the image, resize, and save it as a PNG into the buffer. |