v0.0.9
Event usage metering
You can now see how many events an org has sent this month. Pug counts them on a schedule and stores the totals, so the dashboard reads a single stored number instead of running an analytical query every time someone opens the page. Alongside the headline total you get a per-project daily breakdown, and the whole team can see it — viewers included, since the person who notices a spike is rarely an admin. (#69)
This is counting only. There are no plans, quotas or limits, nothing is billed, and no event is ever rejected, throttled or delayed because of a count — ingestion never looks at these numbers.
Setting it up — please read
The counting does not start on its own. Neither pug server nor any worker schedules it. Upgrade and stop there and usage reads as unknown forever, not as a number.
Run the new cron-usage image on a schedule — hourly is what it is designed for:
apiVersion: batch/v1
kind: CronJob
metadata:
name: pug-cron-usage
spec:
schedule: "0 * * * *"
# The pass takes a Postgres advisory lock, so an overrun cannot double-count.
concurrencyPolicy: Forbid
startingDeadlineSeconds: 600
jobTemplate:
spec:
backoffLimit: 1
# Backstop for a pod wedged past the pass's own 30m timeout.
activeDeadlineSeconds: 3000
template:
spec:
restartPolicy: Never
containers:
- name: pug-cron-usage
image: ghcr.io/pug-sh/pug-cron-usage:0.0.9
# Reads ClickHouse, writes Postgres. No NATS, no app secrets.
envFrom:
- configMapRef: { name: pug-config }
- secretRef: { name: pug-db }It needs the same database configuration as the migrate jobs and nothing else. Each run recounts the last couple of days to pick up late-arriving events, re-totals every org's current month, and exits; once a day it widens to recount the whole month. Overlapping runs are safe, and a failed run exits non-zero so your scheduler shows it as failed.
PUG_USAGE_RESCAN_DAYS (default 2) controls how many trailing days each run recounts. That is the only setting.
Reading the number
The API deliberately distinguishes "never counted" from "counted, and it really is zero". If the job has never run, the timestamp comes back absent and the count is omitted entirely — render that as unknown, never as 0. There is a third state too: just after a month rolls over, you get a timestamp with no count yet, which means "counting".
Usage is only as fresh as the schedule you chose, so show the last-updated timestamp beside the number rather than implying it is live. Everything is counted in UTC, which can differ at the edges from a project's own reporting timezone in insights — they answer different questions.
Counts move down as well as up: erasing a data subject's events lowers the total on the next run rather than leaving it frozen. And a run that cannot trust what it read leaves the timestamp to go stale instead of writing a number it never verified, so a stale timestamp is the signal worth alerting on.
Upgrading
- One new migration, applied by the usual
migrate-postgresstep. It adds new tables only — nothing existing changes shape. - No breaking changes. No API or wire changes to anything that already existed, and no dashboard changes are required to upgrade.
- Metering is optional. Skip the scheduled job entirely and everything else works exactly as before.
docs/architecture/usage.md is the full reference, including what is counted and the known imprecisions.