Add pg-boss background job queue with session expiry

Add @trails-cool/jobs package wrapping pg-boss for durable background job
execution using the existing PostgreSQL database. Wire up planner session
expiry as the first scheduled job (hourly, 7-day TTL) — this was previously
dead code that never ran. Add placeholder worker to journal for future
Komoot import and federation jobs.

Infrastructure: grant grafana_reader access to pgboss schema, add job queue
health panels to Service Health dashboard, add failed job alert.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ullrich Schäfer 2026-04-13 21:17:24 +02:00
parent fc80e66ad4
commit 32c5fbde8f
25 changed files with 614 additions and 6 deletions

View file

@ -479,6 +479,69 @@
"legendFormat": "in-flight"
}
]
},
{
"title": "Job Queue \u2014 Status",
"description": "Background job counts by state from pg-boss. Queries the pgboss.job table via the PostgreSQL datasource.",
"type": "table",
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 72
},
"targets": [
{
"datasource": {
"type": "postgres",
"uid": "postgres"
},
"rawSql": "SELECT state, count(*) AS count FROM pgboss.job GROUP BY state ORDER BY count DESC",
"format": "table"
}
]
},
{
"title": "Job Queue \u2014 Failed Jobs (last 24h)",
"description": "Recently failed background jobs with error details.",
"type": "table",
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 72
},
"targets": [
{
"datasource": {
"type": "postgres",
"uid": "postgres"
},
"rawSql": "SELECT name, state, createdon, completedon, output::text AS error FROM pgboss.job WHERE state = 'failed' AND completedon > now() - interval '24 hours' ORDER BY completedon DESC LIMIT 20",
"format": "table"
}
]
},
{
"title": "Job Queue \u2014 Completed/hour",
"description": "Background jobs completed per hour over the last 24 hours.",
"type": "timeseries",
"gridPos": {
"h": 8,
"w": 24,
"x": 0,
"y": 80
},
"targets": [
{
"datasource": {
"type": "postgres",
"uid": "postgres"
},
"rawSql": "SELECT date_trunc('hour', completedon) AS time, name, count(*) AS completed FROM pgboss.job WHERE state = 'completed' AND completedon > now() - interval '24 hours' GROUP BY 1, 2 ORDER BY 1",
"format": "time_series"
}
]
}
]
}

View file

@ -152,6 +152,36 @@ groups:
annotations:
summary: "Crash signature detected in application logs"
- uid: background-job-failures
title: Background job failures
condition: C
noDataState: OK
data:
- refId: A
relativeTimeRange: { from: 3600, to: 0 }
datasourceUid: postgres
model:
rawSql: "SELECT count(*) AS failed FROM pgboss.job WHERE state = 'failed' AND completedon > now() - interval '1 hour'"
format: table
- refId: B
datasourceUid: __expr__
model:
type: reduce
expression: A
reducer: last
- refId: C
datasourceUid: __expr__
model:
type: threshold
expression: B
conditions:
- evaluator: { params: [0], type: gt }
operator: { type: and }
reducer: { type: last }
for: 0s
annotations:
summary: "Background jobs have failed in the last hour — check Grafana Service Health dashboard"
- uid: caddy-502-rate
title: Caddy 502 errors detected
condition: B

View file

@ -31,5 +31,10 @@ BEGIN
GRANT SELECT ON ALL TABLES IN SCHEMA journal TO grafana_reader;
ALTER DEFAULT PRIVILEGES IN SCHEMA journal GRANT SELECT ON TABLES TO grafana_reader;
END IF;
IF EXISTS (SELECT 1 FROM information_schema.schemata WHERE schema_name = 'pgboss') THEN
GRANT USAGE ON SCHEMA pgboss TO grafana_reader;
GRANT SELECT ON ALL TABLES IN SCHEMA pgboss TO grafana_reader;
ALTER DEFAULT PRIVILEGES IN SCHEMA pgboss GRANT SELECT ON TABLES TO grafana_reader;
END IF;
END
$$;