Task S5T05: gk_loves Activity Trigger¶
Sprint Context¶
- Sprint index: Sprint 5 Task Index
- Tags: database, database-refactor, sprint-5, task-index
Source¶
- DBA source:
task-S5T05.dba.md - Specification source:
task-S5T05.specification.md
Resolved Decision¶
- S5T05 is canonically the
gk_lovesactivity trigger task from the Sprint 5 index. - The stale
stats.outbox_eventsdraft was merge residue from the event-bridge work and is not part of this task. - Love attribution uses the loved GK current country context when available; otherwise only shard and daily counters are updated.
Purpose & Scope¶
Defines the trigger function and trigger attachment on geokrety.gk_loves that maintain love-related counters and rollups across insert, update, and delete operations.
Scope:
stats.entity_counters_shardfor entitygk_lovesstats.daily_activity.loves_countstats.country_daily_stats.loves_countwhen GK country context is resolvable
Out of scope:
- Event bridge or queue emission mechanics
stats.outbox_eventspg_notify
Requirements¶
| ID | Description | MoSCoW |
|---|---|---|
| REQ-580 | Function geokrety.fn_gk_loves_activity() exists |
MUST |
| REQ-581 | Trigger tr_gk_loves_activity is attached to geokrety.gk_loves |
MUST |
| REQ-582 | The trigger fires AFTER INSERT OR UPDATE OR DELETE on geokrety.gk_loves |
MUST |
| REQ-583 | Love changes update stats.entity_counters_shard for entity gk_loves |
MUST |
| REQ-584 | Love changes update stats.daily_activity.loves_count |
MUST |
| REQ-585 | Love changes update stats.country_daily_stats.loves_count using GK country context when available |
MUST |
| REQ-586 | When GK country context is unavailable, country love rollups are skipped without affecting daily totals | MUST |
| REQ-587 | phinx rollback drops the trigger and function cleanly |
MUST |
Acceptance Criteria¶
| # | Criterion | How to Verify |
|---|---|---|
| 1 | Function created in geokrety schema |
\df geokrety.fn_gk_loves_activity |
| 2 | Trigger attached to geokrety.gk_loves |
\d geokrety.gk_loves trigger list |
| 3 | Love insert/update/delete adjusts the gk_loves shard counter |
Verify SUM(cnt) deltas for the entity |
| 4 | Love insert/update/delete adjusts daily_activity.loves_count |
Verify touched day buckets |
| 5 | Country rollup uses GK country context when available | Verify touched country/day buckets |
| 6 | Missing country context skips only the country rollup | Verify daily counters change without country mutation |
| 7 | Rollback drops trigger and function | Both absent after phinx rollback |
Migration File¶
20260310500400_create_gk_loves_counter_trigger.php
Full SQL DDL¶
CREATE OR REPLACE FUNCTION geokrety.fn_gk_loves_activity()
RETURNS TRIGGER
LANGUAGE plpgsql
SECURITY DEFINER
AS $$
BEGIN
-- Maintain `stats.entity_counters_shard` for entity `gk_loves`.
-- Maintain `stats.daily_activity.loves_count` for the touched date bucket(s).
-- Maintain `stats.country_daily_stats.loves_count` only when the loved GK
-- current country context can be resolved from the canonical GK location path.
-- UPDATE reconciles OLD then NEW exactly for touched date/country buckets.
RETURN COALESCE(NEW, OLD);
END;
$$;
CREATE TRIGGER tr_gk_loves_activity
AFTER INSERT OR UPDATE OR DELETE ON geokrety.gk_loves
FOR EACH ROW
EXECUTE FUNCTION geokrety.fn_gk_loves_activity();
Full Phinx Migration PHP Code¶
<?php
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
final class CreateGkLovesCounterTrigger extends AbstractMigration
{
public function up(): void
{
$this->execute(<<<'SQL'
CREATE OR REPLACE FUNCTION geokrety.fn_gk_loves_activity()
RETURNS TRIGGER LANGUAGE plpgsql SECURITY DEFINER
AS $$
BEGIN
RETURN COALESCE(NEW, OLD);
END;
$$;
CREATE TRIGGER tr_gk_loves_activity
AFTER INSERT OR UPDATE OR DELETE ON geokrety.gk_loves
FOR EACH ROW EXECUTE FUNCTION geokrety.fn_gk_loves_activity();
SQL
);
}
public function down(): void
{
$this->execute(<<<'SQL'
DROP TRIGGER IF EXISTS tr_gk_loves_activity ON geokrety.gk_loves;
DROP FUNCTION IF EXISTS geokrety.fn_gk_loves_activity();
SQL
);
}
}
The placeholder function body above must be replaced by the canonical counter-maintenance logic required by REQ-583 through REQ-586.
Canonical Notes¶
gk_lovesis assumed to exist per Q-001 and Q-007.- Country attribution follows Q-005: use the loved GK current country context if resolvable; otherwise skip only the country rollup.
- Event-bridge concerns are handled separately in S5T06.
pgTAP Unit Tests¶
| Test ID | Assertion | Pass Condition |
|---|---|---|
| T-5.5.001 | Function exists | has_function() |
| T-5.5.002 | Trigger exists | has_trigger() |
| T-5.5.003 | gk_loves shard counter updates across INSERT/UPDATE/DELETE |
exact match |
| T-5.5.004 | daily_activity.loves_count updates across INSERT/UPDATE/DELETE |
exact match |
| T-5.5.005 | Country rollup follows GK country context rules | exact match |
| T-5.5.006 | Rollback removes trigger and function | pass |
Implementation Checklist¶
- 1. Write
20260310500400_create_gk_loves_counter_trigger.php - 2.
phinx migrate— no errors - 3. Verify trigger and function are attached correctly
- 4. Test insert, update, and delete behavior for shard, daily, and country love rollups
- 5. Run pgTAP T-5.5.001 through T-5.5.006 — all pass
- 6.
phinx rollback— trigger and function gone
Canonical Alignment¶
- The canonical requirement is a
gk_lovestrigger family that updatesstats.entity_counters_shard,stats.daily_activity, andstats.country_daily_statswhere country attribution is available. - Loves remain part of both daily and country rollups.
stats.outbox_eventsis not part of the canonical S5T05 scope.
Agent Loop Log¶
- 2026-03-10T19:20:00Z —
dba: removed the merged outbox-table identity from S5T05 and restored the loves-trigger contract from the sprint index. - 2026-03-10T19:20:00Z —
critical-thinking: separated loves-counter maintenance from move-event bridge concerns so the task boundaries match the Sprint 5 plan. - 2026-03-10T19:20:00Z —
specification: aligned migration naming, scope, and attribution rules with Q-001, Q-005, Q-007, and the Sprint 5 index.
Resolution¶
Q-031 is resolved by canonizing S5T05 as the gk_loves activity trigger task only.
Objects Created¶
| Object Type | Name | Schema |
|---|---|---|
| Function | fn_gk_loves_activity() |
geokrety |
| Trigger | tr_gk_loves_activity |
geokrety.gk_loves |
Country Attribution Rule¶
Country love rollups use the GeoKret current country context available at the time of the love event. When the loved GK has no resolvable country context, the trigger still updates shard and daily counters but skips stats.country_daily_stats.
Security Note¶
The trigger should run with only the privileges required to update the canonical stats tables.