From 0b51da86d1ec134086d756bdce066d666df01f67 Mon Sep 17 00:00:00 2001 From: brontolosone <177225737+brontolosone@users.noreply.github.com> Date: Sat, 25 Oct 2025 12:02:55 +0000 Subject: [PATCH 1/2] Event counter migrations, towards getodk/central#1439 --- ...1025-01-auditlog-eventcounters-01.down.sql | 9 +++ ...251025-01-auditlog-eventcounters-01.up.sql | 8 +++ ...1025-01-auditlog-eventcounters-02.down.sql | 1 + ...251025-01-auditlog-eventcounters-02.up.sql | 59 +++++++++++++++++++ .../20251025-01-auditlog-eventcounters.js | 1 + 5 files changed, 78 insertions(+) create mode 100644 lib/model/migrations/20251025-01-auditlog-eventcounters-01.down.sql create mode 100644 lib/model/migrations/20251025-01-auditlog-eventcounters-01.up.sql create mode 100644 lib/model/migrations/20251025-01-auditlog-eventcounters-02.down.sql create mode 100644 lib/model/migrations/20251025-01-auditlog-eventcounters-02.up.sql create mode 120000 lib/model/migrations/20251025-01-auditlog-eventcounters.js diff --git a/lib/model/migrations/20251025-01-auditlog-eventcounters-01.down.sql b/lib/model/migrations/20251025-01-auditlog-eventcounters-01.down.sql new file mode 100644 index 000000000..8a35b2652 --- /dev/null +++ b/lib/model/migrations/20251025-01-auditlog-eventcounters-01.down.sql @@ -0,0 +1,9 @@ + +--- drop: eventcounter_bump_triggerfunction --- +DROP FUNCTION IF EXISTS "public"."eventcounter_bump_triggerfunction"() CASCADE; + +--- drop: eventcounter_squash(actee_id character varying) --- +DROP FUNCTION IF EXISTS "public"."eventcounter_squash"(actee_id character varying) CASCADE; + +--- drop: public.audits.eventcounter_bump_trigger --- +DROP TRIGGER IF EXISTS eventcounter_bump_trigger ON "public"."audits" CASCADE; diff --git a/lib/model/migrations/20251025-01-auditlog-eventcounters-01.up.sql b/lib/model/migrations/20251025-01-auditlog-eventcounters-01.up.sql new file mode 100644 index 000000000..0985fec7b --- /dev/null +++ b/lib/model/migrations/20251025-01-auditlog-eventcounters-01.up.sql @@ -0,0 +1,8 @@ +CREATE TABLE eventcounters ( + "acteeId" varchar(36) REFERENCES actees (id) ON DELETE CASCADE NOT NULL, + evt_count integer NOT NULL DEFAULT 1 +); + +-- Embedding the evt_count into the index makes the aggregate sum(evt_count) for a given actor +-- possible with an index-only scan. +CREATE INDEX idx_eventcounters ON eventcounters USING btree ("acteeId") INCLUDE (evt_count); diff --git a/lib/model/migrations/20251025-01-auditlog-eventcounters-02.down.sql b/lib/model/migrations/20251025-01-auditlog-eventcounters-02.down.sql new file mode 100644 index 000000000..ef99ae02b --- /dev/null +++ b/lib/model/migrations/20251025-01-auditlog-eventcounters-02.down.sql @@ -0,0 +1 @@ +DROP TABLE eventcounters; diff --git a/lib/model/migrations/20251025-01-auditlog-eventcounters-02.up.sql b/lib/model/migrations/20251025-01-auditlog-eventcounters-02.up.sql new file mode 100644 index 000000000..6bc5e5f3d --- /dev/null +++ b/lib/model/migrations/20251025-01-auditlog-eventcounters-02.up.sql @@ -0,0 +1,59 @@ + +--- create: eventcounter_squash(actee_id varchar(36)) --- +CREATE FUNCTION "public"."eventcounter_squash"(actee_id varchar(36)) +RETURNS integer +AS + $BODY$ + WITH deleted AS ( + DELETE FROM eventcounters WHERE "acteeId" = actee_id RETURNING evt_count + ) + INSERT INTO eventcounters ("acteeId", evt_count) VALUES (actee_id, (SELECT COALESCE(SUM(evt_count), 0) FROM deleted)) RETURNING evt_count + $BODY$ +LANGUAGE sql +VOLATILE +STRICT +PARALLEL UNSAFE +; + +--- sign: eventcounter_squash(actee_id varchar(36)) --- +COMMENT ON FUNCTION "public"."eventcounter_squash"(actee_id varchar(36)) IS '{"dbsamizdat": {"version": 1, "definition_hash": "b264d1502e124c331ad7d11b20b8fca2"}}'; + +--- create: eventcounter_bump_triggerfunction --- +CREATE FUNCTION "public"."eventcounter_bump_triggerfunction"() +RETURNS trigger +AS + $BODY$ + BEGIN + INSERT INTO eventcounters ("acteeId") VALUES (NEW."acteeId"); + IF + (random() < 0.01) + THEN + PERFORM eventcounter_squash(NEW."acteeId"); + END IF; + RETURN NULL; + END; + $BODY$ +LANGUAGE plpgsql +VOLATILE +STRICT +PARALLEL UNSAFE +; + +--- sign: eventcounter_bump_triggerfunction --- +COMMENT ON FUNCTION "public"."eventcounter_bump_triggerfunction"() IS '{"dbsamizdat": {"version": 1, "definition_hash": "2ddd8bde4e781812dd87338f21b024e8"}}'; + +--- create: public.audits.eventcounter_bump_trigger --- + CREATE TRIGGER "eventcounter_bump_trigger" + AFTER INSERT OR UPDATE OF processed +ON "public"."audits" + FOR EACH ROW + WHEN ( + (NEW."acteeId" IS NOT NULL) + AND + (NEW.action = ANY(ARRAY['form.update.publish', 'submission.create', 'submission.update', 'submission.update.version', 'submission.attachment.update', 'submission.delete', 'submission.restore', 'dataset.update', 'dataset.update.publish', 'entity.create', 'entity.update.version', 'entity.update.resolve', 'entity.delete', 'entity.restore', 'entity.bulk.delete', 'entity.bulk.restore'])) + ) + EXECUTE PROCEDURE eventcounter_bump_triggerfunction() + ; + +--- sign: public.audits.eventcounter_bump_trigger --- +COMMENT ON TRIGGER "eventcounter_bump_trigger" ON "public"."audits" IS '{"dbsamizdat": {"version": 1, "definition_hash": "45fddf42c072bc6d04104bbaed5ac120"}}'; diff --git a/lib/model/migrations/20251025-01-auditlog-eventcounters.js b/lib/model/migrations/20251025-01-auditlog-eventcounters.js new file mode 120000 index 000000000..e09c67a2e --- /dev/null +++ b/lib/model/migrations/20251025-01-auditlog-eventcounters.js @@ -0,0 +1 @@ +../pure-sql-migration.js \ No newline at end of file From 6d106ca74aa97bdaa51ef97696a81f9aee8d4a74 Mon Sep 17 00:00:00 2001 From: brontolosone <177225737+brontolosone@users.noreply.github.com> Date: Mon, 3 Nov 2025 09:59:04 +0000 Subject: [PATCH 2/2] update used migration machinery for #1666 --- .../migrations/20251025-01-auditlog-eventcounters.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) mode change 120000 => 100644 lib/model/migrations/20251025-01-auditlog-eventcounters.js diff --git a/lib/model/migrations/20251025-01-auditlog-eventcounters.js b/lib/model/migrations/20251025-01-auditlog-eventcounters.js deleted file mode 120000 index e09c67a2e..000000000 --- a/lib/model/migrations/20251025-01-auditlog-eventcounters.js +++ /dev/null @@ -1 +0,0 @@ -../pure-sql-migration.js \ No newline at end of file diff --git a/lib/model/migrations/20251025-01-auditlog-eventcounters.js b/lib/model/migrations/20251025-01-auditlog-eventcounters.js new file mode 100644 index 000000000..1a47cf703 --- /dev/null +++ b/lib/model/migrations/20251025-01-auditlog-eventcounters.js @@ -0,0 +1,10 @@ +// Copyright 2025 ODK Central Developers +// See the NOTICE file at the top-level directory of this distribution and at +// https://github.com/getodk/central-backend/blob/master/NOTICE. +// This file is part of ODK Central. It is subject to the license terms in +// the LICENSE file found in the top-level directory of this distribution and at +// https://www.apache.org/licenses/LICENSE-2.0. No part of ODK Central, +// including this file, may be copied, modified, propagated, or distributed +// except according to the terms contained in the LICENSE file. + +module.exports = require('../pure-sql-migration')(__filename);