Centralize durable schema migration ownership
This commit is contained in:
parent
cc328237f4
commit
069f3813ae
21 changed files with 354 additions and 350 deletions
73
crates/remora-schema/migrations/admission/0001_admission.sql
Normal file
73
crates/remora-schema/migrations/admission/0001_admission.sql
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
CREATE TABLE sensitive_source_inputs (
|
||||
sensitive_source_ref TEXT PRIMARY KEY,
|
||||
source_input JSONB NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE execution_grants (
|
||||
grant_id TEXT PRIMARY KEY,
|
||||
owner_id TEXT NOT NULL,
|
||||
target_store TEXT NOT NULL,
|
||||
target_bucket TEXT NOT NULL,
|
||||
target_prefix JSONB NOT NULL,
|
||||
policy_version TEXT NOT NULL,
|
||||
credential_id TEXT,
|
||||
credential_version_id TEXT,
|
||||
status TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
revoked_at TIMESTAMPTZ,
|
||||
CONSTRAINT execution_grants_status_check CHECK (status IN ('active', 'revoked')),
|
||||
CONSTRAINT execution_grants_revocation_check CHECK (
|
||||
(status = 'active' AND revoked_at IS NULL)
|
||||
OR (status = 'revoked' AND revoked_at IS NOT NULL)
|
||||
),
|
||||
CONSTRAINT execution_grants_credential_check CHECK (
|
||||
(credential_id IS NULL) = (credential_version_id IS NULL)
|
||||
),
|
||||
CONSTRAINT execution_grants_authority_tuple_unique UNIQUE (
|
||||
grant_id,
|
||||
status,
|
||||
credential_id,
|
||||
credential_version_id
|
||||
)
|
||||
);
|
||||
|
||||
ALTER TABLE execution_grant_credential_pins
|
||||
ADD CONSTRAINT execution_grant_pins_authoritative_grant_fk
|
||||
FOREIGN KEY (grant_id, status, credential_id, version_id)
|
||||
REFERENCES execution_grants (
|
||||
grant_id,
|
||||
status,
|
||||
credential_id,
|
||||
credential_version_id
|
||||
)
|
||||
MATCH FULL
|
||||
DEFERRABLE INITIALLY DEFERRED;
|
||||
|
||||
CREATE TABLE jobs (
|
||||
job_id TEXT PRIMARY KEY,
|
||||
owner_id TEXT NOT NULL,
|
||||
label TEXT,
|
||||
revision BIGINT NOT NULL DEFAULT 1 CHECK (revision >= 1),
|
||||
phase TEXT NOT NULL CHECK (phase IN ('queued', 'running', 'retry_wait', 'cancelling', 'succeeded', 'failed', 'cancelled')),
|
||||
lifecycle TEXT NOT NULL CHECK (lifecycle IN ('visible', 'soft_deleted', 'hard_deleting', 'hard_deleted')),
|
||||
source_projection JSONB NOT NULL,
|
||||
sensitive_source_ref TEXT NOT NULL UNIQUE REFERENCES sensitive_source_inputs (sensitive_source_ref),
|
||||
target_store TEXT NOT NULL,
|
||||
target_bucket TEXT NOT NULL,
|
||||
target_prefix JSONB NOT NULL,
|
||||
execution_grant_id TEXT NOT NULL UNIQUE REFERENCES execution_grants (grant_id) DEFERRABLE INITIALLY DEFERRED,
|
||||
dispatch_needed BOOLEAN NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL,
|
||||
CONSTRAINT jobs_label_length CHECK (label IS NULL OR char_length(label) BETWEEN 1 AND 200)
|
||||
);
|
||||
|
||||
ALTER TABLE execution_grants
|
||||
ADD CONSTRAINT execution_grants_job_fk
|
||||
FOREIGN KEY (grant_id)
|
||||
REFERENCES jobs (execution_grant_id)
|
||||
DEFERRABLE INITIALLY DEFERRED;
|
||||
|
||||
CREATE INDEX jobs_owner_created_idx ON jobs (owner_id, created_at DESC, job_id DESC);
|
||||
CREATE INDEX jobs_dispatch_idx ON jobs (created_at, job_id) WHERE dispatch_needed;
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
CREATE TABLE live_event_history_state (
|
||||
singleton BOOLEAN PRIMARY KEY DEFAULT TRUE CHECK (singleton),
|
||||
floor_outbox_id BIGINT NOT NULL DEFAULT 0 CHECK (floor_outbox_id >= 0),
|
||||
progress_poll_not_before TIMESTAMPTZ NOT NULL DEFAULT '-infinity'
|
||||
);
|
||||
|
||||
INSERT INTO live_event_history_state (singleton) VALUES (TRUE);
|
||||
|
||||
CREATE TABLE live_progress_event_state (
|
||||
job_id TEXT PRIMARY KEY REFERENCES jobs (job_id),
|
||||
attempt_id TEXT NOT NULL,
|
||||
fence BIGINT NOT NULL CHECK (fence BETWEEN 1 AND 9007199254740991),
|
||||
progress_present BOOLEAN NOT NULL,
|
||||
completed_bytes BIGINT,
|
||||
total_bytes BIGINT,
|
||||
completed_members BIGINT,
|
||||
total_members BIGINT,
|
||||
updated_at TIMESTAMPTZ NOT NULL,
|
||||
CONSTRAINT live_progress_event_shape_check CHECK (
|
||||
(
|
||||
progress_present
|
||||
AND completed_bytes IS NOT NULL
|
||||
AND completed_members IS NOT NULL
|
||||
)
|
||||
OR (
|
||||
NOT progress_present
|
||||
AND completed_bytes IS NULL
|
||||
AND total_bytes IS NULL
|
||||
AND completed_members IS NULL
|
||||
AND total_members IS NULL
|
||||
)
|
||||
)
|
||||
);
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
CREATE TABLE live_event_tickets (
|
||||
ticket_digest BYTEA PRIMARY KEY CHECK (octet_length(ticket_digest) = 32),
|
||||
principal JSONB NOT NULL,
|
||||
origin TEXT NOT NULL CHECK (origin <> ''),
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
expires_at TIMESTAMPTZ NOT NULL CHECK (expires_at > created_at)
|
||||
);
|
||||
|
||||
CREATE INDEX live_event_tickets_expiry_idx
|
||||
ON live_event_tickets (expires_at);
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
ALTER TABLE jobs
|
||||
ADD COLUMN current_fence BIGINT NOT NULL DEFAULT 0,
|
||||
ADD COLUMN authority_attempt_id TEXT,
|
||||
ADD CONSTRAINT jobs_current_fence_check CHECK (
|
||||
current_fence >= 0 AND current_fence <= 9007199254740991
|
||||
);
|
||||
|
||||
CREATE TABLE attempts (
|
||||
attempt_id TEXT PRIMARY KEY,
|
||||
job_id TEXT NOT NULL REFERENCES jobs (job_id),
|
||||
fence BIGINT NOT NULL CHECK (fence BETWEEN 1 AND 9007199254740991),
|
||||
worker_incarnation TEXT NOT NULL,
|
||||
state TEXT NOT NULL CHECK (state IN ('authorized', 'executing', 'reaped')),
|
||||
initial_progress BIGINT NOT NULL DEFAULT 0 CHECK (initial_progress >= 0),
|
||||
authorized_at TIMESTAMPTZ NOT NULL,
|
||||
executing_at TIMESTAMPTZ,
|
||||
reaped_at TIMESTAMPTZ,
|
||||
CONSTRAINT attempts_state_timestamps_check CHECK (
|
||||
(state = 'authorized' AND executing_at IS NULL AND reaped_at IS NULL)
|
||||
OR (state = 'executing' AND executing_at IS NOT NULL AND reaped_at IS NULL)
|
||||
OR (state = 'reaped' AND executing_at IS NULL AND reaped_at IS NOT NULL)
|
||||
),
|
||||
CONSTRAINT attempts_job_fence_unique UNIQUE (job_id, fence),
|
||||
CONSTRAINT attempts_job_authority_unique UNIQUE (attempt_id, job_id, fence),
|
||||
CONSTRAINT attempts_authority_tuple_unique UNIQUE (
|
||||
attempt_id, job_id, fence, worker_incarnation
|
||||
)
|
||||
);
|
||||
|
||||
ALTER TABLE jobs
|
||||
ADD CONSTRAINT jobs_authority_attempt_fk
|
||||
FOREIGN KEY (authority_attempt_id, job_id, current_fence)
|
||||
REFERENCES attempts (attempt_id, job_id, fence)
|
||||
DEFERRABLE INITIALLY DEFERRED;
|
||||
|
||||
CREATE INDEX attempts_job_authorized_idx
|
||||
ON attempts (job_id, authorized_at)
|
||||
WHERE state = 'authorized';
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
ALTER TABLE attempts
|
||||
DROP CONSTRAINT attempts_state_check,
|
||||
DROP CONSTRAINT attempts_state_timestamps_check,
|
||||
ADD COLUMN stopped_at TIMESTAMPTZ,
|
||||
ADD COLUMN committed_at TIMESTAMPTZ,
|
||||
ADD COLUMN failure JSONB,
|
||||
ADD CONSTRAINT attempts_state_check CHECK (
|
||||
state IN ('authorized', 'executing', 'reaped', 'stopped', 'committed')
|
||||
),
|
||||
ADD CONSTRAINT attempts_state_timestamps_check CHECK (
|
||||
(state = 'authorized' AND executing_at IS NULL AND reaped_at IS NULL
|
||||
AND stopped_at IS NULL AND committed_at IS NULL)
|
||||
OR (state = 'executing' AND executing_at IS NOT NULL AND reaped_at IS NULL
|
||||
AND stopped_at IS NULL AND committed_at IS NULL)
|
||||
OR (state = 'reaped' AND executing_at IS NULL AND reaped_at IS NOT NULL
|
||||
AND stopped_at IS NULL AND committed_at IS NULL)
|
||||
OR (state = 'stopped' AND executing_at IS NOT NULL AND reaped_at IS NULL
|
||||
AND stopped_at IS NOT NULL AND committed_at IS NULL)
|
||||
OR (state = 'committed' AND executing_at IS NOT NULL AND reaped_at IS NULL
|
||||
AND stopped_at IS NULL AND committed_at IS NOT NULL)
|
||||
);
|
||||
|
||||
ALTER TABLE jobs
|
||||
ADD COLUMN cancel_requested BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
ADD COLUMN resolved_source JSONB,
|
||||
ADD COLUMN checkpoint JSONB,
|
||||
ADD COLUMN committed_result JSONB,
|
||||
ADD COLUMN failure JSONB,
|
||||
ADD COLUMN terminal_at TIMESTAMPTZ,
|
||||
ADD CONSTRAINT jobs_terminal_projection_check CHECK (
|
||||
((phase = 'succeeded') = (committed_result IS NOT NULL))
|
||||
AND ((phase = 'failed') = (failure IS NOT NULL))
|
||||
AND ((phase IN ('succeeded', 'failed', 'cancelled')) = (terminal_at IS NOT NULL))
|
||||
AND (committed_result IS NULL OR failure IS NULL)
|
||||
);
|
||||
63
crates/remora-schema/migrations/attempt/0003_recovery.sql
Normal file
63
crates/remora-schema/migrations/attempt/0003_recovery.sql
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
ALTER TABLE jobs
|
||||
ADD COLUMN retry_not_before TIMESTAMPTZ,
|
||||
ADD COLUMN coordination_cleaned_at TIMESTAMPTZ,
|
||||
ADD COLUMN recovery_checked_at TIMESTAMPTZ;
|
||||
|
||||
ALTER TABLE attempts
|
||||
DROP CONSTRAINT attempts_state_timestamps_check,
|
||||
ADD COLUMN worker_down_at TIMESTAMPTZ,
|
||||
ADD CONSTRAINT attempts_state_timestamps_check CHECK (
|
||||
(state = 'authorized' AND executing_at IS NULL AND reaped_at IS NULL
|
||||
AND stopped_at IS NULL AND committed_at IS NULL)
|
||||
OR (state = 'executing' AND executing_at IS NOT NULL AND reaped_at IS NULL
|
||||
AND stopped_at IS NULL AND committed_at IS NULL)
|
||||
OR (state = 'reaped' AND executing_at IS NULL AND reaped_at IS NOT NULL
|
||||
AND stopped_at IS NULL AND committed_at IS NULL)
|
||||
OR (state = 'stopped' AND reaped_at IS NULL
|
||||
AND stopped_at IS NOT NULL AND committed_at IS NULL)
|
||||
OR (state = 'committed' AND executing_at IS NOT NULL AND reaped_at IS NULL
|
||||
AND stopped_at IS NULL AND committed_at IS NOT NULL)
|
||||
);
|
||||
|
||||
CREATE TABLE attempt_staging_cleanup (
|
||||
cleanup_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
attempt_id TEXT NOT NULL REFERENCES attempts (attempt_id),
|
||||
kind TEXT NOT NULL CHECK (kind IN ('object', 'multipart')),
|
||||
target_store TEXT NOT NULL,
|
||||
bucket TEXT NOT NULL,
|
||||
object_key TEXT NOT NULL,
|
||||
version_id TEXT,
|
||||
upload_id TEXT,
|
||||
state TEXT NOT NULL DEFAULT 'pending'
|
||||
CHECK (state IN ('pending', 'claimed', 'complete')),
|
||||
claim_token TEXT,
|
||||
claimed_until TIMESTAMPTZ,
|
||||
failure_count BIGINT NOT NULL DEFAULT 0 CHECK (failure_count >= 0),
|
||||
last_error TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
completed_at TIMESTAMPTZ,
|
||||
CONSTRAINT attempt_staging_cleanup_identity_shape_check CHECK (
|
||||
(kind = 'object' AND upload_id IS NULL)
|
||||
OR (kind = 'multipart' AND version_id IS NULL AND upload_id IS NOT NULL)
|
||||
),
|
||||
CONSTRAINT attempt_staging_cleanup_claim_check CHECK (
|
||||
(state = 'pending' AND claim_token IS NULL AND claimed_until IS NULL
|
||||
AND completed_at IS NULL)
|
||||
OR (state = 'claimed' AND claim_token IS NOT NULL AND claimed_until IS NOT NULL
|
||||
AND completed_at IS NULL)
|
||||
OR (state = 'complete' AND claim_token IS NULL AND claimed_until IS NULL
|
||||
AND completed_at IS NOT NULL)
|
||||
),
|
||||
UNIQUE NULLS NOT DISTINCT (
|
||||
attempt_id, kind, target_store, bucket, object_key, version_id, upload_id
|
||||
)
|
||||
);
|
||||
|
||||
CREATE INDEX attempt_staging_cleanup_claimable_idx
|
||||
ON attempt_staging_cleanup (cleanup_id)
|
||||
WHERE state IN ('pending', 'claimed');
|
||||
|
||||
CREATE INDEX jobs_recovery_idx
|
||||
ON jobs (updated_at, job_id)
|
||||
WHERE phase NOT IN ('succeeded', 'failed', 'cancelled')
|
||||
OR coordination_cleaned_at IS NULL;
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE attempts
|
||||
ADD COLUMN execution_envelope JSONB;
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
ALTER TABLE attempts
|
||||
ADD COLUMN staging_origin_attempt_id TEXT;
|
||||
|
||||
UPDATE attempts
|
||||
SET staging_origin_attempt_id = attempt_id;
|
||||
|
||||
ALTER TABLE attempts
|
||||
ALTER COLUMN staging_origin_attempt_id SET NOT NULL,
|
||||
ADD CONSTRAINT attempts_staging_origin_fk
|
||||
FOREIGN KEY (staging_origin_attempt_id)
|
||||
REFERENCES attempts (attempt_id)
|
||||
DEFERRABLE INITIALLY DEFERRED,
|
||||
ADD CONSTRAINT attempts_id_job_unique UNIQUE (attempt_id, job_id);
|
||||
|
||||
ALTER TABLE jobs
|
||||
ADD COLUMN checkpoint_owner_attempt_id TEXT;
|
||||
|
||||
UPDATE jobs j
|
||||
SET checkpoint_owner_attempt_id = (
|
||||
SELECT a.attempt_id
|
||||
FROM attempts a
|
||||
WHERE a.job_id = j.job_id
|
||||
ORDER BY a.fence DESC
|
||||
LIMIT 1
|
||||
)
|
||||
WHERE j.checkpoint IS NOT NULL;
|
||||
|
||||
ALTER TABLE jobs
|
||||
ADD CONSTRAINT jobs_checkpoint_owner_fk
|
||||
FOREIGN KEY (checkpoint_owner_attempt_id, job_id)
|
||||
REFERENCES attempts (attempt_id, job_id)
|
||||
DEFERRABLE INITIALLY DEFERRED,
|
||||
ADD CONSTRAINT jobs_checkpoint_owner_check CHECK (
|
||||
(checkpoint IS NULL) = (checkpoint_owner_attempt_id IS NULL)
|
||||
);
|
||||
|
||||
CREATE INDEX jobs_checkpoint_owner_idx
|
||||
ON jobs (checkpoint_owner_attempt_id)
|
||||
WHERE checkpoint_owner_attempt_id IS NOT NULL;
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
ALTER TABLE attempt_staging_cleanup
|
||||
DROP CONSTRAINT attempt_staging_cleanup_kind_check,
|
||||
DROP CONSTRAINT attempt_staging_cleanup_identity_shape_check,
|
||||
ADD CONSTRAINT attempt_staging_cleanup_kind_check
|
||||
CHECK (kind IN ('write_intent', 'object', 'multipart')),
|
||||
ADD CONSTRAINT attempt_staging_cleanup_identity_shape_check CHECK (
|
||||
(kind IN ('write_intent', 'object') AND upload_id IS NULL)
|
||||
OR (kind = 'multipart' AND version_id IS NULL AND upload_id IS NOT NULL)
|
||||
);
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
CREATE TABLE remora_schema_state (
|
||||
singleton BOOLEAN PRIMARY KEY DEFAULT TRUE CHECK (singleton),
|
||||
generation BIGINT NOT NULL CHECK (generation > 0),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
INSERT INTO remora_schema_state (singleton, generation)
|
||||
VALUES (TRUE, 1);
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
-- Source credential registry metadata. Secret material lives in Vault KV v2;
|
||||
-- this package only stores owner, kind, status, and opaque version coordinates.
|
||||
|
||||
CREATE TABLE source_credentials (
|
||||
credential_id TEXT PRIMARY KEY,
|
||||
owner_id TEXT NOT NULL,
|
||||
label TEXT NOT NULL,
|
||||
kind TEXT NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
active_version_id TEXT,
|
||||
secret_mount TEXT NOT NULL,
|
||||
secret_path TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL,
|
||||
CONSTRAINT source_credentials_kind_check CHECK (
|
||||
kind IN (
|
||||
'basic_auth',
|
||||
'bearer_token',
|
||||
'http_headers',
|
||||
'object_store',
|
||||
'hugging_face'
|
||||
)
|
||||
),
|
||||
CONSTRAINT source_credentials_status_check CHECK (status IN ('active', 'revoked')),
|
||||
CONSTRAINT source_credentials_label_len CHECK (
|
||||
char_length(label) BETWEEN 1 AND 200
|
||||
),
|
||||
CONSTRAINT source_credentials_active_status CHECK (
|
||||
(status = 'active' AND active_version_id IS NOT NULL)
|
||||
OR (status = 'revoked')
|
||||
)
|
||||
);
|
||||
|
||||
CREATE INDEX source_credentials_owner_created_idx
|
||||
ON source_credentials (owner_id, created_at DESC, credential_id DESC);
|
||||
|
||||
CREATE TABLE source_credential_versions (
|
||||
version_id TEXT PRIMARY KEY,
|
||||
credential_id TEXT NOT NULL REFERENCES source_credentials (credential_id),
|
||||
vault_kv_version BIGINT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
destroyed_at TIMESTAMPTZ,
|
||||
CONSTRAINT source_credential_versions_kv_positive CHECK (vault_kv_version >= 1)
|
||||
);
|
||||
|
||||
CREATE INDEX source_credential_versions_credential_idx
|
||||
ON source_credential_versions (credential_id, created_at DESC);
|
||||
|
||||
ALTER TABLE source_credentials
|
||||
ADD CONSTRAINT source_credentials_active_version_fk
|
||||
FOREIGN KEY (active_version_id)
|
||||
REFERENCES source_credential_versions (version_id)
|
||||
DEFERRABLE INITIALLY DEFERRED;
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
-- Durable intent for Vault secret writes. Secret material is never stored here.
|
||||
-- Intents make create/rotate recoverable across ambiguous PostgreSQL commits
|
||||
-- without best-effort Vault destruction that could erase a committed version.
|
||||
|
||||
CREATE TABLE secret_write_intents (
|
||||
intent_id TEXT PRIMARY KEY,
|
||||
operation TEXT NOT NULL,
|
||||
credential_id TEXT NOT NULL,
|
||||
version_id TEXT NOT NULL,
|
||||
owner_id TEXT,
|
||||
label TEXT,
|
||||
credential_kind TEXT,
|
||||
secret_mount TEXT NOT NULL,
|
||||
secret_path TEXT NOT NULL,
|
||||
vault_kv_version BIGINT,
|
||||
status TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL,
|
||||
CONSTRAINT secret_write_intents_operation_check CHECK (
|
||||
operation IN ('create', 'rotate')
|
||||
),
|
||||
CONSTRAINT secret_write_intents_status_check CHECK (
|
||||
status IN ('prepared', 'vault_written', 'durable_committed', 'abandoned')
|
||||
),
|
||||
CONSTRAINT secret_write_intents_kv_check CHECK (
|
||||
vault_kv_version IS NULL OR vault_kv_version >= 1
|
||||
),
|
||||
CONSTRAINT secret_write_intents_create_fields_check CHECK (
|
||||
(operation = 'create'
|
||||
AND owner_id IS NOT NULL
|
||||
AND label IS NOT NULL
|
||||
AND credential_kind IS NOT NULL)
|
||||
OR (operation = 'rotate'
|
||||
AND owner_id IS NULL
|
||||
AND label IS NULL
|
||||
AND credential_kind IS NULL)
|
||||
),
|
||||
CONSTRAINT secret_write_intents_written_has_version CHECK (
|
||||
(status = 'prepared' AND vault_kv_version IS NULL)
|
||||
OR (status IN ('vault_written', 'durable_committed')
|
||||
AND vault_kv_version IS NOT NULL)
|
||||
OR status = 'abandoned'
|
||||
)
|
||||
);
|
||||
|
||||
CREATE INDEX secret_write_intents_status_idx
|
||||
ON secret_write_intents (status, created_at);
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
-- Durable execution-grant credential pins. Resolution always re-reads this table:
|
||||
-- revoked or missing grants fail closed even if a worker still holds a grant id.
|
||||
|
||||
CREATE TABLE execution_grant_credential_pins (
|
||||
grant_id TEXT PRIMARY KEY,
|
||||
credential_id TEXT NOT NULL,
|
||||
version_id TEXT NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
revoked_at TIMESTAMPTZ,
|
||||
CONSTRAINT execution_grant_credential_pins_status_check CHECK (
|
||||
status IN ('active', 'revoked')
|
||||
),
|
||||
CONSTRAINT execution_grant_credential_pins_revoked_at_check CHECK (
|
||||
(status = 'active' AND revoked_at IS NULL)
|
||||
OR (status = 'revoked' AND revoked_at IS NOT NULL)
|
||||
)
|
||||
);
|
||||
|
||||
CREATE INDEX execution_grant_credential_pins_credential_idx
|
||||
ON execution_grant_credential_pins (credential_id, status);
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
-- Reconciler ownership claim: vault destruction runs only after an intent is
|
||||
-- moved to abandoning under the credential lock. Create/rotate materialize
|
||||
-- refuses non-vault_written intents, so a concurrent commit cannot land a
|
||||
-- durable version that points at material the reconciler is about to destroy.
|
||||
|
||||
ALTER TABLE secret_write_intents
|
||||
DROP CONSTRAINT secret_write_intents_status_check;
|
||||
|
||||
ALTER TABLE secret_write_intents
|
||||
ADD CONSTRAINT secret_write_intents_status_check CHECK (
|
||||
status IN (
|
||||
'prepared',
|
||||
'vault_written',
|
||||
'durable_committed',
|
||||
'abandoning',
|
||||
'abandoned'
|
||||
)
|
||||
);
|
||||
|
||||
ALTER TABLE secret_write_intents
|
||||
DROP CONSTRAINT secret_write_intents_written_has_version;
|
||||
|
||||
ALTER TABLE secret_write_intents
|
||||
ADD CONSTRAINT secret_write_intents_written_has_version CHECK (
|
||||
(status = 'prepared' AND vault_kv_version IS NULL)
|
||||
OR (status IN ('vault_written', 'durable_committed', 'abandoning')
|
||||
AND vault_kv_version IS NOT NULL)
|
||||
OR status = 'abandoned'
|
||||
);
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
ALTER TABLE source_credential_versions
|
||||
ADD COLUMN secret_mount TEXT,
|
||||
ADD COLUMN secret_path TEXT;
|
||||
|
||||
UPDATE source_credential_versions AS version
|
||||
SET secret_mount = credential.secret_mount,
|
||||
secret_path = credential.secret_path
|
||||
FROM source_credentials AS credential
|
||||
WHERE credential.credential_id = version.credential_id;
|
||||
|
||||
ALTER TABLE source_credential_versions
|
||||
ALTER COLUMN secret_mount SET NOT NULL,
|
||||
ALTER COLUMN secret_path SET NOT NULL;
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
CREATE TABLE deletion_operations (
|
||||
operation_id TEXT PRIMARY KEY,
|
||||
job_id TEXT NOT NULL UNIQUE REFERENCES jobs (job_id),
|
||||
state TEXT NOT NULL CHECK (
|
||||
state IN ('queued', 'running', 'retry_wait', 'blocked', 'completed')
|
||||
),
|
||||
problem_class TEXT CHECK (
|
||||
problem_class IS NULL
|
||||
OR problem_class IN ('authorization', 'target_unavailable', 'configuration', 'internal')
|
||||
),
|
||||
problem_message TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL,
|
||||
completed_at TIMESTAMPTZ,
|
||||
CONSTRAINT deletion_operations_problem_check CHECK (
|
||||
(state = 'blocked') = (problem_class IS NOT NULL AND problem_message IS NOT NULL)
|
||||
),
|
||||
CONSTRAINT deletion_operations_completion_check CHECK (
|
||||
(state = 'completed') = (completed_at IS NOT NULL)
|
||||
)
|
||||
);
|
||||
|
||||
ALTER TABLE jobs
|
||||
ADD COLUMN deletion_operation_id TEXT,
|
||||
ADD COLUMN hard_delete_started_at TIMESTAMPTZ,
|
||||
ADD CONSTRAINT jobs_deletion_operation_fk
|
||||
FOREIGN KEY (deletion_operation_id)
|
||||
REFERENCES deletion_operations (operation_id)
|
||||
DEFERRABLE INITIALLY DEFERRED,
|
||||
ADD CONSTRAINT jobs_deletion_lifecycle_check CHECK (
|
||||
(
|
||||
lifecycle IN ('visible', 'soft_deleted')
|
||||
AND deletion_operation_id IS NULL
|
||||
AND hard_delete_started_at IS NULL
|
||||
)
|
||||
OR (
|
||||
lifecycle IN ('hard_deleting', 'hard_deleted')
|
||||
AND deletion_operation_id IS NOT NULL
|
||||
AND hard_delete_started_at IS NOT NULL
|
||||
)
|
||||
);
|
||||
|
||||
CREATE TABLE deletion_items (
|
||||
item_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
operation_id TEXT NOT NULL REFERENCES deletion_operations (operation_id),
|
||||
kind TEXT NOT NULL CHECK (kind IN ('write_intent', 'object', 'multipart', 'manifest')),
|
||||
priority SMALLINT NOT NULL CHECK (priority IN (0, 10, 100)),
|
||||
target_store TEXT NOT NULL,
|
||||
bucket TEXT NOT NULL,
|
||||
object_key TEXT NOT NULL,
|
||||
version_id TEXT,
|
||||
upload_id TEXT,
|
||||
state TEXT NOT NULL DEFAULT 'pending' CHECK (state IN ('pending', 'claimed', 'complete')),
|
||||
claim_token TEXT,
|
||||
claimed_until TIMESTAMPTZ,
|
||||
failure_count BIGINT NOT NULL DEFAULT 0 CHECK (failure_count >= 0),
|
||||
completed_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
CONSTRAINT deletion_items_identity_shape_check CHECK (
|
||||
(kind IN ('write_intent', 'object', 'manifest') AND upload_id IS NULL)
|
||||
OR (kind = 'multipart' AND version_id IS NULL AND upload_id IS NOT NULL)
|
||||
),
|
||||
CONSTRAINT deletion_items_claim_check CHECK (
|
||||
(
|
||||
state = 'pending'
|
||||
AND claim_token IS NULL
|
||||
AND claimed_until IS NULL
|
||||
AND completed_at IS NULL
|
||||
)
|
||||
OR (
|
||||
state = 'claimed'
|
||||
AND claim_token IS NOT NULL
|
||||
AND claimed_until IS NOT NULL
|
||||
AND completed_at IS NULL
|
||||
)
|
||||
OR (
|
||||
state = 'complete'
|
||||
AND claim_token IS NULL
|
||||
AND claimed_until IS NULL
|
||||
AND completed_at IS NOT NULL
|
||||
)
|
||||
),
|
||||
UNIQUE NULLS NOT DISTINCT (
|
||||
operation_id,
|
||||
kind,
|
||||
target_store,
|
||||
bucket,
|
||||
object_key,
|
||||
version_id,
|
||||
upload_id
|
||||
)
|
||||
);
|
||||
|
||||
CREATE INDEX deletion_items_claimable_idx
|
||||
ON deletion_items (operation_id, priority, item_id)
|
||||
WHERE state IN ('pending', 'claimed');
|
||||
38
crates/remora-schema/migrations/durable/0001_substrate.sql
Normal file
38
crates/remora-schema/migrations/durable/0001_substrate.sql
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
-- DurableStore substrate tables owned by remora-durable.
|
||||
-- Feature packages add their own migrations; they do not alter these tables.
|
||||
|
||||
CREATE TABLE mutation_records (
|
||||
scope_subject TEXT NOT NULL,
|
||||
scope_operation TEXT NOT NULL,
|
||||
operation_key TEXT NOT NULL,
|
||||
kind TEXT NOT NULL,
|
||||
-- Opaque RemoraApiMutation eligibility / authority binding (attempt, fence,
|
||||
-- worker incarnation, or other claim material). Compared for exact replay.
|
||||
eligibility_claim BYTEA NOT NULL,
|
||||
request_fingerprint BYTEA NOT NULL CHECK (octet_length(request_fingerprint) = 32),
|
||||
response_status TEXT NOT NULL CHECK (response_status IN ('accepted', 'rejected')),
|
||||
response_body JSONB NOT NULL,
|
||||
effect_applied BOOLEAN NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
PRIMARY KEY (scope_subject, scope_operation, operation_key),
|
||||
CHECK (effect_applied = (response_status = 'accepted'))
|
||||
);
|
||||
|
||||
CREATE TABLE outbox_messages (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
topic TEXT NOT NULL,
|
||||
payload JSONB NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
available_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
claimed_by TEXT,
|
||||
claimed_until TIMESTAMPTZ,
|
||||
-- Monotonic per-row claim epoch; with claimed_by forms the ownership token.
|
||||
claim_generation BIGINT NOT NULL DEFAULT 0,
|
||||
projected_at TIMESTAMPTZ,
|
||||
attempt_count INTEGER NOT NULL DEFAULT 0,
|
||||
last_error TEXT
|
||||
);
|
||||
|
||||
CREATE INDEX outbox_messages_claimable_idx
|
||||
ON outbox_messages (available_at, id)
|
||||
WHERE projected_at IS NULL;
|
||||
10
crates/remora-schema/migrations/identity/0001_principals.sql
Normal file
10
crates/remora-schema/migrations/identity/0001_principals.sql
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
-- Stable principal mapping from OIDC issuer+subject pairs.
|
||||
-- Feature package remora-identity owns this schema.
|
||||
|
||||
CREATE TABLE principal_identities (
|
||||
principal_id TEXT PRIMARY KEY,
|
||||
issuer TEXT NOT NULL,
|
||||
subject TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
UNIQUE (issuer, subject)
|
||||
);
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
CREATE TABLE result_inventories (
|
||||
attempt_id TEXT PRIMARY KEY,
|
||||
state TEXT NOT NULL DEFAULT 'open' CHECK (state IN ('open', 'closed', 'sealed')),
|
||||
manifest_schema_version INTEGER,
|
||||
manifest_id TEXT,
|
||||
manifest_store TEXT,
|
||||
manifest_bucket TEXT,
|
||||
manifest_key TEXT,
|
||||
manifest_version_id TEXT,
|
||||
manifest_sha256 TEXT,
|
||||
manifest_size BIGINT,
|
||||
member_count BIGINT NOT NULL DEFAULT 0 CHECK (member_count >= 0),
|
||||
total_bytes BIGINT NOT NULL DEFAULT 0 CHECK (total_bytes >= 0),
|
||||
CHECK (
|
||||
(state <> 'sealed' AND manifest_schema_version IS NULL AND manifest_id IS NULL
|
||||
AND manifest_store IS NULL AND manifest_bucket IS NULL AND manifest_key IS NULL
|
||||
AND manifest_version_id IS NULL AND manifest_sha256 IS NULL AND manifest_size IS NULL)
|
||||
OR
|
||||
(state = 'sealed' AND manifest_schema_version IS NOT NULL AND manifest_id IS NOT NULL
|
||||
AND manifest_store IS NOT NULL AND manifest_bucket IS NOT NULL AND manifest_key IS NOT NULL
|
||||
AND manifest_sha256 IS NOT NULL AND manifest_size IS NOT NULL)
|
||||
)
|
||||
);
|
||||
|
||||
CREATE TABLE result_inventory_members (
|
||||
attempt_id TEXT NOT NULL REFERENCES result_inventories(attempt_id) ON DELETE CASCADE,
|
||||
member_id TEXT COLLATE "C" NOT NULL,
|
||||
path TEXT COLLATE "C" NOT NULL,
|
||||
size BIGINT NOT NULL CHECK (size >= 0),
|
||||
object_store TEXT NOT NULL,
|
||||
object_bucket TEXT NOT NULL,
|
||||
object_key TEXT NOT NULL,
|
||||
object_version_id TEXT,
|
||||
object_sha256 TEXT NOT NULL,
|
||||
PRIMARY KEY (attempt_id, member_id),
|
||||
UNIQUE (attempt_id, path)
|
||||
);
|
||||
|
||||
CREATE INDEX result_inventory_members_order_idx
|
||||
ON result_inventory_members (attempt_id, path, member_id);
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
ALTER TABLE result_inventory_members
|
||||
DROP CONSTRAINT result_inventory_members_attempt_id_fkey,
|
||||
ADD CONSTRAINT result_inventory_members_attempt_id_fkey
|
||||
FOREIGN KEY (attempt_id)
|
||||
REFERENCES result_inventories (attempt_id)
|
||||
ON UPDATE CASCADE
|
||||
ON DELETE CASCADE
|
||||
DEFERRABLE INITIALLY DEFERRED;
|
||||
|
||||
ALTER TABLE result_inventories
|
||||
ADD COLUMN staging_origin_attempt_id TEXT,
|
||||
ADD COLUMN target_prefix TEXT,
|
||||
ADD CONSTRAINT result_inventories_staging_binding_check CHECK (
|
||||
(staging_origin_attempt_id IS NULL) = (target_prefix IS NULL)
|
||||
);
|
||||
|
||||
UPDATE result_inventories
|
||||
SET staging_origin_attempt_id = attempt_id,
|
||||
target_prefix = '';
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue