diff --git a/backend/crates/gateway-store/tests/postgres/mod.rs b/backend/crates/gateway-store/tests/postgres/mod.rs index 022048f57..e274651d2 100644 --- a/backend/crates/gateway-store/tests/postgres/mod.rs +++ b/backend/crates/gateway-store/tests/postgres/mod.rs @@ -55,10 +55,6 @@ pub(super) fn admin_account_store(pool: &PgPool) -> PgAdminAccountStore { impl TestDatabase { pub(super) async fn create(label: &str) -> Option { - Self::create_at(label, i64::MAX).await - } - - pub(super) async fn create_at(label: &str, version: i64) -> Option { let database_url = crate::support::test_env("CPR_TEST_DATABASE_URL")?; let schema = format!("cpr_store_{label}_{}", Uuid::new_v4().simple()); let admin = PgPoolOptions::new() @@ -86,16 +82,10 @@ impl TestDatabase { .connect(&database_url) .await .expect("connect isolated test schema"); - sqlx::migrate::Migrator::with_migrations( - TEST_MIGRATOR - .iter() - .filter(|migration| migration.version <= version) - .cloned() - .collect(), - ) - .run(&pool) - .await - .expect("apply test migrations"); + TEST_MIGRATOR + .run(&pool) + .await + .expect("apply test migrations"); Some(Self { admin, pool, diff --git a/backend/crates/gateway-store/tests/postgres/schema_integrity.rs b/backend/crates/gateway-store/tests/postgres/schema_integrity.rs index 0794e4efb..50b6973eb 100644 --- a/backend/crates/gateway-store/tests/postgres/schema_integrity.rs +++ b/backend/crates/gateway-store/tests/postgres/schema_integrity.rs @@ -1,4 +1,4 @@ -use super::{TEST_MIGRATOR, TestDatabase}; +use super::TestDatabase; async fn seed_request(pool: &sqlx::PgPool) { sqlx::query( @@ -101,8 +101,8 @@ async fn account_snapshots_are_required_before_live_foreign_keys_can_be_cleared( } #[tokio::test] -async fn upgrade_normalizes_admin_identity_and_preserves_existing_event_rows() { - let Some(db) = TestDatabase::create_at("audit_upgrade", 7).await else { +async fn audit_requires_canonical_admin_identity_and_retains_it_after_admin_deletion() { + let Some(db) = TestDatabase::create("audit_identity").await else { return; }; sqlx::raw_sql( @@ -110,28 +110,12 @@ async fn upgrade_normalizes_admin_identity_and_preserves_existing_event_rows() { values ('admin_test', 'test_hash', now(), now()); insert into admin_audit_events (id, actor_kind, actor_admin_user_id, actor_ref, action, entity_kind, entity_ref, created_at) - values ('audit_before_upgrade', 'admin_session', 'admin_test', 'admin_test', - 'update', 'settings', '1', now()); - insert into ops_events (id, level, component, operation, failure_kind, message, created_at) - values ('ops_before_upgrade', 'error', 'probe', 'probe', 'timeout', 'test', now());", + values ('audit_identity', 'admin_session', 'admin_test', 'admin:admin_test', + 'update', 'settings', '1', now());", ) .execute(&db.pool) .await - .expect("seed data from pre-optimization schema"); - TEST_MIGRATOR - .run(&db.pool) - .await - .expect("upgrade populated database"); - let identity: String = sqlx::query_scalar("select actor_ref from admin_audit_events") - .fetch_one(&db.pool) - .await - .expect("read normalized identity"); - assert_eq!(identity, "admin:admin_test"); - let events: i64 = sqlx::query_scalar("select count(*) from ops_events") - .fetch_one(&db.pool) - .await - .expect("read retained events"); - assert_eq!(events, 1); + .expect("seed audit event with canonical admin identity"); let error = sqlx::query("update admin_audit_events set actor_ref = 'admin_test'") .execute(&db.pool) .await diff --git a/backend/migrations/.frozen-sha256 b/backend/migrations/.frozen-sha256 index 0ac6beac1..319a0eb9f 100644 --- a/backend/migrations/.frozen-sha256 +++ b/backend/migrations/.frozen-sha256 @@ -1,10 +1 @@ -60bcece284f2d6a2cd3e8dea67833dfc1c7da625cdd5f0b63884c725fa00a3d4 0001_initial.sql -93219760c733616e7bba9a8933c207b26752b2ebf7e6edfe1ef42ba227c50a7d 0002_runtime_pressure_observability.sql -1b6ffab434c754814aebb0eee3b650bd9770010059472bccf5bc856b319880ca 0003_client_min_versions.sql -22f912ab3e196de7983ef50a1dba5aee340bbea82bdd15d05863e87012000c83 0004_ops_error_send_state.sql -194d7b4bedd83f0d76428a21699b0e946449bf2ba61a0fdf8710ebfd421af8b2 0005_openai_continuation_recovery_observability.sql -3b4bfd5e3f5135fa2fad1088b0c2a287861e1250d66af31dbd40dc890751b567 0006_openai_session_transport_recovery.sql -8c0ec7fab80d95dfd5a370c70706c36ac5907bb5afea0cf1886196f21fd8317d 0007_request_diagnostics.sql -374f0d8fa8b671ca845d085f7d173b2aa57320e7e5c41e0398329f0bdd89506e 0008_persisted_fact_integrity.sql -de20c9c9c57fcf0371a2245a3de9a57d72ab75d1c08ffff92f76b2b270d6a72c 0009_trim_observability_indexes.sql -661aba0469d4b9e4626e9e9331015bb2120cc948e8d15b1ca83a394a5007e11a 0010_remove_unused_event_aggregation.sql +b7d3df7b48d835a00a3b74cbefc958a039d38fea5529cdad04bfbc703dca72aa 0001_initial.sql diff --git a/backend/migrations/0001_initial.sql b/backend/migrations/0001_initial.sql index 8bcb7b05b..51066e4ec 100644 --- a/backend/migrations/0001_initial.sql +++ b/backend/migrations/0001_initial.sql @@ -33,18 +33,18 @@ create table admin_audit_events ( constraint admin_audit_events_actor_fk foreign key (actor_admin_user_id) references admin_users (id) on update restrict - on delete set null + on delete set null, + constraint admin_audit_events_actor_identity_ck check ( + actor_admin_user_id is null + or (actor_kind = 'admin_session' and actor_ref = 'admin:' || actor_admin_user_id) + ) ); create index admin_audit_events_actor_idx - on admin_audit_events (actor_admin_user_id, created_at desc, id desc) + on admin_audit_events (actor_admin_user_id) where actor_admin_user_id is not null; create index admin_audit_events_created_idx on admin_audit_events (created_at desc, id desc); -create index admin_audit_events_entity_idx - on admin_audit_events (entity_kind, entity_ref, created_at desc, id desc); -create index admin_audit_events_actor_ref_idx - on admin_audit_events (actor_ref, created_at desc, id desc); -- 客户端 API 密钥、鉴权范围与限额。 create table client_api_keys ( @@ -70,8 +70,6 @@ create table client_api_keys ( create index client_api_keys_created_idx on client_api_keys (created_at desc, id desc); -create index client_api_keys_last_used_idx - on client_api_keys (last_used_at desc, id desc); -- 全局运行参数与配置版本。 create table runtime_settings ( @@ -88,6 +86,8 @@ create table runtime_settings ( ops_event_retention_days bigint not null default 30, audit_retention_days bigint not null default 90, updated_at timestamptz not null, + min_codex_desktop_version text, + min_codex_cli_version text, constraint runtime_settings_singleton_ck check (id = 1), constraint runtime_settings_revision_ck check (config_revision > 0), constraint runtime_settings_refresh_ck check ( @@ -107,6 +107,24 @@ create table runtime_settings ( usage_retention_days >= 31 and ops_event_retention_days > 0 and audit_retention_days > 0 + ), + constraint runtime_settings_client_versions_ck check ( + ( + min_codex_desktop_version is null + or ( + octet_length(min_codex_desktop_version) between 1 and 64 + and min_codex_desktop_version = btrim(min_codex_desktop_version) + and min_codex_desktop_version !~ '[[:cntrl:]]' + ) + ) + and ( + min_codex_cli_version is null + or ( + octet_length(min_codex_cli_version) between 1 and 64 + and min_codex_cli_version = btrim(min_codex_cli_version) + and min_codex_cli_version !~ '[[:cntrl:]]' + ) + ) ) ); @@ -224,13 +242,8 @@ create index provider_accounts_runtime_idx on provider_accounts (provider_kind, enabled, id); create index provider_accounts_credential_state_idx on provider_accounts (credential_state, id); -create index provider_accounts_quota_access_idx - on provider_accounts (quota_access_state, quota_reset_at, id); create index provider_accounts_access_expiry_idx on provider_accounts (access_token_expires_at, id); -create index provider_accounts_refresh_due_idx - on provider_accounts (next_refresh_at, id) - where enabled and has_refresh_token and next_refresh_at is not null; create index provider_accounts_email_idx on provider_accounts (provider_kind, lower(email)) where email is not null; @@ -266,8 +279,6 @@ create table account_groups ( create unique index account_groups_name_uq on account_groups (lower(name)); -create index account_groups_list_idx - on account_groups (enabled, created_at desc, id desc); create table account_group_accounts ( account_group_id text not null, @@ -376,6 +387,25 @@ create table model_requests ( routing_scope text not null, routing_group_refs text[] not null default '{}', routing_group_names_snapshot jsonb not null default '[]'::jsonb, + admission_decision_ms bigint, + account_selection_wait_ms bigint, + capacity_used_slots bigint, + capacity_total_slots bigint, + raw_upstream_error text, + continuation_affinity_hash text, + continuation_previous_response_id_hash text, + continuation_requested boolean not null default false, + continuation_unavailable_reason text, + upstream_connection_id text, + upstream_connection_exit_reason text, + upstream_connection_age_ms bigint, + upstream_connection_idle_ms bigint, + recovery_request_id text, + recovered_at timestamptz, + recovery_attempt_count integer not null default 0, + recovery_retry_delay_ms bigint, + recovery_total_latency_ms bigint, + diagnostic_trace_json jsonb, constraint model_requests_client_ref_ck check ( client_api_key_id is null or client_api_key_id = client_api_key_ref ), @@ -523,18 +553,97 @@ create table model_requests ( constraint model_requests_account_fk foreign key (provider_account_id) references provider_accounts (id) on update restrict - on delete set null + on delete set null, + constraint model_requests_runtime_pressure_ck check ( + (admission_decision_ms is null or admission_decision_ms >= 0) + and (account_selection_wait_ms is null or account_selection_wait_ms >= 0) + and ( + (capacity_used_slots is null and capacity_total_slots is null) + or ( + capacity_used_slots >= 0 + and capacity_total_slots > 0 + and capacity_used_slots <= capacity_total_slots + ) + ) + ), + constraint model_requests_continuation_hashes_ck check ( + (continuation_affinity_hash is null + or continuation_affinity_hash ~ '^[0-9a-f]{64}$') + and (continuation_previous_response_id_hash is null + or continuation_previous_response_id_hash ~ '^[0-9a-f]{64}$') + and (continuation_requested + or continuation_previous_response_id_hash is null) + ), + constraint model_requests_continuation_reason_ck check ( + continuation_unavailable_reason is null + or ( + octet_length(continuation_unavailable_reason) between 1 and 64 + and continuation_unavailable_reason ~ '^[a-z][a-z0-9_]*$' + ) + ), + constraint model_requests_upstream_connection_ck check ( + ( + upstream_connection_id is null + and upstream_connection_exit_reason is null + and upstream_connection_age_ms is null + and upstream_connection_idle_ms is null + ) + or ( + octet_length(upstream_connection_id) between 1 and 128 + and upstream_connection_id !~ '[[:cntrl:]]' + and octet_length(upstream_connection_exit_reason) between 1 and 64 + and upstream_connection_exit_reason ~ '^[a-z][a-z0-9_]*$' + and upstream_connection_age_ms >= 0 + and upstream_connection_idle_ms between 0 and upstream_connection_age_ms + ) + ), + constraint model_requests_recovery_ck check ( + recovery_attempt_count >= 0 + and ( + ( + recovered_at is null + and recovery_request_id is null + and recovery_retry_delay_ms is null + and recovery_total_latency_ms is null + ) + or ( + recovered_at is not null + and recovered_at >= completed_at + and recovery_request_id is not null + and octet_length(recovery_request_id) between 1 and 128 + and recovery_attempt_count > 0 + and recovery_retry_delay_ms >= 0 + and recovery_total_latency_ms >= recovery_retry_delay_ms + ) + ) + ), + constraint model_requests_diagnostic_trace_object + check (diagnostic_trace_json is null or jsonb_typeof(diagnostic_trace_json) = 'object'), + constraint model_requests_fact_completeness_ck check ( + (provider_account_id is null or provider_account_ref is not null) + and (cost_source = 'unavailable' or cost_currency is not null) + and num_nonnulls(capacity_used_slots, capacity_total_slots) in (0, 2) + and num_nonnulls( + upstream_connection_id, upstream_connection_exit_reason, + upstream_connection_age_ms, upstream_connection_idle_ms + ) in (0, 4) + and ( + recovered_at is null + or ( + completed_at is not null + and recovery_retry_delay_ms is not null + and recovery_total_latency_ms is not null + ) + ) + ) ); create index model_requests_client_idx - on model_requests (client_api_key_id, started_at desc, id desc); + on model_requests (client_api_key_id) + where client_api_key_id is not null; create index model_requests_account_idx - on model_requests ( - provider_account_id, - provider_kind, - started_at desc, - id desc - ); + on model_requests (provider_account_id) + where provider_account_id is not null; create index model_requests_started_idx on model_requests (started_at desc, id desc); create index model_requests_client_ref_idx @@ -561,6 +670,37 @@ create index model_requests_retention_idx create index model_requests_routing_group_refs_idx on model_requests using gin (routing_group_refs); +create index model_requests_pending_continuation_recovery_idx + on model_requests ( + client_api_key_ref, + continuation_affinity_hash, + completed_at desc, + id desc + ) + where error_kind = 'continuation_recovery_required' + and recovered_at is null + and continuation_affinity_hash is not null; + +create index model_requests_recovery_request_idx + on model_requests (recovery_request_id) + where recovery_request_id is not null; + +create index model_requests_pending_ws_transport_recovery_idx + on model_requests ( + client_api_key_ref, + continuation_affinity_hash, + completed_at desc, + id desc + ) + where provider_kind = 'openai' + and outcome = 'failed' + and error_kind = 'upstream_unavailable' + and upstream_transport = 'websocket' + and upstream_send_state = 'ambiguous' + and downstream_committed_at is null + and recovered_at is null + and continuation_affinity_hash is not null; + -- 运行期警告与错误事件。 create table ops_events ( id text primary key, @@ -583,8 +723,9 @@ create table ops_events ( upstream_request_id text, latency_ms bigint, message text not null, - occurrence_count integer not null default 1, created_at timestamptz not null, + upstream_send_state text, + raw_upstream_error text, constraint ops_events_request_attempt_ck check ( ( model_request_id is null @@ -601,7 +742,6 @@ create table ops_events ( (status_code is null or status_code between 100 and 599) and (retry_after_ms is null or retry_after_ms >= 0) and (latency_ms is null or latency_ms >= 0) - and occurrence_count > 0 ), constraint ops_events_account_ref_ck check ( provider_account_id is null or provider_account_id = provider_account_ref @@ -613,24 +753,23 @@ create table ops_events ( constraint ops_events_account_fk foreign key (provider_account_id) references provider_accounts (id) on update restrict - on delete set null + on delete set null, + constraint ops_events_upstream_send_state_ck check ( + upstream_send_state is null + or upstream_send_state in ('not_sent', 'sent', 'ambiguous') + ), + constraint ops_events_account_snapshot_present_ck check ( + provider_account_id is null or provider_account_ref is not null + ) ); create index ops_events_request_idx on ops_events (model_request_id, attempt_index, id); create index ops_events_account_idx - on ops_events ( - provider_account_id, - provider_kind, - created_at desc, - id desc - ); + on ops_events (provider_account_id) + where provider_account_id is not null; create index ops_events_created_idx on ops_events (created_at desc, id desc); -create index ops_events_component_idx - on ops_events (component, created_at desc, id desc); -create index ops_events_failure_idx - on ops_events (failure_kind, created_at desc, id desc); create index ops_events_account_ref_idx on ops_events (provider_account_ref, created_at desc, id desc) where provider_account_ref is not null; @@ -798,6 +937,9 @@ create table backup_records ( -- expires_at 为空,或不早于创建时间(创建时 now + 天数 恒晚于 created_at)。 constraint backup_records_expiry_ck check ( expires_at is null or expires_at >= created_at + ), + constraint backup_records_completion_order_ck check ( + completed_at is null or completed_at >= started_at ) ); diff --git a/backend/migrations/0002_runtime_pressure_observability.sql b/backend/migrations/0002_runtime_pressure_observability.sql deleted file mode 100644 index cf3392737..000000000 --- a/backend/migrations/0002_runtime_pressure_observability.sql +++ /dev/null @@ -1,20 +0,0 @@ --- 请求热路径的准入判定、账号选择等待与账号池容量观测。 -alter table model_requests - add column admission_decision_ms bigint, - add column account_selection_wait_ms bigint, - add column capacity_used_slots bigint, - add column capacity_total_slots bigint; - -alter table model_requests - add constraint model_requests_runtime_pressure_ck check ( - (admission_decision_ms is null or admission_decision_ms >= 0) - and (account_selection_wait_ms is null or account_selection_wait_ms >= 0) - and ( - (capacity_used_slots is null and capacity_total_slots is null) - or ( - capacity_used_slots >= 0 - and capacity_total_slots > 0 - and capacity_used_slots <= capacity_total_slots - ) - ) - ); diff --git a/backend/migrations/0003_client_min_versions.sql b/backend/migrations/0003_client_min_versions.sql deleted file mode 100644 index 55c3d1bfd..000000000 --- a/backend/migrations/0003_client_min_versions.sql +++ /dev/null @@ -1,21 +0,0 @@ -alter table runtime_settings - add column min_codex_desktop_version text, - add column min_codex_cli_version text, - add constraint runtime_settings_client_versions_ck check ( - ( - min_codex_desktop_version is null - or ( - octet_length(min_codex_desktop_version) between 1 and 64 - and min_codex_desktop_version = btrim(min_codex_desktop_version) - and min_codex_desktop_version !~ '[[:cntrl:]]' - ) - ) - and ( - min_codex_cli_version is null - or ( - octet_length(min_codex_cli_version) between 1 and 64 - and min_codex_cli_version = btrim(min_codex_cli_version) - and min_codex_cli_version !~ '[[:cntrl:]]' - ) - ) - ); diff --git a/backend/migrations/0004_ops_error_send_state.sql b/backend/migrations/0004_ops_error_send_state.sql deleted file mode 100644 index 8d9eae92b..000000000 --- a/backend/migrations/0004_ops_error_send_state.sql +++ /dev/null @@ -1,11 +0,0 @@ --- 为运维错误保留 payload 发送边界与上游错误原文。 -alter table model_requests - add column raw_upstream_error text; - -alter table ops_events - add column upstream_send_state text, - add column raw_upstream_error text, - add constraint ops_events_upstream_send_state_ck check ( - upstream_send_state is null - or upstream_send_state in ('not_sent', 'sent', 'ambiguous') - ); diff --git a/backend/migrations/0005_openai_continuation_recovery_observability.sql b/backend/migrations/0005_openai_continuation_recovery_observability.sql deleted file mode 100644 index 8f7477013..000000000 --- a/backend/migrations/0005_openai_continuation_recovery_observability.sql +++ /dev/null @@ -1,80 +0,0 @@ -alter table model_requests - add column continuation_affinity_hash text, - add column continuation_previous_response_id_hash text, - add column continuation_requested boolean not null default false, - add column continuation_unavailable_reason text, - add column upstream_connection_id text, - add column upstream_connection_exit_reason text, - add column upstream_connection_age_ms bigint, - add column upstream_connection_idle_ms bigint, - add column recovery_request_id text, - add column recovered_at timestamptz, - add column recovery_attempt_count integer not null default 0, - add column recovery_retry_delay_ms bigint, - add column recovery_total_latency_ms bigint, - add constraint model_requests_continuation_hashes_ck check ( - (continuation_affinity_hash is null - or continuation_affinity_hash ~ '^[0-9a-f]{64}$') - and (continuation_previous_response_id_hash is null - or continuation_previous_response_id_hash ~ '^[0-9a-f]{64}$') - and (continuation_requested - or continuation_previous_response_id_hash is null) - ), - add constraint model_requests_continuation_reason_ck check ( - continuation_unavailable_reason is null - or ( - octet_length(continuation_unavailable_reason) between 1 and 64 - and continuation_unavailable_reason ~ '^[a-z][a-z0-9_]*$' - ) - ), - add constraint model_requests_upstream_connection_ck check ( - ( - upstream_connection_id is null - and upstream_connection_exit_reason is null - and upstream_connection_age_ms is null - and upstream_connection_idle_ms is null - ) - or ( - octet_length(upstream_connection_id) between 1 and 128 - and upstream_connection_id !~ '[[:cntrl:]]' - and octet_length(upstream_connection_exit_reason) between 1 and 64 - and upstream_connection_exit_reason ~ '^[a-z][a-z0-9_]*$' - and upstream_connection_age_ms >= 0 - and upstream_connection_idle_ms between 0 and upstream_connection_age_ms - ) - ), - add constraint model_requests_recovery_ck check ( - recovery_attempt_count >= 0 - and ( - ( - recovered_at is null - and recovery_request_id is null - and recovery_retry_delay_ms is null - and recovery_total_latency_ms is null - ) - or ( - recovered_at is not null - and recovered_at >= completed_at - and recovery_request_id is not null - and octet_length(recovery_request_id) between 1 and 128 - and recovery_attempt_count > 0 - and recovery_retry_delay_ms >= 0 - and recovery_total_latency_ms >= recovery_retry_delay_ms - ) - ) - ); - -create index model_requests_pending_continuation_recovery_idx - on model_requests ( - client_api_key_ref, - continuation_affinity_hash, - completed_at desc, - id desc - ) - where error_kind = 'continuation_recovery_required' - and recovered_at is null - and continuation_affinity_hash is not null; - -create index model_requests_recovery_request_idx - on model_requests (recovery_request_id) - where recovery_request_id is not null; diff --git a/backend/migrations/0006_openai_session_transport_recovery.sql b/backend/migrations/0006_openai_session_transport_recovery.sql deleted file mode 100644 index 759372e77..000000000 --- a/backend/migrations/0006_openai_session_transport_recovery.sql +++ /dev/null @@ -1,61 +0,0 @@ -create index model_requests_pending_ws_transport_recovery_idx - on model_requests ( - client_api_key_ref, - continuation_affinity_hash, - completed_at desc, - id desc - ) - where provider_kind = 'openai' - and outcome = 'failed' - and error_kind = 'upstream_unavailable' - and upstream_transport = 'websocket' - and upstream_send_state = 'ambiguous' - and downstream_committed_at is null - and recovered_at is null - and continuation_affinity_hash is not null; - -with session_transport_recoveries as ( - select - prior.id as failed_request_id, - recovered.id as recovery_request_id, - recovered.started_at as recovery_started_at, - recovered.completed_at as recovered_at - from model_requests prior - cross join lateral ( - select current.id, current.started_at, current.completed_at - from model_requests current - where current.client_api_key_ref = prior.client_api_key_ref - and current.continuation_affinity_hash = prior.continuation_affinity_hash - and current.provider_kind = prior.provider_kind - and current.outcome = 'succeeded' - and current.upstream_transport = 'http_sse' - and current.completed_at is not null - and current.started_at >= prior.completed_at - and current.started_at <= prior.completed_at + interval '30 seconds' - order by current.started_at, current.id - limit 1 - ) recovered - where prior.provider_kind = 'openai' - and prior.outcome = 'failed' - and prior.error_kind = 'upstream_unavailable' - and prior.upstream_transport = 'websocket' - and prior.upstream_send_state = 'ambiguous' - and prior.downstream_committed_at is null - and prior.recovered_at is null - and prior.continuation_affinity_hash is not null -) -update model_requests prior -set recovery_attempt_count = prior.recovery_attempt_count + 1, - recovery_request_id = recovery.recovery_request_id, - recovered_at = recovery.recovered_at, - recovery_retry_delay_ms = greatest( - 0, - floor(extract(epoch from (recovery.recovery_started_at - prior.completed_at)) * 1000) - )::bigint, - recovery_total_latency_ms = greatest( - 0, - floor(extract(epoch from (recovery.recovered_at - prior.completed_at)) * 1000) - )::bigint -from session_transport_recoveries recovery -where prior.id = recovery.failed_request_id - and prior.recovered_at is null; diff --git a/backend/migrations/0007_request_diagnostics.sql b/backend/migrations/0007_request_diagnostics.sql deleted file mode 100644 index a926cae6a..000000000 --- a/backend/migrations/0007_request_diagnostics.sql +++ /dev/null @@ -1,5 +0,0 @@ --- Bounded request evidence is retained with its owning model request. --- NULL identifies historical requests for which no trace was captured. -alter table model_requests add column diagnostic_trace_json jsonb; -alter table model_requests add constraint model_requests_diagnostic_trace_object - check (diagnostic_trace_json is null or jsonb_typeof(diagnostic_trace_json) = 'object'); diff --git a/backend/migrations/0008_persisted_fact_integrity.sql b/backend/migrations/0008_persisted_fact_integrity.sql deleted file mode 100644 index fc97fc0c2..000000000 --- a/backend/migrations/0008_persisted_fact_integrity.sql +++ /dev/null @@ -1,41 +0,0 @@ --- Existing value checks also need explicit presence checks: CHECK accepts NULL. -alter table model_requests - add constraint model_requests_fact_completeness_ck check ( - (provider_account_id is null or provider_account_ref is not null) - and (cost_source = 'unavailable' or cost_currency is not null) - and num_nonnulls(capacity_used_slots, capacity_total_slots) in (0, 2) - and num_nonnulls( - upstream_connection_id, upstream_connection_exit_reason, - upstream_connection_age_ms, upstream_connection_idle_ms - ) in (0, 4) - and ( - recovered_at is null - or ( - completed_at is not null - and recovery_retry_delay_ms is not null - and recovery_total_latency_ms is not null - ) - ) - ); - -alter table ops_events - add constraint ops_events_account_snapshot_present_ck check ( - provider_account_id is null or provider_account_ref is not null - ); - -alter table backup_records - add constraint backup_records_completion_order_ck check ( - completed_at is null or completed_at >= started_at - ); - -update admin_audit_events -set actor_ref = 'admin:' || actor_admin_user_id -where actor_admin_user_id is not null - and actor_kind = 'admin_session' - and actor_ref is distinct from 'admin:' || actor_admin_user_id; - -alter table admin_audit_events - add constraint admin_audit_events_actor_identity_ck check ( - actor_admin_user_id is null - or (actor_kind = 'admin_session' and actor_ref = 'admin:' || actor_admin_user_id) - ); diff --git a/backend/migrations/0009_trim_observability_indexes.sql b/backend/migrations/0009_trim_observability_indexes.sql deleted file mode 100644 index e5018d544..000000000 --- a/backend/migrations/0009_trim_observability_indexes.sql +++ /dev/null @@ -1,28 +0,0 @@ --- Live references support FK maintenance; historical queries use *_ref indexes. -drop index model_requests_account_idx; -create index model_requests_account_idx on model_requests (provider_account_id) - where provider_account_id is not null; - -drop index model_requests_client_idx; -create index model_requests_client_idx on model_requests (client_api_key_id) - where client_api_key_id is not null; - -drop index ops_events_account_idx; -create index ops_events_account_idx on ops_events (provider_account_id) - where provider_account_id is not null; - -drop index admin_audit_events_actor_idx; -create index admin_audit_events_actor_idx on admin_audit_events (actor_admin_user_id) - where actor_admin_user_id is not null; - --- Key usage is updated continuously; a small management list can sort on demand. -drop index client_api_keys_last_used_idx; - --- These leading keys no longer match the current runtime queries. -drop index provider_accounts_quota_access_idx; -drop index provider_accounts_refresh_due_idx; -drop index account_groups_list_idx; -drop index ops_events_component_idx; -drop index ops_events_failure_idx; -drop index admin_audit_events_actor_ref_idx; -drop index admin_audit_events_entity_idx; diff --git a/backend/migrations/0010_remove_unused_event_aggregation.sql b/backend/migrations/0010_remove_unused_event_aggregation.sql deleted file mode 100644 index 3e339153c..000000000 --- a/backend/migrations/0010_remove_unused_event_aggregation.sql +++ /dev/null @@ -1,8 +0,0 @@ --- Each row records one event; aggregation is performed by the read queries. -alter table ops_events drop constraint ops_events_values_ck; -alter table ops_events drop column occurrence_count; -alter table ops_events add constraint ops_events_values_ck check ( - (status_code is null or status_code between 100 and 599) - and (retry_after_ms is null or retry_after_ms >= 0) - and (latency_ms is null or latency_ms >= 0) -); diff --git a/backend/migrations/README.md b/backend/migrations/README.md index 12d7bc4e9..84c0067c4 100644 --- a/backend/migrations/README.md +++ b/backend/migrations/README.md @@ -3,8 +3,8 @@ sqlx 在服务启动、监听请求之前按编号执行迁移,并把文件 checksum 记入 `_sqlx_migrations`。 已应用迁移的字节与记录不一致时,服务会拒绝启动。 -`0001_initial.sql` 是初始基线,后续编号继续增加字段和索引; -当前完整清单以本目录 SQL 文件和 `.frozen-sha256` 为准,不要只执行 `0001`。 +`0001_initial.sql` 定义数据库初始化基线,后续 schema 变更按编号新增迁移。 +完整清单以本目录 SQL 文件和 `.frozen-sha256` 为准。 ## 冻结规则