Use human-readable memory cache capacity in config

28bad6479cafba8fa1effe94b30528e26b26518b

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

4 files changed, +19 -19Ignore whitespace
default/config.yaml+2 -2
@@ -138,8 +138,8 @@ performance:
138 # Enables lazy loading of character cards. Improves performances with large card libraries.138 # Enables lazy loading of character cards. Improves performances with large card libraries.
139 # May have compatibility issues with some extensions.139 # May have compatibility issues with some extensions.
140 lazyLoadCharacters: false140 lazyLoadCharacters: false
141 # The maximum amount of memory that parsed character cards can use in MB141 # The maximum amount of memory that parsed character cards can use. Set to 0 to disable memory caching.
142 cardsCacheCapacity: 100142 memoryCacheCapacity: '100mb'
143143
144# Allow secret keys exposure via API144# Allow secret keys exposure via API
145allowKeysExposure: false145allowKeysExposure: false
post-install.js+2 -2
@@ -98,8 +98,8 @@ const keyMigrationMap = [
98 },98 },
99 {99 {
100 oldKey: 'cardsCacheCapacity',100 oldKey: 'cardsCacheCapacity',
101 newKey: 'performance.cardsCacheCapacity',101 newKey: 'performance.memoryCacheCapacity',
102 migrate: (value) => value,102 migrate: (value) => `${value}mb`,
103 },103 },
104 // uncomment one release after 1.12.13104 // uncomment one release after 1.12.13
105 /*105 /*
src/endpoints/characters.js+7 -8
@@ -23,10 +23,9 @@ import { invalidateThumbnail } from './thumbnails.js';
23import { importRisuSprites } from './sprites.js';23import { importRisuSprites } from './sprites.js';
24const defaultAvatarPath = './public/img/ai4.png';24const defaultAvatarPath = './public/img/ai4.png';
2525
26// KV-store for parsed character data
27const cacheCapacity = Number(getConfigValue('performance.cardsCacheCapacity', 100, 'number')); // MB
28// With 100 MB limit it would take roughly 3000 characters to reach this limit26// With 100 MB limit it would take roughly 3000 characters to reach this limit
29const characterDataCache = new MemoryLimitedMap(1024 * 1024 * cacheCapacity);27const memoryCacheCapacity = getConfigValue('performance.memoryCacheCapacity', '100mb');
28const memoryCache = new MemoryLimitedMap(memoryCacheCapacity);
30// Some Android devices require tighter memory management29// Some Android devices require tighter memory management
31const isAndroid = process.platform === 'android';30const isAndroid = process.platform === 'android';
32// Use shallow character data for the character list31// Use shallow character data for the character list
@@ -41,12 +40,12 @@ const useShallowCharacters = !!getConfigValue('performance.lazyLoadCharacters',
41async function readCharacterData(inputFile, inputFormat = 'png') {40async function readCharacterData(inputFile, inputFormat = 'png') {
42 const stat = fs.statSync(inputFile);41 const stat = fs.statSync(inputFile);
43 const cacheKey = `${inputFile}-${stat.mtimeMs}`;42 const cacheKey = `${inputFile}-${stat.mtimeMs}`;
44 if (characterDataCache.has(cacheKey)) {43 if (memoryCache.has(cacheKey)) {
45 return characterDataCache.get(cacheKey);44 return memoryCache.get(cacheKey);
46 }45 }
4746
48 const result = parse(inputFile, inputFormat);47 const result = parse(inputFile, inputFormat);
49 !isAndroid && characterDataCache.set(cacheKey, result);48 !isAndroid && memoryCache.set(cacheKey, result);
50 return result;49 return result;
51}50}
5251
@@ -62,12 +61,12 @@ async function readCharacterData(inputFile, inputFormat = 'png') {
62async function writeCharacterData(inputFile, data, outputFile, request, crop = undefined) {61async function writeCharacterData(inputFile, data, outputFile, request, crop = undefined) {
63 try {62 try {
64 // Reset the cache63 // Reset the cache
65 for (const key of characterDataCache.keys()) {64 for (const key of memoryCache.keys()) {
66 if (Buffer.isBuffer(inputFile)) {65 if (Buffer.isBuffer(inputFile)) {
67 break;66 break;
68 }67 }
69 if (key.startsWith(inputFile)) {68 if (key.startsWith(inputFile)) {
70 characterDataCache.delete(key);69 memoryCache.delete(key);
71 break;70 break;
72 }71 }
73 }72 }
src/util.js+8 -7
@@ -16,6 +16,7 @@ import mime from 'mime-types';
16import { default as simpleGit } from 'simple-git';16import { default as simpleGit } from 'simple-git';
17import chalk from 'chalk';17import chalk from 'chalk';
18import { LOG_LEVELS } from './constants.js';18import { LOG_LEVELS } from './constants.js';
19import bytes from 'bytes';
1920
20/**21/**
21 * Parsed config object.22 * Parsed config object.
@@ -856,14 +857,10 @@ export function setupLogLevel() {
856export class MemoryLimitedMap {857export class MemoryLimitedMap {
857 /**858 /**
858 * Creates an instance of MemoryLimitedMap.859 * Creates an instance of MemoryLimitedMap.
859 * @param {number} maxMemoryInBytes - The maximum allowed memory in bytes for string values.860 * @param {string} cacheCapacity - Maximum memory usage in human-readable format (e.g., '1 GB').
860 */861 */
861 constructor(maxMemoryInBytes) {862 constructor(cacheCapacity) {
862 if (typeof maxMemoryInBytes !== 'number' || maxMemoryInBytes <= 0 || isNaN(maxMemoryInBytes)) {863 this.maxMemory = bytes.parse(cacheCapacity) ?? 0;
863 console.warn('Invalid maxMemoryInBytes, using a fallback value of 1 GB.');
864 maxMemoryInBytes = 1024 * 1024 * 1024; // 1 GB
865 }
866 this.maxMemory = maxMemoryInBytes;
867 this.currentMemory = 0;864 this.currentMemory = 0;
868 this.map = new Map();865 this.map = new Map();
869 this.queue = [];866 this.queue = [];
@@ -886,6 +883,10 @@ export class MemoryLimitedMap {
886 * @param {string} value883 * @param {string} value
887 */884 */
888 set(key, value) {885 set(key, value) {
886 if (this.maxMemory <= 0) {
887 return;
888 }
889
889 if (typeof key !== 'string' || typeof value !== 'string') {890 if (typeof key !== 'string' || typeof value !== 'string') {
890 return;891 return;
891 }892 }