go-htmx-gsd #1

Merged
arthur merged 558 commits from go-htmx-gsd into main 2026-05-23 15:16:44 +00:00
2 changed files with 62 additions and 7 deletions
Showing only changes of commit e202ad3a9e - Show all commits

View file

@ -66,7 +66,7 @@ identical. If you don't have podman:
backend/
cmd/
web/main.go # HTTP server entry point
worker/main.go # background worker (skeleton — boot/log/shutdown only)
worker/main.go # background worker — river periodic jobs (Phase 6)
internal/
db/ # pgxpool wiring + sqlc-generated queries
web/ # chi router, handlers, middleware, design-system
@ -123,17 +123,61 @@ Every command in this table is a recipe in `backend/justfile`.
| `just build` | Generates assets, then builds `bin/web` and `bin/worker` | Producing release binaries locally |
| `just clean` | Removes `bin/`, `tmp/`, `static/htmx.min.js`, `static/tailwind.css`, and `*_templ.go` files | Reset to a fresh-clone state without dropping the Postgres volume |
## Worker (skeleton — Phase 1 only)
## Running the Worker
`cmd/worker` in Phase 1 boots, logs `worker ready`, and idles waiting for a
signal. Real job runtime lands in Phase 6 (D-03). To run it manually:
`cmd/worker` is the background job processor. It runs river periodic jobs against
the same Postgres as `cmd/web`. Start it with:
```
DATABASE_URL=postgres://xtablo:xtablo@localhost:5432/xtablo?sslmode=disable \
go run ./cmd/worker
just worker
```
Ctrl-C to exit.
This requires `just db-up` (handled automatically as a dependency) and MinIO
running (used by the orphan-file cleanup job). If MinIO is not running, the worker
will exit on startup with "file store init failed".
### What to expect
- Structured logs appear immediately at startup.
- A `"worker ready"` log line appears within a few seconds after `rivermigrate`
and S3 init complete.
- A `"worker heartbeat"` log line appears almost immediately (the heartbeat job
is configured with `RunOnStart: true`, so it fires on the first scheduler tick
which happens within seconds of startup).
- Subsequent heartbeat logs appear every ~1 minute.
- The orphan-file cleanup job runs every hour (no `RunOnStart` — first run is
~1 hour after startup).
### Single-worker constraint
**Run only one worker process at a time (v1).** River uses advisory locks for
leader election and concurrent rivermigrate runs are unsafe. Do not run multiple
worker instances against the same database in this version.
### Graceful shutdown
Send SIGINT (Ctrl+C) and observe:
```
{"level":"INFO","msg":"shutting down"}
{"level":"INFO","msg":"shutdown complete"}
```
The worker calls `riverClient.StopAndCancel` with a 10-second timeout, which
cancels in-flight job contexts and waits for goroutines to exit before closing
the pool.
### Observing failed job retries
River logs each failure via the `SlogErrorHandler`. A failed job produces a log
line like:
```
{"level":"ERROR","msg":"job error","job_id":42,"job_kind":"heartbeat","attempt":1,"max_attempts":25,"err":"..."}
```
River retries up to 25 times with exponential backoff (`attempts^4` + jitter).
After 25 failed attempts the job is moved to the discarded state in `river_job`.
## Troubleshooting

View file

@ -112,6 +112,17 @@ dev: db-up
just generate
DATABASE_URL='{{ database_url }}' SESSION_SECRET=191affeb1624de1f0e07bd5cfab14cd655510a24f7e673bd784ea56847890caf air -c .air.toml
# Start the worker binary (development — requires db-up and MinIO running).
worker: db-up
DATABASE_URL='{{ database_url }}' \
S3_ENDPOINT='http://localhost:9000' \
S3_BUCKET='xtablo' \
S3_REGION='us-east-1' \
S3_ACCESS_KEY='minioadmin' \
S3_SECRET_KEY='minioadmin' \
S3_USE_PATH_STYLE='true' \
go run ./cmd/worker
test:
just generate
go test ./...