mirror of
https://github.com/penpot/penpot.git
synced 2026-10-29 05:50:35 +00:00
An instance left the audit events pipeline dead: every push-audit-events answered with 429 and 69 worker threads parked for good inside a limiter. Two code paths of one profile acquired the same two limiters in opposite order: a command declaring its own limits (upload-file-media-object) took process-image/by-profile and then root/by-profile, while a command with no limits of its own took the roots first and then re-entered climit from inside its handler with the same ids (create-file-media-object-from-url). Each path held what the other one waited for, and no limiter had a timeout to break the wait. Now wrap puts the root limiters after the command's own, so they end up outermost and every path meets them first: the nested chains carry only the command's own limits and run inside them, which puts both paths in the same order. impl-invoke also keeps, per thread, the set of limiters that thread is inside of and runs the handler inline when a call asks for one it already holds, so a handler can no longer queue behind itself. The shipped climit.edn sets a timeout on every limiter (60s for operations that can be slow, 30s otherwise), so no wait can park a thread for ever. The production file lives outside the repository and has to be mirrored. Tests cover the acquisition order, the deadlock, the reentrancy and the timeouts of the shipped config. AI-assisted-by: deepseek-v4.1-flash