| 32 | const useShallowCharacters = !!getConfigValue('performance.lazyLoadCharacters', false, 'boolean'); | 32 | const useShallowCharacters = !!getConfigValue('performance.lazyLoadCharacters', false, 'boolean'); |
| 33 | const useDiskCache = !!getConfigValue('performance.useDiskCache', true, 'boolean'); | 33 | const useDiskCache = !!getConfigValue('performance.useDiskCache', true, 'boolean'); |
| 34 | | 34 | |
| 35 | const diskCache = { | 35 | class DiskCache { |
| 36 | /** | 36 | /** @type {string} */ |
| 37 | * @type {import('node-persist').LocalStorage?} | 37 | static DIRECTORY = 'characters'; |
| 38 | * @private | 38 | |
| 39 | */ | 39 | /** @type {number} */ |
| 40 | _instance: null, | 40 | static REMOVAL_INTERVAL = 60000; |
| | 41 | |
| | 42 | /** @type {import('node-persist').LocalStorage} */ |
| | 43 | #instance; |
| | 44 | |
| | 45 | /** @type {NodeJS.Timeout} */ |
| | 46 | #removalInterval; |
| | 47 | |
| 41 | /** | 48 | /** |
| 42 | * @type {NodeJS.Timeout?} | 49 | * Queue for removal of cache entries. |
| 43 | * @private | 50 | * @type {Set<string>} |
| 44 | */ | 51 | */ |
| 45 | _removalInterval: null, | 52 | removalQueue = new Set(); |
| | 53 | |
| 46 | /** | 54 | /** |
| 47 | * Processes the removal queue. | 55 | * Processes the removal queue. |
| 48 | * @returns {Promise<void>} | 56 | * @returns {Promise<void>} |
| 49 | * @private | | |
| 50 | */ | 57 | */ |
| 51 | _removeCacheEntries: async function() { | 58 | async #removeCacheEntries() { |
| 52 | try { | 59 | try { |
| 53 | if (!useDiskCache || this.removalQueue.size === 0) { | 60 | if (!useDiskCache || this.removalQueue.size === 0) { |
| 54 | return; | 61 | return; |
| 55 | } | 62 | } |
| 56 | | 63 | |
| 57 | const keys = await diskCache.instance().then(i => i.keys()); | 64 | const keys = await this.instance().then(i => i.keys()); |
| 58 | for (const item of this.removalQueue) { | 65 | for (const item of this.removalQueue) { |
| 59 | const key = keys.find(k => k.startsWith(item)); | 66 | const key = keys.find(k => k.startsWith(item)); |
| 60 | if (key) { | 67 | if (key) { |
| 61 | await diskCache.instance().then(i => i.removeItem(key)); | 68 | await this.instance().then(i => i.removeItem(key)); |
| 62 | } | 69 | } |
| 63 | } | 70 | } |
| 64 | this.removalQueue.clear(); | 71 | this.removalQueue.clear(); |
| 65 | } catch (error) { | 72 | } catch (error) { |
| 66 | console.error('Error while removing cache entries:', error); | 73 | console.error('Error while removing cache entries:', error); |
| 67 | } | 74 | } |
| 68 | }, | 75 | }; |
| | 76 | |
| 69 | /** | 77 | /** |
| 70 | * Gets the disk cache instance. | 78 | * Gets the disk cache instance. |
| 71 | * @returns {Promise<import('node-persist').LocalStorage>} | 79 | * @returns {Promise<import('node-persist').LocalStorage>} |
| 72 | */ | 80 | */ |
| 73 | instance: async function() { | 81 | async instance() { |
| 74 | if (this._instance) { | 82 | if (this.#instance) { |
| 75 | return this._instance; | 83 | return this.#instance; |
| 76 | } | 84 | } |
| 77 | | 85 | |
| 78 | const cacheDir = path.join(globalThis.DATA_ROOT, '_cache', 'characters'); | 86 | const cacheDir = path.join(globalThis.DATA_ROOT, '_cache', DiskCache.DIRECTORY); |
| 79 | this._instance = storage.create({ dir: cacheDir, ttl: false }); | 87 | this.#instance = storage.create({ dir: cacheDir, ttl: false }); |
| 80 | await this._instance.init(); | 88 | await this.#instance.init(); |
| 81 | this._removalInterval = setInterval(this._removeCacheEntries.bind(this), 60000); | 89 | this.#removalInterval = setInterval(this.#removeCacheEntries.bind(this), DiskCache.REMOVAL_INTERVAL); |
| 82 | return this._instance; | 90 | return this.#instance; |
| 83 | }, | 91 | } |
| 84 | /** | | |
| 85 | * Queue for removal of cache entries. | | |
| 86 | * @type {Set<string>} | | |
| 87 | */ | | |
| 88 | removalQueue: new Set(), | | |
| 89 | }; | | |
| 90 | | 92 | |
| 91 | /** | 93 | /** |
| 92 | * Verifies disk cache size and prunes it if necessary. | 94 | * Verifies disk cache size and prunes it if necessary. |
| 93 | * @param {import('../users.js').UserDirectoryList[]} directoriesList List of user directories | 95 | * @param {import('../users.js').UserDirectoryList[]} directoriesList List of user directories |
| 94 | * @returns {Promise<void>} | 96 | * @returns {Promise<void>} |
| 95 | */ | 97 | */ |
| 96 | export async function verifyCharactersDiskCache(directoriesList) { | 98 | async verify(directoriesList) { |
| 97 | if (!useDiskCache) { | 99 | if (!useDiskCache) { |
| 98 | return; | 100 | return; |
| 99 | } | 101 | } |
| 100 | | 102 | |
| 101 | const cache = await diskCache.instance(); | 103 | const cache = await this.instance(); |
| 102 | const validKeys = []; | 104 | const validKeys = []; |
| 103 | for (const dir of directoriesList) { | 105 | for (const dir of directoriesList) { |
| 104 | const files = fs.readdirSync(dir.characters); | 106 | const files = fs.readdirSync(dir.characters); |