Status: Accepted Date: 2026-06-09 Authors: Felix T.J. Dietrich Supersedes (partition sub-decision only): ADR 0017
ADR 0017 introduced auth_event, a monthly RANGE-partitioned audit log, and — to keep it running on
stock Postgres with no extension — managed its partitions with a bespoke Java bean
(AuthEventPartitionManager): a @Scheduled + ShedLock job plus a Liquibase DO block that
hand-created auth_event_pYYYYMM partitions (create-ahead) and DROPped expired ones (12-month
retention), with an auth_event_default catch-all.
That was ~280 lines of bespoke DDL re-implementing what pg_partman — the de-facto OSS standard —
does. The "no extension" constraint held only because we did not control the Postgres image. We now
do: every environment can run a custom image.
Adopt pg_partman 5.x for auth_event, and delete AuthEventPartitionManager.
- Image. One thin Debian image,
ghcr.io/ls1intum/hephaestus/postgres(docker/postgres/Dockerfile,postgres:17-bookworm+ pinnedpostgresql-17-partman), used identically in dev, preview, and prod. This also retires preview's inconsistentpostgres:17-alpine(Alpine has nopg_partmanpackage). - Postgres major: 17 (GA, supported to Nov 2029; preview already ran it). 18 is a separate, later decision — not bundled with "new extension + new image + manager deletion".
- Definition. Liquibase changeset
1780825201546-18runspartman.create_parentonauth_event(monthly RANGE,premake=2) and setspart_configretention to 12 months with hardDROP. - Scheduling: in-app
CALL partman.run_maintenance_proc()from a ShedLock'd, server-role@Scheduledbean (AuthEventPartitionMaintenance) — not thepg_partman_bgwbackground worker and notpg_cron. This deliberately avoidsshared_preload_libraries: the BGW must be preloaded before the firstCREATE EXTENSIONor maintenance silently never runs. Keeping maintenance as plain SQL in the app's own scheduler keeps the image "stock Postgres + one extension", reuses the proven scheduler pattern, and keeps maintenance observable in app logs.
- Partitions are now named
auth_event_pYYYYMMDD(pg_partman's convention) and the default partition is partman-owned. The JPA-vs-DB drift gate already excludesauth_event_default/auth_event_p\d+; pg_partman's own objects live in thepartmanschema and are not in the diffedpublicschema, so no new exclusions are required. The ERD generator already filters partition children + non-publicschemas, so the diagram is unchanged. - Retention's
DROP TABLEis DDL, so it is unaffected byauth_event'sBEFORE UPDATE OR DELETEimmutability trigger (which only blocks row mutation). - Greenfield: this lands in the unmerged ADR-0017 branch, so there is no data to migrate — the
bespoke partitions/manager are simply replaced. Reversibility:
AuthEventPartitionManagerremains in git history, and a misbehaving partman still accepts writes via the default partition.
Built the image locally; applied the full changelog via liquibase:update; confirmed the extension
loads, create_parent builds current+2 ahead with a default, an insert lands in a real monthly child
(not the default), and run_maintenance_proc() runs — all with no shared_preload_libraries.