Replace the Event_Summaries view with a physical snapshot table
refreshed via Stale-While-Revalidate (SWR) pattern. A stored
procedure (Refresh_Summaries_SWR) uses GET_LOCK for non-blocking
concurrency and atomic table rename for zero-downtime refresh.
Database changes (db/views.sql):
- Rename Event_Summaries view to VIEW_Event_Summaries (source view)
- Add Event_Summaries snapshot table and Event_Summaries_Metadata table
- Add Refresh_Summaries_SWR stored procedure with GET_LOCK and
atomic rename pattern to prevent thundering herd
- Add MySQL EVENT for background refresh every 600 seconds
PHP call sites (web/):
- Add ensureSummariesFresh() helper in database.php with static
per-request dedup and 60s staleness check
- Call ensureSummariesFresh() before Event_Summaries queries in
console.php, _monitor_filters.php, and Monitor.php
- Add beforeFind() hook in CakePHP Event_Summary model
Perl call sites (scripts/):
- Add ensureSummariesFresh() sub in Event_Summary.pm with
per-process 60s rate-limiting
- Call ensureSummariesFresh() in Monitor.pm Event_Summary accessor
Upgrade path (db/zm_update-1.37.78.sql.in):
- Drop any prior Event_Summaries view or table before recreating
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove denormalized event summary tables and their associated triggers,
replacing them with views that query the Events table directly. This
eliminates trigger maintenance overhead and periodic reconciliation in
zmaudit/zmstats, since the views compute stats on the fly.
- Remove trigger definitions for event summary table maintenance
- Remove event summary table inserts from zm_event.cpp
- Remove event count reconciliation queries from zmaudit.pl
- Remove DELETE-on-views calls from zmstats.pl (views filter by date inherently)
- Remove Event_Summaries DELETE from Monitor.php (can't delete from a view)
- Add db/views.sql with view definitions and covering index
- Add upgrade script zm_update-1.37.78.sql.in (drop triggers, drop tables, create views)
- Update zm_create.sql.in to use views instead of tables for fresh installs
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When auth is disabled or auth_relay is empty, appending '&'+auth_relay
produces a trailing '&' which results in double '&&' when the next
parameter is appended (e.g. ?monitor=2&&scale=41&mode=single).
Guard all 4 places in MonitorStream.js where auth_relay is concatenated
into URLs, consistent with EventStream.js which already guards this.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ondisconnect() nulls this.ws, but the WebSocket open event fires
asynchronously. If ondisconnect() runs between socket creation and
the open callback, onopen() hits a null this.ws when adding the
message listener. Add a null guard to bail out cleanly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The bandwidth limiter in skin.php ran after config.php was included,
so the ZM_WEB_* constants were defined using the unclamped cookie
value. When no cookie existed and ZM_BANDWIDTH_DEFAULT was 'high',
a user with MaxBandwidth='medium' would get high bandwidth config
values despite the navbar showing medium.
Move the MaxBandwidth clamp before the config.php include so the
switch statement sees the correct value.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Each filter now has its own ExecuteInterval column, making the global
ZM_FILTER_EXECUTE_INTERVAL unused. The DB row will be cleaned up
automatically by zmupdate.pl on the next upgrade.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- The table refresh message will not appear briefly. The table will not "flicker."
Scrolling will attempt to return to the position it was in before the refresh.
When starting a stream on the Watch page, don't execute controlMute(), as this will cause the icon to be displayed before the stream actually starts playing.
We'll just set the "muted" property for the stream.
We'll manage the icon later.
Because we store 'zmWatchMuted' in cookies, not 'zmWatchMute'
We can pass 'on' or 'off' to the "controlMute" function, but we store a Boolean value in cookies, so we need to convert it.
Also, if there's no cookie, getCookie will return "null," but for us, that doesn't equal "false."
- Add user= parameter to get_auth_relay() so zms can use the indexed
Username column instead of iterating all users to validate the hash
- Apply the same fix to Event.php getStreamSrc() and getThumbnailSrc()
- Tighten Monitor.php from isset() to !empty() for consistency
- In MonitorStream.js start(), check if the auth hash in the img src
matches the current auth_hash before resuming via CMD_PLAY. If stale,
fall through to rebuild the URL with fresh auth_relay. This prevents
long-running montage pages from spawning zms with expired credentials.
- Downgrade zms auth failure from Error to Warning
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a boolean web config option to control whether server statistics
(load, CPU, DB connections, storage, RAM) are rendered in the navbar.
When disabled, the HTML is not output at all, saving the polling overhead.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a boolean web config option to control visibility of the Back and
Refresh navigation buttons shown at the top of most views. Uses a body
class and CSS rule so no individual view files need changes.
Also remove the ZM_WEB_BUTTON_STYLE INSERT from the migration SQL since
zmupdate.pl handles Config table inserts from ConfigData.pm.in.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a web config option to control toolbar button display:
- icons+text (default): show both icon and label
- icons: show only the icon, hide text labels
- text: show only the label, hide icons on buttons that have labels
Body class (btn-icons-only / btn-text-only) is set in getBodyTopHTML() and
CSS rules in skin.css toggle visibility of .text spans and icon elements.
Add title tooltips to console.php buttons so they remain usable in icon-only
mode. Migration appended to zm_update-1.39.4.sql.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Change ZM_OPT_USE_REMEMBER_ME from a boolean to a tri-state string:
- None: checkbox hidden, sessions persist for ZM_COOKIE_LIFETIME (old disabled)
- Yes: checkbox shown and pre-checked by default
- No: checkbox shown and unchecked by default (old enabled behavior)
Update ConfigData.pm.in with new type definition, login.php to honor the
checked state, and session/action handlers to recognize the new values.
Migration in zm_update-1.39.4.sql maps old '1' to 'No' and '0' to 'None'.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Column headers like "Alarm<br/>Frames" and "Avg.<br/>Score" show the literal
<br/> text in the column selection dropdown. Add data-switchable-label attributes
with the <br/> replaced by a space so the column picker shows clean labels while
the table headers still render the line break. Fix applied to events.php and
watch.php.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Switch filter labels from stacked (above dropdown) to inline (beside dropdown)
to reduce vertical space. Make sizeControl form use flex-wrap for proper flow.
Move Show Zones and Control links from floating headerButtons div into the
sizeControl form so they flow with the layout buttons. Fix chosen.js Ratio
dropdown being too narrow by setting min-width with !important to override
inline styles set by chosen on dynamically-populated selects.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>