From 6aaacb35413caf8bb660762d4e55c27463a56792 Mon Sep 17 00:00:00 2001 From: Ben Dailey Date: Mon, 26 Jan 2026 11:30:13 -0500 Subject: [PATCH 1/2] Replace Events_Hour/Day/Week/Month/Archived and Event_Summaries tables with views 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 --- db/CMakeLists.txt | 3 + db/triggers.sql | 204 ++---------------------------------- db/views.sql | 90 ++++++++++++++++ db/zm_create.sql.in | 74 +------------ db/zm_update-1.37.78.sql.in | 20 ++++ scripts/zmaudit.pl.in | 56 ---------- scripts/zmstats.pl.in | 5 - src/zm_event.cpp | 34 ------ web/includes/Monitor.php | 1 - 9 files changed, 122 insertions(+), 365 deletions(-) create mode 100644 db/views.sql create mode 100644 db/zm_update-1.37.78.sql.in diff --git a/db/CMakeLists.txt b/db/CMakeLists.txt index 236049b62c0..f0499e3a501 100644 --- a/db/CMakeLists.txt +++ b/db/CMakeLists.txt @@ -8,6 +8,7 @@ configure_file(zm_update-1.37.4.sql.in "${CMAKE_CURRENT_BINARY_DIR}/zm_update-1. configure_file(zm_update-1.37.69.sql.in "${CMAKE_CURRENT_BINARY_DIR}/zm_update-1.37.69.sql" @ONLY) configure_file(zm_update-1.37.74.sql.in "${CMAKE_CURRENT_BINARY_DIR}/zm_update-1.37.74.sql" @ONLY) configure_file(zm_update-1.37.77.sql.in "${CMAKE_CURRENT_BINARY_DIR}/zm_update-1.37.77.sql" @ONLY) +configure_file(zm_update-1.37.78.sql.in "${CMAKE_CURRENT_BINARY_DIR}/zm_update-1.37.78.sql" @ONLY) # install zm_create.sql install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm_create.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db") @@ -27,6 +28,8 @@ install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm_update-1.37.69.sql" DESTINATION "$ install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm_update-1.37.74.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db") # install zm_update-1.37.77.sql install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm_update-1.37.77.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db") +# install zm_update-1.37.78.sql +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm_update-1.37.78.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db") # Install the database upgrade scripts install(FILES ${dbfileslist} DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db") diff --git a/db/triggers.sql b/db/triggers.sql index b6e64ee11b4..ff0157b5082 100644 --- a/db/triggers.sql +++ b/db/triggers.sql @@ -1,220 +1,29 @@ +DELIMITER // -delimiter // DROP TRIGGER IF EXISTS Events_Hour_delete_trigger// -CREATE TRIGGER Events_Hour_delete_trigger BEFORE DELETE ON Events_Hour -FOR EACH ROW BEGIN - UPDATE Event_Summaries SET - HourEvents = GREATEST(COALESCE(HourEvents,1)-1,0), - HourEventDiskSpace=GREATEST(COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) - WHERE Event_Summaries.MonitorId=OLD.MonitorId; -END; -// DROP TRIGGER IF EXISTS Events_Hour_update_trigger// -CREATE TRIGGER Events_Hour_update_trigger AFTER UPDATE ON Events_Hour -FOR EACH ROW - BEGIN - declare diff BIGINT default 0; - - set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); - IF ( diff ) THEN - IF ( NEW.MonitorID != OLD.MonitorID ) THEN - UPDATE Event_Summaries SET HourEventDiskSpace=GREATEST(COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Event_Summaries.MonitorId=OLD.MonitorId; - UPDATE Event_Summaries SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Event_Summaries.MonitorId=NEW.MonitorId; - ELSE - UPDATE Event_Summaries SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)+diff WHERE Event_Summaries.MonitorId=NEW.MonitorId; - END IF; - END IF; - END; -// - DROP TRIGGER IF EXISTS Events_Day_delete_trigger// -CREATE TRIGGER Events_Day_delete_trigger BEFORE DELETE ON Events_Day -FOR EACH ROW BEGIN - UPDATE Event_Summaries SET - DayEvents = GREATEST(COALESCE(DayEvents,1)-1,0), - DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) - WHERE Event_Summaries.MonitorId=OLD.MonitorId; -END; -// - -DROP TRIGGER IF EXISTS Events_Day_update_trigger; -CREATE TRIGGER Events_Day_update_trigger AFTER UPDATE ON Events_Day -FOR EACH ROW - BEGIN - declare diff BIGINT default 0; - - set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); - IF ( diff ) THEN - IF ( NEW.MonitorID != OLD.MonitorID ) THEN - UPDATE Event_Summaries SET DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Event_Summaries.MonitorId=OLD.MonitorId; - UPDATE Event_Summaries SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Event_Summaries.MonitorId=NEW.MonitorId; - ELSE - UPDATE Event_Summaries SET DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)+diff,0) WHERE Event_Summaries.MonitorId=NEW.MonitorId; - END IF; - END IF; - END; - // +DROP TRIGGER IF EXISTS Events_Day_update_trigger// DROP TRIGGER IF EXISTS Events_Week_delete_trigger// -CREATE TRIGGER Events_Week_delete_trigger BEFORE DELETE ON Events_Week -FOR EACH ROW BEGIN - UPDATE Event_Summaries SET - WeekEvents = GREATEST(COALESCE(WeekEvents,1)-1,0), - WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) - WHERE Event_Summaries.MonitorId=OLD.MonitorId; -END; -// -DROP TRIGGER IF EXISTS Events_Week_update_trigger; -CREATE TRIGGER Events_Week_update_trigger AFTER UPDATE ON Events_Week -FOR EACH ROW - BEGIN - declare diff BIGINT default 0; - - set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); - IF ( diff ) THEN - IF ( NEW.MonitorID != OLD.MonitorID ) THEN - UPDATE Event_Summaries SET WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Event_Summaries.MonitorId=OLD.MonitorId; - UPDATE Event_Summaries SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Event_Summaries.MonitorId=NEW.MonitorId; - ELSE - UPDATE Event_Summaries SET WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)+diff,0) WHERE Event_Summaries.MonitorId=NEW.MonitorId; - END IF; - END IF; - END; - // +DROP TRIGGER IF EXISTS Events_Week_update_trigger// DROP TRIGGER IF EXISTS Events_Month_delete_trigger// -CREATE TRIGGER Events_Month_delete_trigger BEFORE DELETE ON Events_Month -FOR EACH ROW BEGIN - UPDATE Event_Summaries SET - MonthEvents = GREATEST(COALESCE(MonthEvents,1)-1,0), - MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) - WHERE Event_Summaries.MonitorId=OLD.MonitorId; -END; -// - -DROP TRIGGER IF EXISTS Events_Month_update_trigger; -CREATE TRIGGER Events_Month_update_trigger AFTER UPDATE ON Events_Month -FOR EACH ROW - BEGIN - declare diff BIGINT default 0; - - set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); - IF ( diff ) THEN - IF ( NEW.MonitorID != OLD.MonitorID ) THEN - UPDATE Event_Summaries SET MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace),0) WHERE Event_Summaries.MonitorId=OLD.MonitorId; - UPDATE Event_Summaries SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)+COALESCE(NEW.DiskSpace) WHERE Event_Summaries.MonitorId=NEW.MonitorId; - ELSE - UPDATE Event_Summaries SET MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)+diff,0) WHERE Event_Summaries.MonitorId=NEW.MonitorId; - END IF; - END IF; - END; - // - -drop procedure if exists update_storage_stats// -drop trigger if exists event_update_trigger// - -CREATE TRIGGER event_update_trigger AFTER UPDATE ON Events -FOR EACH ROW -BEGIN - declare diff BIGINT default 0; - set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); +DROP TRIGGER IF EXISTS Events_Month_update_trigger// - IF ( diff ) THEN - UPDATE Events_Hour SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; - UPDATE Events_Day SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; - UPDATE Events_Week SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; - UPDATE Events_Month SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; - UPDATE Event_Summaries - SET - TotalEventDiskSpace = GREATEST(COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0),0) - WHERE Event_Summaries.MonitorId=OLD.MonitorId; - END IF; +DROP PROCEDURE IF EXISTS update_storage_stats// - IF ( NEW.Archived != OLD.Archived ) THEN - IF ( NEW.Archived ) THEN - INSERT INTO Events_Archived (EventId,MonitorId,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.DiskSpace); - INSERT INTO Event_Summaries (MonitorId,ArchivedEvents,ArchivedEventDiskSpace) VALUES (NEW.MonitorId,1,NEW.DiskSpace) ON DUPLICATE KEY - UPDATE ArchivedEvents = COALESCE(ArchivedEvents,0)+1, ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) + COALESCE(NEW.DiskSpace,0); - ELSEIF ( OLD.Archived ) THEN - DELETE FROM Events_Archived WHERE EventId=OLD.Id; - UPDATE Event_Summaries - SET - ArchivedEvents = GREATEST(COALESCE(ArchivedEvents,0)-1,0), - ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0) - WHERE Event_Summaries.MonitorId=OLD.MonitorId; - ELSE - IF ( OLD.DiskSpace != NEW.DiskSpace ) THEN - UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; - UPDATE Event_Summaries SET - ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0),0) - WHERE Event_Summaries.MonitorId=OLD.MonitorId; - END IF; - END IF; - ELSEIF ( NEW.Archived AND diff ) THEN - UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; - END IF; - -END; - -// +DROP TRIGGER IF EXISTS event_update_trigger// DROP TRIGGER IF EXISTS event_insert_trigger// -/* The assumption is that when an Event is inserted, it has no size yet, so don't bother updating the DiskSpace, just the count. - * The DiskSpace will get update in the Event Update Trigger - */ -/* -CREATE TRIGGER event_insert_trigger AFTER INSERT ON Events -FOR EACH ROW - BEGIN - - INSERT INTO Events_Hour (EventId,MonitorId,StartDateTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartDateTime,0); - INSERT INTO Events_Day (EventId,MonitorId,StartDateTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartDateTime,0); - INSERT INTO Events_Week (EventId,MonitorId,StartDateTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartDateTime,0); - INSERT INTO Events_Month (EventId,MonitorId,StartDateTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartDateTime,0); - INSERT INTO Event_Summaries (MonitorId,HourEvents,DayEvents,WeekEvents,MonthEvents,TotalEvents) VALUES (NEW.MonitorId,1,1,1,1,1) ON DUPLICATE KEY - UPDATE - HourEvents = COALESCE(HourEvents,0)+1, - DayEvents = COALESCE(DayEvents,0)+1, - WeekEvents = COALESCE(WeekEvents,0)+1, - MonthEvents = COALESCE(MonthEvents,0)+1, - TotalEvents = COALESCE(TotalEvents,0)+1; -END; -// -*/ - DROP TRIGGER IF EXISTS event_delete_trigger// -CREATE TRIGGER event_delete_trigger BEFORE DELETE ON Events -FOR EACH ROW -BEGIN - DELETE FROM Events_Hour WHERE EventId=OLD.Id; - DELETE FROM Events_Day WHERE EventId=OLD.Id; - DELETE FROM Events_Week WHERE EventId=OLD.Id; - DELETE FROM Events_Month WHERE EventId=OLD.Id; - IF ( OLD.Archived ) THEN - DELETE FROM Events_Archived WHERE EventId=OLD.Id; - UPDATE Event_Summaries SET - ArchivedEvents = GREATEST(COALESCE(ArchivedEvents,1) - 1,0), - ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0), - TotalEvents = GREATEST(COALESCE(TotalEvents,1) - 1,0), - TotalEventDiskSpace = GREATEST(COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0) - WHERE Event_Summaries.MonitorId=OLD.MonitorId; - ELSE - UPDATE Event_Summaries SET - TotalEvents = GREATEST(COALESCE(TotalEvents,1)-1,0), - TotalEventDiskSpace=GREATEST(COALESCE(TotalEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) - WHERE Event_Summaries.MonitorId=OLD.MonitorId; - END IF; -END; - -// - DROP TRIGGER IF EXISTS Zone_Insert_Trigger// CREATE TRIGGER Zone_Insert_Trigger AFTER INSERT ON Zones FOR EACH ROW @@ -222,6 +31,7 @@ FOR EACH ROW UPDATE Monitors SET ZoneCount=(SELECT COUNT(*) FROM Zones WHERE MonitorId=NEW.MonitorId) WHERE Monitors.Id=NEW.MonitorID; END // + DROP TRIGGER IF EXISTS Zone_Delete_Trigger// CREATE TRIGGER Zone_Delete_Trigger AFTER DELETE ON Zones FOR EACH ROW diff --git a/db/views.sql b/db/views.sql new file mode 100644 index 00000000000..b1c8f2b4618 --- /dev/null +++ b/db/views.sql @@ -0,0 +1,90 @@ +DELIMITER // + +-- This index covers MonitorId grouping, StartDateTime ranges, +-- Archived status filtering, and includes DiskSpace for summation. +CREATE INDEX IF NOT EXISTS Events_Summaries_Performance_idx +ON Events (MonitorId, StartDateTime, Archived, DiskSpace)// + +-- Clean up a previous typoed version of the index +DROP INDEX IF EXISTS Events_Summaries_Perfomance_idx ON Events// + +-- Hourly Events View +CREATE OR REPLACE VIEW Events_Hour AS +SELECT + Id AS EventId, + MonitorId, + DiskSpace, + StartDateTime +FROM Events +WHERE StartDateTime >= (NOW() - INTERVAL 1 HOUR)// + +-- Daily Events View +CREATE OR REPLACE VIEW Events_Day AS +SELECT + Id AS EventId, + MonitorId, + DiskSpace, + StartDateTime +FROM Events +WHERE StartDateTime >= (NOW() - INTERVAL 1 DAY)// + +-- Weekly Events View +CREATE OR REPLACE VIEW Events_Week AS +SELECT + Id AS EventId, + MonitorId, + DiskSpace, + StartDateTime +FROM Events +WHERE StartDateTime >= (NOW() - INTERVAL 7 DAY)// + +-- Monthly Events View +CREATE OR REPLACE VIEW Events_Month AS +SELECT + Id AS EventId, + MonitorId, + DiskSpace, + StartDateTime +FROM Events +WHERE StartDateTime >= (NOW() - INTERVAL 1 MONTH)// + +-- Archived Events View +CREATE OR REPLACE VIEW Events_Archived AS +SELECT + Id AS EventId, + MonitorId, + DiskSpace +FROM Events +WHERE Archived = 1// + +-- Event Summaries View +CREATE OR REPLACE VIEW Event_Summaries AS +SELECT + MonitorId, + -- Hour Stats + COUNT(CASE WHEN StartDateTime >= (NOW() - INTERVAL 1 HOUR) THEN 1 END) AS HourEvents, + COALESCE(SUM(CASE WHEN StartDateTime >= (NOW() - INTERVAL 1 HOUR) THEN DiskSpace ELSE 0 END), 0) AS HourEventDiskSpace, + + -- Day Stats + COUNT(CASE WHEN StartDateTime >= (NOW() - INTERVAL 1 DAY) THEN 1 END) AS DayEvents, + COALESCE(SUM(CASE WHEN StartDateTime >= (NOW() - INTERVAL 1 DAY) THEN DiskSpace ELSE 0 END), 0) AS DayEventDiskSpace, + + -- Week Stats + COUNT(CASE WHEN StartDateTime >= (NOW() - INTERVAL 7 DAY) THEN 1 END) AS WeekEvents, + COALESCE(SUM(CASE WHEN StartDateTime >= (NOW() - INTERVAL 7 DAY) THEN DiskSpace ELSE 0 END), 0) AS WeekEventDiskSpace, + + -- Month Stats + COUNT(CASE WHEN StartDateTime >= (NOW() - INTERVAL 1 MONTH) THEN 1 END) AS MonthEvents, + COALESCE(SUM(CASE WHEN StartDateTime >= (NOW() - INTERVAL 1 MONTH) THEN DiskSpace ELSE 0 END), 0) AS MonthEventDiskSpace, + + -- Archive Stats + COUNT(CASE WHEN Archived = 1 THEN 1 END) AS ArchivedEvents, + COALESCE(SUM(CASE WHEN Archived = 1 THEN DiskSpace ELSE 0 END), 0) AS ArchivedEventDiskSpace, + + -- Totals + COUNT(Id) AS TotalEvents, + COALESCE(SUM(DiskSpace), 0) AS TotalEventDiskSpace +FROM Events +GROUP BY MonitorId// + +DELIMITER ; \ No newline at end of file diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index abe60df6fcd..25478bfb755 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -225,60 +225,6 @@ CREATE TABLE `Events` ( KEY `Events_EndDateTime_DiskSpace` (`EndDateTime`,`DiskSpace`) ) ENGINE=@ZM_MYSQL_ENGINE@; -DROP TABLE IF EXISTS `Events_Hour`; -CREATE TABLE `Events_Hour` ( - `EventId` BIGINT unsigned NOT NULL, - `MonitorId` int(10) unsigned NOT NULL, - `StartDateTime` datetime default NULL, - `DiskSpace` bigint default NULL, - PRIMARY KEY (`EventId`), - KEY `Events_Hour_MonitorId_idx` (`MonitorId`), - KEY `Events_Hour_StartDateTime_idx` (`StartDateTime`) -) ENGINE=@ZM_MYSQL_ENGINE@; - -DROP TABLE IF EXISTS `Events_Day`; -CREATE TABLE `Events_Day` ( - `EventId` BIGINT unsigned NOT NULL, - `MonitorId` int(10) unsigned NOT NULL, - `StartDateTime` datetime default NULL, - `DiskSpace` bigint default NULL, - PRIMARY KEY (`EventId`), - KEY `Events_Day_MonitorId_idx` (`MonitorId`), - KEY `Events_Day_StartDateTime_idx` (`StartDateTime`) -) ENGINE=@ZM_MYSQL_ENGINE@; - -DROP TABLE IF EXISTS `Events_Week`; -CREATE TABLE `Events_Week` ( - `EventId` BIGINT unsigned NOT NULL, - `MonitorId` int(10) unsigned NOT NULL, - `StartDateTime` datetime default NULL, - `DiskSpace` bigint default NULL, - PRIMARY KEY (`EventId`), - KEY `Events_Week_MonitorId_idx` (`MonitorId`), - KEY `Events_Week_StartDateTime_idx` (`StartDateTime`) -) ENGINE=@ZM_MYSQL_ENGINE@; - -DROP TABLE IF EXISTS `Events_Month`; -CREATE TABLE `Events_Month` ( - `EventId` BIGINT unsigned NOT NULL, - `MonitorId` int(10) unsigned NOT NULL, - `StartDateTime` datetime default NULL, - `DiskSpace` bigint default NULL, - PRIMARY KEY (`EventId`), - KEY `Events_Month_MonitorId_idx` (`MonitorId`), - KEY `Events_Month_StartDateTime_idx` (`StartDateTime`) -) ENGINE=@ZM_MYSQL_ENGINE@; - - -DROP TABLE IF EXISTS `Events_Archived`; -CREATE TABLE `Events_Archived` ( - `EventId` BIGINT unsigned NOT NULL, - `MonitorId` int(10) unsigned NOT NULL, - `DiskSpace` bigint default NULL, - PRIMARY KEY (`EventId`), - KEY `Events_Archived_MonitorId_idx` (`MonitorId`) -) ENGINE=@ZM_MYSQL_ENGINE@; - DROP TABLE IF EXISTS `Event_Data`; CREATE TABLE `Event_Data` ( `Id` BIGINT unsigned NOT NULL auto_increment, @@ -646,24 +592,6 @@ CREATE TABLE `Monitor_Status` ( ) ENGINE=@ZM_MYSQL_ENGINE@; CREATE INDEX Monitor_Status_UpdatedOn_idx on Monitor_Status(UpdatedOn); -DROP TABLE IF EXISTS `Event_Summaries`; -CREATE TABLE `Event_Summaries` ( - `MonitorId` int(10) unsigned NOT NULL, - `TotalEvents` int(10) default NULL, - `TotalEventDiskSpace` bigint default NULL, - `HourEvents` int(10) default NULL, - `HourEventDiskSpace` bigint default NULL, - `DayEvents` int(10) default NULL, - `DayEventDiskSpace` bigint default NULL, - `WeekEvents` int(10) default NULL, - `WeekEventDiskSpace` bigint default NULL, - `MonthEvents` int(10) default NULL, - `MonthEventDiskSpace` bigint default NULL, - `ArchivedEvents` int(10) default NULL, - `ArchivedEventDiskSpace` bigint default NULL, - PRIMARY KEY (`MonitorId`) -) ENGINE=@ZM_MYSQL_ENGINE@; - -- -- Table structure for table `States` -- PP - Added IsActive to track custom run states @@ -1272,6 +1200,8 @@ CREATE TABLE `Events_Tags` ( source @PKGDATADIR@/db/Object_Types.sql -- We generally don't alter triggers, we drop and re-create them, so let's keep them in a separate file that we can just source in update scripts. source @PKGDATADIR@/db/triggers.sql +-- Views replace the old Events_Hour, Events_Day, Events_Week, Events_Month, Events_Archived, and Event_Summaries tables. +source @PKGDATADIR@/db/views.sql source @PKGDATADIR@/db/manufacturers.sql source @PKGDATADIR@/db/models.sql diff --git a/db/zm_update-1.37.78.sql.in b/db/zm_update-1.37.78.sql.in new file mode 100644 index 00000000000..550781e29f8 --- /dev/null +++ b/db/zm_update-1.37.78.sql.in @@ -0,0 +1,20 @@ +-- +-- Replace Events_Hour, Events_Day, Events_Week, Events_Month, +-- Events_Archived, and Event_Summaries tables with views. +-- These views query the Events table directly, eliminating the need +-- for triggers and periodic reconciliation in zmaudit/zmstats. +-- + +-- Step 1: Drop triggers that maintained the old tables +source @PKGDATADIR@/db/triggers.sql + +-- Step 2: Drop the old denormalized tables +DROP TABLE IF EXISTS `Events_Hour`; +DROP TABLE IF EXISTS `Events_Day`; +DROP TABLE IF EXISTS `Events_Week`; +DROP TABLE IF EXISTS `Events_Month`; +DROP TABLE IF EXISTS `Events_Archived`; +DROP TABLE IF EXISTS `Event_Summaries`; + +-- Step 3: Create views with the same names and a covering index +source @PKGDATADIR@/db/views.sql diff --git a/scripts/zmaudit.pl.in b/scripts/zmaudit.pl.in index 2ad29a7a5eb..d5617fd9d54 100644 --- a/scripts/zmaudit.pl.in +++ b/scripts/zmaudit.pl.in @@ -888,62 +888,6 @@ FROM `Frames` WHERE `EventId`=?'; } # end if ZM_LOG_DATABASE_LIMIT $loop = $continuous; - my $eventcounts_sql = ' - UPDATE `Event_Summaries` SET - `TotalEvents`=(SELECT COUNT(`Id`) FROM `Events` WHERE `MonitorId`=`Event_Summaries`.`MonitorId`), - `TotalEventDiskSpace`=(SELECT SUM(`DiskSpace`) FROM `Events` WHERE `MonitorId`=`Event_Summaries`.`MonitorId` AND `DiskSpace` IS NOT NULL), - `ArchivedEvents`=(SELECT COUNT(`Id`) FROM `Events` WHERE `MonitorId`=`Event_Summaries`.`MonitorId` AND `Archived`=1), - `ArchivedEventDiskSpace`=(SELECT SUM(`DiskSpace`) FROM `Events` WHERE `MonitorId`=`Event_Summaries`.`MonitorId` AND `Archived`=1 AND `DiskSpace` IS NOT NULL) - '; - - ZoneMinder::Database::zmDbDo($eventcounts_sql); - aud_print('Finished updating TotalEvents, ArchivedEvents'); - - my $eventcounts_hour_sql = ' - UPDATE `Event_Summaries` INNER JOIN ( - SELECT `MonitorId`, COUNT(*) AS `HourEvents`, SUM(COALESCE(`DiskSpace`,0)) AS `HourEventDiskSpace` - FROM `Events_Hour` GROUP BY `MonitorId` - ) AS `E` ON `E`.`MonitorId`=`Event_Summaries`.`MonitorId` SET - `Event_Summaries`.`HourEvents` = `E`.`HourEvents`, - `Event_Summaries`.`HourEventDiskSpace` = `E`.`HourEventDiskSpace` - '; - ZoneMinder::Database::zmDbDo($eventcounts_hour_sql); - aud_print("Finished updating HourEvents"); - - - my $eventcounts_day_sql = ' - UPDATE `Event_Summaries` INNER JOIN ( - SELECT `MonitorId`, COUNT(*) AS `DayEvents`, SUM(COALESCE(`DiskSpace`,0)) AS `DayEventDiskSpace` - FROM `Events_Day` GROUP BY `MonitorId` - ) AS `E` ON `E`.`MonitorId`=`Event_Summaries`.`MonitorId` SET - `Event_Summaries`.`DayEvents` = `E`.`DayEvents`, - `Event_Summaries`.`DayEventDiskSpace` = `E`.`DayEventDiskSpace` - '; - ZoneMinder::Database::zmDbDo($eventcounts_day_sql); - aud_print("Finished updating DayEvents"); - - my $eventcounts_week_sql = ' - UPDATE `Event_Summaries` INNER JOIN ( - SELECT `MonitorId`, COUNT(*) AS `WeekEvents`, SUM(COALESCE(`DiskSpace`,0)) AS `WeekEventDiskSpace` - FROM `Events_Week` GROUP BY `MonitorId` - ) AS `E` ON `E`.`MonitorId`=`Event_Summaries`.`MonitorId` SET - `Event_Summaries`.`WeekEvents` = `E`.`WeekEvents`, - `Event_Summaries`.`WeekEventDiskSpace` = `E`.`WeekEventDiskSpace` - '; - ZoneMinder::Database::zmDbDo($eventcounts_week_sql); - aud_print("Finished updating WeekEvents"); - - my $eventcounts_month_sql = ' - UPDATE `Event_Summaries` INNER JOIN ( - SELECT `MonitorId`, COUNT(*) AS `MonthEvents`, SUM(COALESCE(`DiskSpace`,0)) AS `MonthEventDiskSpace` - FROM `Events_Month` GROUP BY `MonitorId` - ) AS `E` ON `E`.`MonitorId`=`Event_Summaries`.`MonitorId` SET - `Event_Summaries`.`MonthEvents` = `E`.`MonthEvents`, - `Event_Summaries`.`MonthEventDiskSpace` = `E`.`MonthEventDiskSpace` - '; - ZoneMinder::Database::zmDbDo($eventcounts_month_sql); - aud_print("Finished updating MonthEvents"); - ZoneMinder::Database::zmDbDo('UPDATE Storage SET DiskSpace=(SELECT SUM(DiskSpace) FROM Events WHERE StorageId=Storage.Id)'); aud_print("Finished updating Storage DiskSpace"); diff --git a/scripts/zmstats.pl.in b/scripts/zmstats.pl.in index 282adb20b74..0cb1b7aa796 100644 --- a/scripts/zmstats.pl.in +++ b/scripts/zmstats.pl.in @@ -86,11 +86,6 @@ while (!$zm_terminate) { # Clear out statuses for Monitors that aren't updating themselves. zmDbDo('DELETE FROM Monitor_Status WHERE UpdatedOn < timestamp(DATE_SUB(NOW(), INTERVAL 1 MINUTE))'); - zmDbDo('DELETE FROM Events_Hour WHERE StartDateTime < DATE_SUB(NOW(), INTERVAL 1 hour)'); - zmDbDo('DELETE FROM Events_Day WHERE StartDateTime < DATE_SUB(NOW(), INTERVAL 1 day)'); - zmDbDo('DELETE FROM Events_Week WHERE StartDateTime < DATE_SUB(NOW(), INTERVAL 1 week)'); - zmDbDo('DELETE FROM Events_Month WHERE StartDateTime < DATE_SUB(NOW(), INTERVAL 1 month)'); - # Prune the Logs table if required if ( $Config{ZM_LOG_DATABASE_LIMIT} ) { if ( $Config{ZM_LOG_DATABASE_LIMIT} =~ /^\d+$/ ) { diff --git a/src/zm_event.cpp b/src/zm_event.cpp index 634b03a2f11..272912ac339 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -149,40 +149,6 @@ Event::Event( id = zmDbDoInsert(sql); } while (!id and !zm_terminate); - /* None of these are crucial, and are simple when done as individual transactions */ - sql = stringtf("INSERT INTO `Events_Hour` (EventId,MonitorId,StartDateTime,DiskSpace)" - " VALUES (%" PRId64 ",%u,'%s',NULL)", - id, monitor->Id(), now_str.c_str()); - dbQueue.push(std::move(sql)); - sql = stringtf("INSERT INTO `Events_Day` (EventId,MonitorId,StartDateTime,DiskSpace)" - " VALUES (%" PRId64 ",%u,'%s',NULL)", - id, monitor->Id(), now_str.c_str()); - dbQueue.push(std::move(sql)); - sql = stringtf("INSERT INTO `Events_Week` (EventId,MonitorId,StartDateTime,DiskSpace)" - " VALUES (%" PRId64 ",%u,'%s',NULL)", - id, monitor->Id(), now_str.c_str()); - dbQueue.push(std::move(sql)); - sql = stringtf("INSERT INTO `Events_Month` (EventId,MonitorId,StartDateTime,DiskSpace)" - " VALUES (%" PRId64 ",%u,'%s',NULL)", - id, monitor->Id(), now_str.c_str()); - dbQueue.push(std::move(sql)); - /* - sql = stringtf("INSERT INTO `Events_Year` (EventId,MonitorId,StartDateTime,DiskSpace)" - " VALUES (%" PRId64 ",%u,'%s',NULL)", - id, monitor->Id(), now_str.c_str()); - dbQueue.push(std::move(sql)); - */ - sql = stringtf("INSERT INTO Event_Summaries " - "(MonitorId,HourEvents,DayEvents,WeekEvents,MonthEvents,TotalEvents)" - " VALUES (%u,1,1,1,1,1) ON DUPLICATE KEY UPDATE" - " HourEvents = COALESCE(HourEvents,0)+1," - " DayEvents = COALESCE(DayEvents,0)+1," - " WeekEvents = COALESCE(WeekEvents,0)+1," - " MonthEvents = COALESCE(MonthEvents,0)+1," - " TotalEvents = COALESCE(TotalEvents,0)+1", - monitor->Id()); - dbQueue.push(std::move(sql)); - thread_ = std::thread(&Event::Run, this); } diff --git a/web/includes/Monitor.php b/web/includes/Monitor.php index ccb6c865188..b1559293c09 100644 --- a/web/includes/Monitor.php +++ b/web/includes/Monitor.php @@ -733,7 +733,6 @@ public function destroy() { if ( ZM_OPT_X10 ) dbQuery('DELETE FROM TriggersX10 WHERE MonitorId=?', array($this->{'Id'})); dbQuery('DELETE FROM Monitor_Status WHERE MonitorId = ?', array($this->{'Id'})); - dbQuery('DELETE FROM Event_Summaries WHERE MonitorId = ?', array($this->{'Id'})); dbQuery('DELETE FROM Monitors WHERE Id = ?', array($this->{'Id'})); } // end function destroy From 5dd83381772863247103ae9df0cd92f74bfd3f23 Mon Sep 17 00:00:00 2001 From: Ben Dailey Date: Mon, 26 Jan 2026 15:42:12 -0500 Subject: [PATCH 2/2] feat: replace Event_Summaries view with SWR snapshot table 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 --- db/views.sql | 85 ++++++++++++++++++- db/zm_update-1.37.78.sql.in | 6 +- .../lib/ZoneMinder/Event_Summary.pm | 10 +++ scripts/ZoneMinder/lib/ZoneMinder/Monitor.pm | 1 + web/ajax/console.php | 7 +- web/api/app/Model/Event_Summary.php | 17 ++++ web/includes/Monitor.php | 1 + web/includes/database.php | 14 +++ web/skins/classic/views/_monitor_filters.php | 6 +- 9 files changed, 139 insertions(+), 8 deletions(-) diff --git a/db/views.sql b/db/views.sql index b1c8f2b4618..84be8d7f79a 100644 --- a/db/views.sql +++ b/db/views.sql @@ -57,8 +57,8 @@ SELECT FROM Events WHERE Archived = 1// --- Event Summaries View -CREATE OR REPLACE VIEW Event_Summaries AS +-- Event Summaries Source View (used by the refresh procedure) +CREATE OR REPLACE VIEW VIEW_Event_Summaries AS SELECT MonitorId, -- Hour Stats @@ -87,4 +87,83 @@ SELECT FROM Events GROUP BY MonitorId// -DELIMITER ; \ No newline at end of file +-- Event Summaries snapshot table (SWR pattern) +CREATE TABLE IF NOT EXISTS `Event_Summaries` ( + `MonitorId` int(10) unsigned NOT NULL, + `HourEvents` int(10) DEFAULT 0, + `HourEventDiskSpace` bigint DEFAULT 0, + `DayEvents` int(10) DEFAULT 0, + `DayEventDiskSpace` bigint DEFAULT 0, + `WeekEvents` int(10) DEFAULT 0, + `WeekEventDiskSpace` bigint DEFAULT 0, + `MonthEvents` int(10) DEFAULT 0, + `MonthEventDiskSpace` bigint DEFAULT 0, + `ArchivedEvents` int(10) DEFAULT 0, + `ArchivedEventDiskSpace` bigint DEFAULT 0, + `TotalEvents` int(10) DEFAULT 0, + `TotalEventDiskSpace` bigint DEFAULT 0, + PRIMARY KEY (`MonitorId`) +) ENGINE=InnoDB// + +-- Metadata table for SWR staleness tracking +CREATE TABLE IF NOT EXISTS `Event_Summaries_Metadata` ( + `table_name` VARCHAR(64) NOT NULL, + `last_updated` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00', + PRIMARY KEY (`table_name`) +) ENGINE=InnoDB// + +INSERT IGNORE INTO `Event_Summaries_Metadata` + (`table_name`, `last_updated`) VALUES ('Event_Summaries', '1970-01-01 00:00:00')// + +-- Stored procedure: atomic refresh of Event_Summaries with GET_LOCK to prevent thundering herd +DROP PROCEDURE IF EXISTS `Refresh_Summaries_SWR`// + +CREATE PROCEDURE `Refresh_Summaries_SWR`() +proc: BEGIN + DECLARE v_lock_result INT DEFAULT 0; + DECLARE v_last DATETIME; + + -- Non-blocking lock: skip if another process is already refreshing + SET v_lock_result = GET_LOCK('refresh_summaries_lock', 0); + IF v_lock_result != 1 THEN + -- Another process holds the lock; return immediately (stale read is fine) + LEAVE proc; + END IF; + + -- Double-check staleness inside lock + SELECT `last_updated` INTO v_last + FROM `Event_Summaries_Metadata` + WHERE `table_name` = 'Event_Summaries'; + + IF v_last IS NOT NULL AND TIMESTAMPDIFF(SECOND, v_last, NOW()) < 60 THEN + DO RELEASE_LOCK('refresh_summaries_lock'); + LEAVE proc; + END IF; + + -- Atomic rename pattern: build new table, swap, drop old + DROP TABLE IF EXISTS `Event_Summaries_New`; + CREATE TABLE `Event_Summaries_New` LIKE `Event_Summaries`; + INSERT INTO `Event_Summaries_New` SELECT * FROM `VIEW_Event_Summaries`; + + DROP TABLE IF EXISTS `Event_Summaries_Old`; + RENAME TABLE `Event_Summaries` TO `Event_Summaries_Old`, + `Event_Summaries_New` TO `Event_Summaries`; + DROP TABLE IF EXISTS `Event_Summaries_Old`; + + -- Update metadata timestamp + UPDATE `Event_Summaries_Metadata` + SET `last_updated` = NOW() + WHERE `table_name` = 'Event_Summaries'; + + DO RELEASE_LOCK('refresh_summaries_lock'); +END proc// + +-- MySQL EVENT for background refresh every 600 seconds (10 minutes) +-- Note: Requires event_scheduler=ON in my.cnf or SET GLOBAL event_scheduler = ON; +DROP EVENT IF EXISTS `Event_Summaries_Refresh_Event`// + +CREATE EVENT IF NOT EXISTS `Event_Summaries_Refresh_Event` + ON SCHEDULE EVERY 600 SECOND + DO CALL `Refresh_Summaries_SWR`()// + +DELIMITER ; diff --git a/db/zm_update-1.37.78.sql.in b/db/zm_update-1.37.78.sql.in index 550781e29f8..226474c17cf 100644 --- a/db/zm_update-1.37.78.sql.in +++ b/db/zm_update-1.37.78.sql.in @@ -8,13 +8,17 @@ -- Step 1: Drop triggers that maintained the old tables source @PKGDATADIR@/db/triggers.sql --- Step 2: Drop the old denormalized tables +-- Step 2: Drop the old denormalized tables (and any prior Event_Summaries view) DROP TABLE IF EXISTS `Events_Hour`; DROP TABLE IF EXISTS `Events_Day`; DROP TABLE IF EXISTS `Events_Week`; DROP TABLE IF EXISTS `Events_Month`; DROP TABLE IF EXISTS `Events_Archived`; +DROP VIEW IF EXISTS `Event_Summaries`; DROP TABLE IF EXISTS `Event_Summaries`; +DROP TABLE IF EXISTS `Event_Summaries_Metadata`; +DROP TABLE IF EXISTS `Event_Summaries_New`; +DROP TABLE IF EXISTS `Event_Summaries_Old`; -- Step 3: Create views with the same names and a covering index source @PKGDATADIR@/db/views.sql diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Event_Summary.pm b/scripts/ZoneMinder/lib/ZoneMinder/Event_Summary.pm index 0086b5bac04..180c96f741d 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Event_Summary.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Event_Summary.pm @@ -68,6 +68,16 @@ $serial = $primary_key = 'MonitorId'; ArchivedEventDiskSpace => undef, ); +my $_last_checked = 0; + +sub ensureSummariesFresh { + return if (time() - $_last_checked) < 60; + $_last_checked = time(); + + require ZoneMinder::Database; + ZoneMinder::Database::zmDbDo("CALL Refresh_Summaries_SWR()"); +} + sub Monitor { return new ZoneMinder::Monitor( $_[0]{MonitorId} ); } # end sub Monitor diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Monitor.pm b/scripts/ZoneMinder/lib/ZoneMinder/Monitor.pm index 963c1677b69..fdae189f161 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Monitor.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Monitor.pm @@ -393,6 +393,7 @@ sub Event_Summary { my $self = shift; $$self{Event_Summary} = shift if @_; if ( ! $$self{Event_Summary} ) { + ZoneMinder::Event_Summary::ensureSummariesFresh(); $$self{Event_Summary} = ZoneMinder::Event_Summary->find_one(MonitorId=>$$self{Id}); } return $$self{Event_Summary}; diff --git a/web/ajax/console.php b/web/ajax/console.php index c7aded61f54..4171c9b4bf0 100644 --- a/web/ajax/console.php +++ b/web/ajax/console.php @@ -144,11 +144,14 @@ function queryRequest() { } } + // Ensure Event_Summaries snapshot is fresh before querying + ensureSummariesFresh(); + // Build SQL query $sql = 'SELECT M.*, S.*, E.*, (SELECT Name FROM Manufacturers WHERE Manufacturers.Id=M.ManufacturerId) AS Manufacturer, (SELECT Name FROM Models where Models.Id=M.ModelId) AS Model FROM Monitors AS M - LEFT JOIN Monitor_Status AS S ON S.MonitorId=M.Id - LEFT JOIN Event_Summaries AS E ON E.MonitorId=M.Id + LEFT JOIN Monitor_Status AS S ON S.MonitorId=M.Id + LEFT JOIN Event_Summaries AS E ON E.MonitorId=M.Id WHERE M.`Deleted`=false'; if (count($conditions)) { diff --git a/web/api/app/Model/Event_Summary.php b/web/api/app/Model/Event_Summary.php index b9395ab1ba0..00c82274254 100644 --- a/web/api/app/Model/Event_Summary.php +++ b/web/api/app/Model/Event_Summary.php @@ -46,4 +46,21 @@ class Event_Summary extends AppModel { ), ), ); + + public function beforeFind($queryData) { + $db = $this->getDataSource(); + $result = $db->fetchAll( + "SELECT last_updated FROM Event_Summaries_Metadata WHERE table_name='Event_Summaries'" + ); + if ($result) { + $row = $result[0]; + $last_updated = isset($row['Event_Summaries_Metadata']) + ? $row['Event_Summaries_Metadata']['last_updated'] + : (isset($row[0]) ? $row[0]['last_updated'] : null); + if ($last_updated && (time() - strtotime($last_updated)) >= 60) { + $db->rawQuery("CALL Refresh_Summaries_SWR()"); + } + } + return $queryData; + } } diff --git a/web/includes/Monitor.php b/web/includes/Monitor.php index b1559293c09..ce4ceb8ae09 100644 --- a/web/includes/Monitor.php +++ b/web/includes/Monitor.php @@ -516,6 +516,7 @@ public function __call($fn, array $args) { } # end if this->Id return null; } else if (array_key_exists($fn, $this->summary_fields)) { + ensureSummariesFresh(); $sql = 'SELECT * FROM `Event_Summaries` WHERE `MonitorId`=?'; $row = dbFetchOne($sql, NULL, array($this->{'Id'})); if (!$row) { diff --git a/web/includes/database.php b/web/includes/database.php index b0c76612d86..2863d692b54 100644 --- a/web/includes/database.php +++ b/web/includes/database.php @@ -380,6 +380,20 @@ function getTableDescription( $table, $asString=1 ) { return $columns; } +function ensureSummariesFresh($max_age_seconds = 60) { + static $checked = false; + if ($checked) return; + $checked = true; + + $row = dbFetchOne( + "SELECT last_updated FROM Event_Summaries_Metadata WHERE table_name='Event_Summaries'" + ); + if ($row && (time() - strtotime($row['last_updated'])) < $max_age_seconds) { + return; + } + dbQuery("CALL Refresh_Summaries_SWR()"); +} + function db_version() { return dbFetchOne('SELECT VERSION()', 'VERSION()'); } diff --git a/web/skins/classic/views/_monitor_filters.php b/web/skins/classic/views/_monitor_filters.php index 584f5e74a02..86312abc5ec 100644 --- a/web/skins/classic/views/_monitor_filters.php +++ b/web/skins/classic/views/_monitor_filters.php @@ -217,10 +217,12 @@ function buildMonitorsFilters() { $html .= ''; $html .= ''; + ensureSummariesFresh(); + $sqlAll = 'SELECT M.*, S.*, E.* FROM Monitors AS M - LEFT JOIN Monitor_Status AS S ON S.MonitorId=M.Id - LEFT JOIN Event_Summaries AS E ON E.MonitorId=M.Id + LEFT JOIN Monitor_Status AS S ON S.MonitorId=M.Id + LEFT JOIN Event_Summaries AS E ON E.MonitorId=M.Id WHERE M.`Deleted`=false'; $sqlSelected = $sqlAll . ( count($conditions) ? ' AND ' . implode(' AND ', $conditions) : '' ).' ORDER BY Sequence ASC'; $monitors = dbFetchAll($sqlSelected, null, $values);