feat(education): establish operational independence contracts

This commit is contained in:
2026-07-31 12:54:08 +08:00
parent 428d4e10fd
commit f42ef76527
24 changed files with 740 additions and 20 deletions

View File

@@ -0,0 +1,72 @@
-- EDU-015: durable operational heartbeat and dead-letter observability contracts.
-- These tables contain operational metadata only; dead letters retain fingerprints, never payloads or secrets.
CREATE TABLE education_operational_component (
component_key VARCHAR(100) PRIMARY KEY,
component_type VARCHAR(16) NOT NULL,
instance_id VARCHAR(100) NOT NULL,
status VARCHAR(16) NOT NULL,
last_heartbeat_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
last_success_at TIMESTAMP,
last_failure_at TIMESTAMP,
failure_category VARCHAR(64),
detail VARCHAR(255),
pending_count BIGINT NOT NULL DEFAULT 0,
retry_count BIGINT NOT NULL DEFAULT 0,
dead_letter_count BIGINT NOT NULL DEFAULT 0,
creator VARCHAR(64) DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater VARCHAR(64) DEFAULT '',
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted BOOLEAN NOT NULL DEFAULT false,
CONSTRAINT ck_education_operational_component_type
CHECK (component_type IN ('WORKER', 'SCANNER')),
CONSTRAINT ck_education_operational_component_status
CHECK (status IN ('STARTING', 'UP', 'DEGRADED', 'DOWN')),
CONSTRAINT ck_education_operational_component_counts
CHECK (pending_count >= 0 AND retry_count >= 0 AND dead_letter_count >= 0)
);
COMMENT ON TABLE education_operational_component IS 'EDU-015 durable worker and scanner heartbeat summary';
COMMENT ON COLUMN education_operational_component.detail IS 'Sanitized bounded diagnostic; no payloads, credentials, or personal data';
CREATE INDEX idx_education_operational_component_heartbeat
ON education_operational_component (component_type, last_heartbeat_at);
CREATE TABLE education_dead_letter (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
tenant_id BIGINT NOT NULL,
component_key VARCHAR(100) NOT NULL,
workload_key VARCHAR(128) NOT NULL,
payload_fingerprint VARCHAR(128) NOT NULL,
failure_category VARCHAR(64) NOT NULL,
attempts INTEGER NOT NULL,
status VARCHAR(16) NOT NULL DEFAULT 'OPEN',
first_failed_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
last_failed_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
resolved_at TIMESTAMP,
resolution_note VARCHAR(255),
creator VARCHAR(64) DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater VARCHAR(64) DEFAULT '',
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted BOOLEAN NOT NULL DEFAULT false,
CONSTRAINT fk_education_dead_letter_component
FOREIGN KEY (component_key) REFERENCES education_operational_component(component_key),
CONSTRAINT ck_education_dead_letter_attempts CHECK (attempts > 0),
CONSTRAINT ck_education_dead_letter_status CHECK (status IN ('OPEN', 'REQUEUED', 'RESOLVED')),
CONSTRAINT ck_education_dead_letter_resolution CHECK (
(status = 'OPEN' AND resolved_at IS NULL) OR
(status IN ('REQUEUED', 'RESOLVED') AND resolved_at IS NOT NULL))
);
COMMENT ON TABLE education_dead_letter IS 'EDU-015 tenant-scoped dead-letter observability without retained business payload';
COMMENT ON COLUMN education_dead_letter.payload_fingerprint IS 'Stable digest used for correlation and duplicate-safe handling; not the payload';
CREATE UNIQUE INDEX uk_education_dead_letter_open_workload
ON education_dead_letter (tenant_id, component_key, workload_key)
WHERE status = 'OPEN' AND deleted = false;
CREATE INDEX idx_education_dead_letter_status_failure
ON education_dead_letter (status, last_failed_at, id);
CREATE INDEX idx_education_dead_letter_tenant_component
ON education_dead_letter (tenant_id, component_key, status, id);