Monthly partitions for pixelex_events: create them ahead of time, drop them
when they age out.
Why this has to be scheduled
A range-partitioned table with no partition covering now() rejects every
insert. Nothing degrades gracefully — ingestion simply stops at midnight on
the first of the month. So ensure/1 runs ahead, and it must keep running:
# with Oban
config :my_app, Oban,
plugins: [{Oban.Plugins.Cron, crontab: [{"0 3 * * *", Pixelex.Partitions.Worker}]}]ensure/1 is idempotent, so running it hourly is fine and running it on boot
is a reasonable belt-and-braces.
Retention
drop_expired/1 drops whole partitions whose entire range is older than the
cutoff. DELETE FROM pixelex_events WHERE occurred_at < … would do the same
thing by rewriting the largest table in the system and leaving the space to
vacuum; DROP TABLE on a partition returns it to the filesystem immediately.
The cost is granularity: a partition survives until its newest row is
expired, so with 90-day retention rows live 90–120 days. That is the trade
monthly partitions make. Weekly partitions tighten it at the cost of more
relations; set :partition_period to :week if the policy needs it.
Summary
Functions
Drop every partition whose whole range predates retention_days ago.
Create partitions covering this month and ahead further months.
Partition names with the exclusive upper bound of each range.
Functions
@spec drop_expired(pos_integer() | nil) :: [String.t()]
Drop every partition whose whole range predates retention_days ago.
Returns the names dropped.
@spec ensure(non_neg_integer()) :: :ok
Create partitions covering this month and ahead further months.
Idempotent — CREATE TABLE IF NOT EXISTS.
Partition names with the exclusive upper bound of each range.