8337a716b1
* fix(file-watcher): bound concurrent photo processing chokidar fires 'add' once per file — with no ignoreInitial option the boot scan fires it for every existing file, and a bulk drop into the watch folder fires it for every new one at once. Each handler runs DB lookups plus (for new files) a full sharp pipeline; sharp.concurrency(2) only caps libvips threads WITHIN one operation, not the number of parallel pipelines, so unbounded handlers can OOM small hosts. Gate both 'add' and 'unlink' through a shared p-limit (FILE_WATCHER_CONCURRENCY, default 2, floor 1) — mass deletes otherwise burst DB work and ZIP-cache invalidation the same way. p-limit is pinned to ^3.1.0, the last CommonJS release. Adapted from the filpgame fork (426ca491) — thanks @filpgame; extended to cover 'unlink', documented in .env.example, plus a lock-in test for the existing Sharp cache/concurrency caps this bound relies on. * chore(compose): pass FILE_WATCHER_CONCURRENCY into the backend container (codex review of #846) The backend service uses an explicit environment list (no env_file), so the documented override never reached the container in the default compose deployments. Added to both compose files + root .env.example.
31 lines
913 B
JavaScript
31 lines
913 B
JavaScript
/**
|
|
* Locks the process-wide Sharp memory guards. The file-watcher concurrency
|
|
* bound (FILE_WATCHER_CONCURRENCY) assumes these caps stay in place — they
|
|
* limit libvips threads/cache WITHIN one operation while p-limit bounds the
|
|
* number of parallel pipelines. From the filpgame fork (426ca491).
|
|
*/
|
|
|
|
const mockSharp = jest.fn();
|
|
mockSharp.cache = jest.fn();
|
|
mockSharp.concurrency = jest.fn();
|
|
|
|
jest.mock('sharp', () => mockSharp);
|
|
|
|
jest.mock('../../src/utils/logger', () => ({
|
|
error: jest.fn(),
|
|
warn: jest.fn(),
|
|
info: jest.fn(),
|
|
debug: jest.fn(),
|
|
}));
|
|
|
|
describe('imageProcessor Sharp configuration', () => {
|
|
it('disables the Sharp cache and caps libvips concurrency', () => {
|
|
jest.isolateModules(() => {
|
|
require('../../src/services/imageProcessor');
|
|
});
|
|
|
|
expect(mockSharp.cache).toHaveBeenCalledWith(false);
|
|
expect(mockSharp.concurrency).toHaveBeenCalledWith(2);
|
|
});
|
|
});
|