Track voters and beneficiaries, coordinate staff and volunteers, watch spending, and get clear reports — built around how Philippine campaigns actually run on the ground.
Problem
The Areas page renders a hierarchical rollup (region → province → city → barangay) over the full PSGC tree — 42,010 barangays, ~43,768 nodes. Every load needs population, voters, expenses, and staff counts per visible row, scoped to whatever subset of the tree the logged-in member can access.
The initial implementation computed this live on every request: a BFS walk to resolve the member's access scope, then 5 unindexed GROUP BY queries fanned out across every psgc id in scope. When users spam-refreshed the Areas page and its nested area pages, Postgres CPU spiked to ~800%, since each refresh re-triggered the same expensive tree walk + fan-out from scratch. A materialized-view rewrite didn't fix the root issue: materialized views can't be refreshed for a filtered subset, only the entire relation — one org's data change meant refreshing (or going stale) for every tenant.
Solution
Replaced the live fan-out and the materialized view with a Redis caching layer with targeted, keyed invalidation, sitting in front of a single recursive-CTE rollup query:
ResultRepeated refreshes now hit warm cache instead of recomputing a 42k-node tree walk per request, eliminating the CPU spikes.