const DEFAULT_MAX_CONCURRENT_IMAGE_JOBS = 1 const MAX_ALLOWED_IMAGE_JOBS = 2 function readMaxConcurrentImageJobs(): number { const configured = Number.parseInt(process.env.IMAGE_PROCESSING_MAX_CONCURRENT || '', 10) if (!Number.isFinite(configured)) { return DEFAULT_MAX_CONCURRENT_IMAGE_JOBS } return Math.max(1, Math.min(MAX_ALLOWED_IMAGE_JOBS, configured)) } const maxConcurrentImageJobs = readMaxConcurrentImageJobs() let activeImageJobs = 0 const waitingImageJobs: Array<() => void> = [] async function acquireImageProcessingSlot(): Promise { if (activeImageJobs < maxConcurrentImageJobs) { activeImageJobs += 1 return } await new Promise((resolve) => waitingImageJobs.push(resolve)) activeImageJobs += 1 } function releaseImageProcessingSlot(): void { activeImageJobs = Math.max(0, activeImageJobs - 1) const next = waitingImageJobs.shift() if (next) { next() } } /** * 跨请求限制 Sharp 任务数。单个请求内串行并不能阻止多个请求同时解码大图, * 所以白底图、预览图和长图处理都必须共用这个进程级门闩。 */ export async function withImageProcessingSlot(task: () => Promise): Promise { await acquireImageProcessingSlot() try { return await task() } finally { releaseImageProcessingSlot() } }