feat(recoverable-job): add findings
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
-- ============================================================================
|
||||
-- Slice 7 of Part 2 — persistent finding storage.
|
||||
--
|
||||
-- Findings from consistency jobs (and eventually storage_migration failure
|
||||
-- rows, reextract failures) currently flow into `tracing::warn!` targeted
|
||||
-- at `oxicloud::consistency`. That works for live tailing but rotates
|
||||
-- away — an operator opening the admin UI a day after a run has nothing
|
||||
-- to drill into. This table makes findings first-class + queryable.
|
||||
--
|
||||
-- `run_findings` is INTENTIONALLY generic. The consistency-check plan and
|
||||
-- the plan doc's tenant table both list `kind` + `severity` + `resource_id`
|
||||
-- + `detail` as the union of what every current tenant needs. Adding a
|
||||
-- tenant with a novel per-finding field means widening `detail`
|
||||
-- (JSONB, per-tenant shape), not adding a column.
|
||||
--
|
||||
-- Cascade rule: findings live and die with their parent run. Deleting a
|
||||
-- terminal run row (retention pruning, planned) also drops its findings.
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE jobs.run_findings (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
run_id UUID NOT NULL
|
||||
REFERENCES jobs.recoverable_runs(id)
|
||||
ON DELETE CASCADE,
|
||||
-- Machine-readable kind (e.g. 'stale_used_bytes', 'missing_blob').
|
||||
-- Stable across releases per the audit-log convention — see
|
||||
-- feedback memory `enum_over_string_literals_in_logs`. New failure
|
||||
-- mode = new value, never repurpose an existing one.
|
||||
kind TEXT NOT NULL,
|
||||
-- Severity spectrum:
|
||||
-- 'data_loss' — bytes / rows unreachable or gone.
|
||||
-- 'inconsistent' — counters / materialised values wrong,
|
||||
-- content intact.
|
||||
-- 'anomaly' — surprising state worth surfacing, no known impact.
|
||||
-- TEXT (not ENUM) so tenants can grow the vocabulary without a schema
|
||||
-- migration; the app-layer types.rs is where the canonical set lives.
|
||||
severity TEXT NOT NULL,
|
||||
-- Nullable — some findings pertain to the run as a whole
|
||||
-- (e.g. "backend enumeration truncated after 1M keys") rather than
|
||||
-- one specific resource.
|
||||
resource_id UUID,
|
||||
-- Per-tenant per-finding structured detail (cached/actual/delta,
|
||||
-- blob_hash, expected/stored path, ...). Consumers key off `kind`
|
||||
-- to know the shape.
|
||||
detail JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Time-ordered listing per run: `GET /api/admin/jobs/{name}/runs/{id}/findings`
|
||||
-- pages by (run_id, created_at) — index-only scan.
|
||||
CREATE INDEX ON jobs.run_findings (run_id, created_at);
|
||||
|
||||
-- Aggregation queries — "how many `missing_blob` findings across all
|
||||
-- runs of `files_consistency`" or "how many `data_loss`-severity
|
||||
-- findings today". Both need `(kind)` and `(severity)` predicates; a
|
||||
-- composite works for either since the leading column is selective.
|
||||
CREATE INDEX ON jobs.run_findings (kind, severity, created_at);
|
||||
|
||||
COMMENT ON TABLE jobs.run_findings IS
|
||||
'Structured per-finding records emitted by recoverable jobs. Replaces '
|
||||
'the transitional `tracing::warn!(event=consistency_finding)` calls '
|
||||
'in the consistency tenants — see docs/plan/job-registry.md Part 2.';
|
||||
Reference in New Issue
Block a user