docs(03-02): complete wire postgresql support plan

- Add 03-02-SUMMARY.md with execution results
- Update STATE.md: advance plan, record metrics, add decisions, update session
- Update ROADMAP.md: phase 03 complete (2/2 plans, all summaries present)
- Update REQUIREMENTS.md: mark DB-02 complete
This commit is contained in:
2026-03-24 09:14:50 +01:00
parent f611545ae5
commit cf788930e0
4 changed files with 102 additions and 10 deletions

View File

@@ -0,0 +1,88 @@
---
phase: 03-postgresql-support
plan: "02"
subsystem: wiring
tags: [postgresql, sqlite, database, docker-compose, branching]
dependency_graph:
requires: [03-01]
provides: [DATABASE_URL branching, postgres docker profile, NewTestPostgresServer]
affects: [cmd/diunwebhook/main.go, compose.yml, compose.dev.yml, pkg/diunwebhook/diunwebhook.go]
tech_stack:
added: []
patterns: [DATABASE_URL env var branching, Docker Compose profiles, build-tagged test helpers]
key_files:
created:
- pkg/diunwebhook/postgres_test.go
modified:
- cmd/diunwebhook/main.go
- pkg/diunwebhook/diunwebhook.go
- compose.yml
- compose.dev.yml
decisions:
- "DATABASE_URL present activates PostgreSQL path; absent falls back to SQLite with DB_PATH"
- "postgres Docker service uses profiles: [postgres] so default compose up remains SQLite-only"
- "UNIQUE detection uses strings.ToLower for case-insensitive matching across SQLite and PostgreSQL"
- "Build tag //go:build postgres gates postgres_test.go so standard test runs have no pgx dependency"
metrics:
duration: "~2 minutes"
completed: "2026-03-24T08:13:21Z"
tasks_completed: 2
files_changed: 5
---
# Phase 03 Plan 02: Wire PostgreSQL Support and Deployment Infrastructure Summary
DATABASE_URL branching in main.go routes to PostgresStore or SQLiteStore at startup; Docker Compose postgres profile enables optional PostgreSQL; build-tagged test helper and cross-dialect UNIQUE detection complete the integration.
## What Was Built
### Updated main.go (cmd/diunwebhook/main.go)
- `DATABASE_URL` env var check: when set, opens pgx connection, runs `RunPostgresMigrations`, creates `NewPostgresStore`, logs `"Using PostgreSQL database"`
- When absent: existing SQLite path using `RunSQLiteMigrations` (renamed in Plan 01), `NewSQLiteStore`, logs `"Using SQLite database at {path}"`
- Blank import `_ "github.com/jackc/pgx/v5/stdlib"` registers the `"pgx"` driver name
- All route wiring and graceful shutdown logic unchanged
### Cross-dialect UNIQUE detection (pkg/diunwebhook/diunwebhook.go)
- `TagsHandler` now uses `strings.Contains(strings.ToLower(err.Error()), "unique")` for 409 Conflict detection
- SQLite errors: `UNIQUE constraint failed: tags.name` (uppercase UNIQUE)
- PostgreSQL errors: `duplicate key value violates unique constraint "tags_name_key"` (lowercase unique)
- Both backends now return 409 correctly
### Docker Compose postgres profiles
- `compose.yml`: postgres service added with `profiles: [postgres]`, healthcheck via `pg_isready`, `DATABASE_URL` env var in app service, conditional `depends_on` with `required: false`, `postgres-data` volume
- `compose.dev.yml`: same postgres service with port 5432 exposed on host for direct psql access during development
- Default `docker compose up` (no profile) unchanged — SQLite only, no new services start
### Build-tagged test helper (pkg/diunwebhook/postgres_test.go)
- `//go:build postgres` tag — only compiled with `go test -tags postgres`
- `NewTestPostgresServer()` constructs a `*Server` backed by PostgreSQL using `TEST_DATABASE_URL` env var (defaults to `postgres://diun:diun@localhost:5432/diundashboard_test?sslmode=disable`)
- Calls `RunPostgresMigrations` and `NewPostgresStore` — mirrors the production startup path
## Decisions Made
| Decision | Rationale |
|----------|-----------|
| DATABASE_URL presence-check (not a separate DB_DRIVER var) | Simpler UX; empty string = SQLite, any value = PostgreSQL |
| profiles: [postgres] in compose files | Standard Docker Compose pattern for optional services; default deploy unchanged |
| required: false in depends_on | App can start without postgres service (SQLite fallback); Docker Compose v2.20+ required |
| //go:build postgres tag on test helper | Prevents pgx import at test time for standard `go test ./...` runs; explicit opt-in |
| strings.ToLower for UNIQUE check | SQLite and PostgreSQL use different cases in constraint error messages |
## Deviations from Plan
None — plan executed exactly as written. The `export_test.go` rename (RunMigrations -> RunSQLiteMigrations) was already completed as a deviation in Plan 01, as noted in the objective.
## Verification Results
- `go build ./...` exits 0
- `go test -count=1 ./pkg/diunwebhook/` passes (all 20+ SQLite tests, postgres_test.go skipped)
- `docker compose config` validates without errors
- `docker compose --profile postgres config` shows postgres service
- `grep -c "DATABASE_URL" cmd/diunwebhook/main.go` returns 1
- `grep "strings.ToLower" pkg/diunwebhook/diunwebhook.go` shows case-insensitive UNIQUE check
## Known Stubs
None — this plan wires implementation code, no UI stubs.
## Self-Check: PASSED