mirror of
https://github.com/ZoneMinder/zoneminder.git
synced 2026-09-14 22:27:08 -04:00
A filter with LockRows set selected its entire result set FOR UPDATE inside one transaction, so every lock the per-event work went on to take was held until the run committed: Events[Id] -> Events_Hour/Day/Week/Month[EventId] -> Event_Summaries[MonitorId] -> Storage[Id] Because the locks accumulated across events, two filters deadlocked: one held Event_Summaries for a monitor while waiting on a Storage row, the other held that Storage row while waiting on Event_Summaries for its next event. No per-event lock ordering can fix that while both rows stay locked for the length of the batch. Holding Event_Summaries for the whole run also blocked zmc from opening a new event on any monitor the filter had touched, since creating an event updates that row. The transaction spanned ffmpeg encodes, uploads and executed commands as well, so it could be held open for minutes. zmfilter now claims one event at a time in Events_Lock and releases it when it is done with that event, so no InnoDB lock is held across the work. The per-event body moves into checkFilterEvent. skip_locked now adds NOT EXISTS over Events_Lock to the filter query rather than SKIP LOCKED. The exclusion has to happen in the query: a filter whose whole result set was held elsewhere would otherwise fill its LIMIT with events it could only skip, and make no progress. It no longer depends on MariaDB 10.6 / MySQL 8.0.1, so the UI no longer disables the option on older servers. Also drops the two dbh->commit() calls in the AutoCopy branch. With no transaction open they would warn, and before this they were silently ending the batch transaction mid-loop, so AutoCopy filters never had the guarantee LockRows was supposed to give them. filterdebug.php was appending a bare ' SKIP LOCKED' after the LIMIT, which is not valid SQL; it now renders the real clause in the right position. Adds t/event_lock.t and t/filter_sql.t.