Operations
Zwei Features für Background-Arbeit und gradueller Rollout neuer Features.
jobs
Status: ✅ Stable
Wofür: Background-Jobs (Email-Versand, periodische Aufräumarbeiten,
Webhooks, Re-Indexing). Build auf BullMQ + Redis, mit
Lane-Routing — du markierst pro Job, ob er in der api-Lane oder
in einer separaten worker-Lane laufen soll, der Process-Topology
folgt.
Wie es funktioniert: r.job({ id: "send-email", runIn: "worker", handler: async (ctx, payload) => { ... } }) registriert einen Job-
Handler. Trigger-Wege:
- Manuell:
await ctx.jobs.enqueue("send-email", { ... }) - Cron:
r.job({ ..., schedule: "0 9 * * *" })läuft täglich 9 Uhr - Event-getrieben:
r.job({ ..., on: "incident:created" })reagiert auf Domain-Events
runIn: "api" läuft im selben Bun-Prozess wie HTTP (gut für lokale
Tests), runIn: "worker" braucht separate Worker-Container —
Production-Topology mit horizontaler Skalierung.
Logs: Jeder Run wird in job_runs + job_run_logs festgehalten
mit Status (succeeded/failed/retrying). Eingebaute UI-Page
/admin/jobs zeigt die letzten Runs, Re-Run-Button für failed jobs.
Beispiel:
// Cron-Job: nightly cleanupr.job({ id: "cleanup-old-attempts", runIn: "worker", schedule: "0 3 * * *", // täglich 3 Uhr morgens handler: async (ctx) => { await ctx.db.execute(sql` DELETE FROM delivery_attempts WHERE created_at < NOW() - INTERVAL '30 days' `); },});
// Aus einem Write-Handler manuell triggernawait ctx.jobs.enqueue("send-incident-email", { incidentId: id, recipients: subscribers,});feature-toggles
Status: ✅ Stable
Wofür: Per-Tenant Feature-Flags. Operator kann eine neue Feature für einen Tenant freischalten ohne Deploy, oder ein experimentelles Verhalten gradual rausrollen.
Wie es funktioniert: Du deklarierst r.toggleable({ id: "new-billing", default: false }). Im Code:
if (await ctx.toggles.isOn("new-billing")) { ... }. Operator schaltet
über feature_toggle_set-Events oder einen Admin-Endpoint, der Cache
invalidiert pro Tenant. State liegt in global_feature_state (für
Plattform-Defaults) und in einer Per-Tenant-Override-Tabelle.
Best Practice: Toggle-Tod ist real — wenn ein Toggle 100% ausgerollt ist, lösch ihn aus dem Code. Toggles in Tests immer explizit setzen (siehe Coding-Standards: „Feature Flags in Tests explizit setzen/zurücksetzen”), nie auf Defaults verlassen.
Beispiel:
export const billingFeature = defineFeature("billing", (r) => { r.toggleable({ id: "new-billing-pipeline", default: false });
r.queryHandler({ qn: "billing:invoice:list", handler: async (ctx) => { if (await ctx.toggles.isOn("new-billing-pipeline")) { return ctx.db.list("invoice_v2"); } return ctx.db.list("invoice_v1"); }, });});Siehe auch
- Übersicht aller Bundled-Features
- Audit & Sicherheit
- Recipe
lane-routing— Jobs auf api/worker pinnen