Keeps the events table from growing without bound.
Analytics is the one table in a PhoenixKit app that grows with traffic rather than with content, so it needs a story for old data from day one. Once an hour this process:
- Rolls up every completed day that doesn't have a
PhoenixKitWebAnalytics.Schemas.DailyStatrow yet — page views, visitors, sessions, bounces, and total session seconds, per site. - Prunes raw events older than
web_analytics_retention_days(365 by default;0disables pruning), in batches, and only for days that were rolled up first.
So the long-range trend line is permanent while the raw rows behind it are
not. What is lost at the retention horizon is the ability to break an old day
down by page or referrer — see PhoenixKitWebAnalytics.Reports for how
reports handle the boundary.
Scheduling
The first run is deliberately a couple of minutes after boot: a host restart should not spend its first seconds deleting rows while it is also serving the post-deploy traffic spike. Runs are skipped entirely while the module is disabled.
Rollup is idempotent — a day is aggregated once, and the upsert makes a repeat run a no-op — so a crash or a restart mid-pass costs nothing.
Summary
Functions
Returns a specification to start this module under a supervisor.
Deletes raw events past the retention horizon, in batches.
Aggregates one day into DailyStat rows, one per site.
Writes DailyStat rows for completed days that don't have one yet.
Runs a rollup + prune pass immediately, in the caller's process.
Asks the running process to do a pass. Returns immediately.
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec prune_old_events() :: non_neg_integer()
Deletes raw events past the retention horizon, in batches.
Returns the number of rows deleted. Days that have not been rolled up yet are left alone — pruning never runs ahead of the aggregation that preserves the trend line.
@spec rollup_day(Date.t()) :: :ok | :error
Aggregates one day into DailyStat rows, one per site.
@spec rollup_pending_days() :: non_neg_integer()
Writes DailyStat rows for completed days that don't have one yet.
Returns the number of days rolled up. At most 60 days are handled per pass, so a first run against a large backlog spreads its work over several hours instead of locking up the database in one go.
@spec run() :: %{rolled_up: non_neg_integer(), pruned: non_neg_integer()}
Runs a rollup + prune pass immediately, in the caller's process.
Returns %{rolled_up: days, pruned: rows}. Used by the admin settings page's
"Run now" action and by tests, which need the work to happen on the sandbox
connection.
@spec run_async() :: :ok
Asks the running process to do a pass. Returns immediately.