Implement disk caching for character data using node-persist
| @@ -11,6 +11,7 @@ import yaml from 'yaml'; | |||
| 11 | import _ from 'lodash'; | 11 | import _ from 'lodash'; |
| 12 | import mime from 'mime-types'; | 12 | import mime from 'mime-types'; |
| 13 | import jimp from 'jimp'; | 13 | import jimp from 'jimp'; |
| 14 | import storage from 'node-persist'; | ||
| 14 | 15 | ||
| 15 | import { AVATAR_WIDTH, AVATAR_HEIGHT } from '../constants.js'; | 16 | import { AVATAR_WIDTH, AVATAR_HEIGHT } from '../constants.js'; |
| 16 | import { jsonParser, urlencodedParser } from '../express-common.js'; | 17 | import { jsonParser, urlencodedParser } from '../express-common.js'; |
| @@ -30,6 +31,29 @@ const memoryCache = new MemoryLimitedMap(memoryCacheCapacity); | |||
| 30 | const isAndroid = process.platform === 'android'; | 31 | const isAndroid = process.platform === 'android'; |
| 31 | // Use shallow character data for the character list | 32 | // Use shallow character data for the character list |
| 32 | const useShallowCharacters = !!getConfigValue('performance.lazyLoadCharacters', false, 'boolean'); | 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 | * Reads the character card from the specified image file. | 59 | * Reads the character card from the specified image file. |
| @@ -43,9 +67,18 @@ async function readCharacterData(inputFile, inputFormat = 'png') { | |||
| 43 | if (memoryCache.has(cacheKey)) { | 67 | if (memoryCache.has(cacheKey)) { |
| 44 | return memoryCache.get(cacheKey); | 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 | const result = parse(inputFile, inputFormat); | 77 | const result = parse(inputFile, inputFormat); |
| 48 | !isAndroid && memoryCache.set(cacheKey, result); | 78 | !isAndroid && memoryCache.set(cacheKey, result); |
| 79 | if (useDiskCache) { | ||
| 80 | await diskCache.instance().then(i => i.setItem(cacheKey, result)); | ||
| 81 | } | ||
| 49 | return result; | 82 | return result; |
| 50 | } | 83 | } |
| 51 | 84 | ||
| @@ -70,6 +103,15 @@ async function writeCharacterData(inputFile, data, outputFile, request, crop = u | |||
| 70 | break; | 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 | * Read the image, resize, and save it as a PNG into the buffer. | 117 | * Read the image, resize, and save it as a PNG into the buffer. |