Use human-readable memory cache capacity in config

This commit is contained in:
Cohee
2025-03-05 00:45:34 +02:00
parent 13cfe1650f
commit 28bad6479c
4 changed files with 19 additions and 19 deletions

View File

@@ -138,8 +138,8 @@ performance:
# Enables lazy loading of character cards. Improves performances with large card libraries. # Enables lazy loading of character cards. Improves performances with large card libraries.
# May have compatibility issues with some extensions. # May have compatibility issues with some extensions.
lazyLoadCharacters: false lazyLoadCharacters: false
# The maximum amount of memory that parsed character cards can use in MB # The maximum amount of memory that parsed character cards can use. Set to 0 to disable memory caching.
cardsCacheCapacity: 100 memoryCacheCapacity: '100mb'
# Allow secret keys exposure via API # Allow secret keys exposure via API
allowKeysExposure: false allowKeysExposure: false

View File

@@ -98,8 +98,8 @@ const keyMigrationMap = [
}, },
{ {
oldKey: 'cardsCacheCapacity', oldKey: 'cardsCacheCapacity',
newKey: 'performance.cardsCacheCapacity', newKey: 'performance.memoryCacheCapacity',
migrate: (value) => value, migrate: (value) => `${value}mb`,
}, },
// uncomment one release after 1.12.13 // uncomment one release after 1.12.13
/* /*

View File

@@ -23,10 +23,9 @@ import { invalidateThumbnail } from './thumbnails.js';
import { importRisuSprites } from './sprites.js'; import { importRisuSprites } from './sprites.js';
const defaultAvatarPath = './public/img/ai4.png'; const defaultAvatarPath = './public/img/ai4.png';
// KV-store for parsed character data
const cacheCapacity = Number(getConfigValue('performance.cardsCacheCapacity', 100, 'number')); // MB
// With 100 MB limit it would take roughly 3000 characters to reach this limit // With 100 MB limit it would take roughly 3000 characters to reach this limit
const characterDataCache = new MemoryLimitedMap(1024 * 1024 * cacheCapacity); const memoryCacheCapacity = getConfigValue('performance.memoryCacheCapacity', '100mb');
const memoryCache = new MemoryLimitedMap(memoryCacheCapacity);
// Some Android devices require tighter memory management // Some Android devices require tighter memory management
const isAndroid = process.platform === 'android'; const isAndroid = process.platform === 'android';
// Use shallow character data for the character list // Use shallow character data for the character list
@@ -41,12 +40,12 @@ const useShallowCharacters = !!getConfigValue('performance.lazyLoadCharacters',
async function readCharacterData(inputFile, inputFormat = 'png') { async function readCharacterData(inputFile, inputFormat = 'png') {
const stat = fs.statSync(inputFile); const stat = fs.statSync(inputFile);
const cacheKey = `${inputFile}-${stat.mtimeMs}`; const cacheKey = `${inputFile}-${stat.mtimeMs}`;
if (characterDataCache.has(cacheKey)) { if (memoryCache.has(cacheKey)) {
return characterDataCache.get(cacheKey); return memoryCache.get(cacheKey);
} }
const result = parse(inputFile, inputFormat); const result = parse(inputFile, inputFormat);
!isAndroid && characterDataCache.set(cacheKey, result); !isAndroid && memoryCache.set(cacheKey, result);
return result; return result;
} }
@@ -62,12 +61,12 @@ async function readCharacterData(inputFile, inputFormat = 'png') {
async function writeCharacterData(inputFile, data, outputFile, request, crop = undefined) { async function writeCharacterData(inputFile, data, outputFile, request, crop = undefined) {
try { try {
// Reset the cache // Reset the cache
for (const key of characterDataCache.keys()) { for (const key of memoryCache.keys()) {
if (Buffer.isBuffer(inputFile)) { if (Buffer.isBuffer(inputFile)) {
break; break;
} }
if (key.startsWith(inputFile)) { if (key.startsWith(inputFile)) {
characterDataCache.delete(key); memoryCache.delete(key);
break; break;
} }
} }

View File

@@ -16,6 +16,7 @@ import mime from 'mime-types';
import { default as simpleGit } from 'simple-git'; import { default as simpleGit } from 'simple-git';
import chalk from 'chalk'; import chalk from 'chalk';
import { LOG_LEVELS } from './constants.js'; import { LOG_LEVELS } from './constants.js';
import bytes from 'bytes';
/** /**
* Parsed config object. * Parsed config object.
@@ -856,14 +857,10 @@ export function setupLogLevel() {
export class MemoryLimitedMap { export class MemoryLimitedMap {
/** /**
* Creates an instance of MemoryLimitedMap. * Creates an instance of MemoryLimitedMap.
* @param {number} maxMemoryInBytes - The maximum allowed memory in bytes for string values. * @param {string} cacheCapacity - Maximum memory usage in human-readable format (e.g., '1 GB').
*/ */
constructor(maxMemoryInBytes) { constructor(cacheCapacity) {
if (typeof maxMemoryInBytes !== 'number' || maxMemoryInBytes <= 0 || isNaN(maxMemoryInBytes)) { this.maxMemory = bytes.parse(cacheCapacity) ?? 0;
console.warn('Invalid maxMemoryInBytes, using a fallback value of 1 GB.');
maxMemoryInBytes = 1024 * 1024 * 1024; // 1 GB
}
this.maxMemory = maxMemoryInBytes;
this.currentMemory = 0; this.currentMemory = 0;
this.map = new Map(); this.map = new Map();
this.queue = []; this.queue = [];
@@ -886,6 +883,10 @@ export class MemoryLimitedMap {
* @param {string} value * @param {string} value
*/ */
set(key, value) { set(key, value) {
if (this.maxMemory <= 0) {
return;
}
if (typeof key !== 'string' || typeof value !== 'string') { if (typeof key !== 'string' || typeof value !== 'string') {
return; return;
} }