Files
easyai-ai-gateway/apps/api/migrations/0105_execution_pool_routing.sql
T
wangbo 7786692d32 feat(routing): 引入多执行池智能调度
将 Worker 发现、路由画像、容量与执行传输抽象为平台无关接口,新增 Kubernetes 和静态容量适配器,并以 shadow 模式接入生产配置。

实现网络与容量评分、路由防抖、池队列、同步 Worker 租约、一次性执行令牌,以及提交状态不明时禁止重复分配的安全语义。

新增 0105 兼容迁移、管理接口、指标、OpenAPI 和回归测试。已执行全量 Go 测试、go vet、OpenAPI、迁移安全、Compose 与 Kustomize 验证。
2026-08-05 22:25:37 +08:00

193 lines
8.0 KiB
SQL

CREATE TABLE IF NOT EXISTS gateway_execution_pools (
pool_id text PRIMARY KEY,
labels jsonb NOT NULL DEFAULT '{}'::jsonb,
capabilities jsonb NOT NULL DEFAULT '{}'::jsonb,
state text NOT NULL DEFAULT 'active',
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
CONSTRAINT gateway_execution_pools_id_check
CHECK (pool_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$'),
CONSTRAINT gateway_execution_pools_state_check
CHECK (state IN ('active', 'draining', 'disabled')),
CONSTRAINT gateway_execution_pools_labels_object_check
CHECK (jsonb_typeof(labels) = 'object'),
CONSTRAINT gateway_execution_pools_capabilities_object_check
CHECK (jsonb_typeof(capabilities) = 'object')
);
INSERT INTO gateway_execution_pools (pool_id, labels)
VALUES ('legacy-default', '{"migration":"legacy"}'::jsonb)
ON CONFLICT (pool_id) DO NOTHING;
INSERT INTO gateway_execution_pools (pool_id, labels)
SELECT DISTINCT site, jsonb_build_object('legacy_site', site)
FROM gateway_worker_instances
WHERE NULLIF(site, '') IS NOT NULL
ON CONFLICT (pool_id) DO NOTHING;
ALTER TABLE gateway_worker_instances
ADD COLUMN pool_id text DEFAULT 'legacy-default',
ADD COLUMN worker_id text DEFAULT '',
ADD COLUMN endpoint text DEFAULT '',
ADD COLUMN orchestrator_instance_ref text DEFAULT '',
ADD COLUMN labels jsonb DEFAULT '{}'::jsonb,
ADD COLUMN capabilities jsonb DEFAULT '{}'::jsonb,
ADD COLUMN protocol_version text DEFAULT 'v1';
UPDATE gateway_worker_instances
SET pool_id = site
WHERE NULLIF(site, '') IS NOT NULL
AND pool_id = 'legacy-default';
UPDATE gateway_worker_instances
SET worker_id = instance_id
WHERE worker_id = '';
UPDATE gateway_worker_instances
SET orchestrator_instance_ref = pod_name
WHERE orchestrator_instance_ref = ''
AND NULLIF(pod_name, '') IS NOT NULL;
ALTER TABLE gateway_worker_instances
ADD CONSTRAINT gateway_worker_instances_pool_fk
FOREIGN KEY (pool_id) REFERENCES gateway_execution_pools(pool_id) ON DELETE RESTRICT,
ADD CONSTRAINT gateway_worker_instances_routing_fields_not_null_check
CHECK (num_nonnulls(pool_id, worker_id, endpoint, orchestrator_instance_ref, labels, capabilities, protocol_version) = 7) NOT VALID,
ADD CONSTRAINT gateway_worker_instances_labels_object_check
CHECK (jsonb_typeof(labels) = 'object') NOT VALID,
ADD CONSTRAINT gateway_worker_instances_capabilities_object_check
CHECK (jsonb_typeof(capabilities) = 'object') NOT VALID;
ALTER TABLE gateway_worker_instances
VALIDATE CONSTRAINT gateway_worker_instances_routing_fields_not_null_check;
ALTER TABLE gateway_worker_instances
VALIDATE CONSTRAINT gateway_worker_instances_labels_object_check;
ALTER TABLE gateway_worker_instances
VALIDATE CONSTRAINT gateway_worker_instances_capabilities_object_check;
CREATE INDEX IF NOT EXISTS idx_worker_instances_pool_heartbeat
ON gateway_worker_instances(pool_id, status, heartbeat_at, instance_id);
CREATE TABLE IF NOT EXISTS gateway_route_health (
pool_id text NOT NULL REFERENCES gateway_execution_pools(pool_id) ON DELETE CASCADE,
route_profile_key text NOT NULL,
state text NOT NULL DEFAULT 'unknown',
success_rate double precision NOT NULL DEFAULT 0,
connect_tls_p95_ms bigint NOT NULL DEFAULT 0,
first_byte_p95_ms bigint NOT NULL DEFAULT 0,
upload_bytes_per_second double precision NOT NULL DEFAULT 0,
jitter_p95_ms bigint NOT NULL DEFAULT 0,
consecutive_failures integer NOT NULL DEFAULT 0,
consecutive_successes integer NOT NULL DEFAULT 0,
sample_count integer NOT NULL DEFAULT 0,
sampled_at timestamptz NOT NULL,
expires_at timestamptz NOT NULL,
updated_at timestamptz NOT NULL DEFAULT now(),
PRIMARY KEY (pool_id, route_profile_key),
CONSTRAINT gateway_route_health_state_check
CHECK (state IN ('healthy', 'degraded', 'unreachable', 'unknown')),
CONSTRAINT gateway_route_health_values_check
CHECK (
success_rate >= 0 AND success_rate <= 1
AND connect_tls_p95_ms >= 0
AND first_byte_p95_ms >= 0
AND upload_bytes_per_second >= 0
AND jitter_p95_ms >= 0
AND consecutive_failures >= 0
AND consecutive_successes >= 0
AND sample_count >= 0
AND expires_at >= sampled_at
)
);
CREATE INDEX IF NOT EXISTS idx_gateway_route_health_fresh
ON gateway_route_health(route_profile_key, expires_at, state, pool_id);
CREATE TABLE IF NOT EXISTS gateway_route_preferences (
route_profile_key text PRIMARY KEY,
current_pool_id text NOT NULL DEFAULT '',
current_since timestamptz,
challenger_pool_id text NOT NULL DEFAULT '',
challenger_wins integer NOT NULL DEFAULT 0,
updated_at timestamptz NOT NULL DEFAULT now(),
CONSTRAINT gateway_route_preferences_wins_check CHECK (challenger_wins >= 0)
);
CREATE TABLE IF NOT EXISTS gateway_route_probe_requests (
route_profile_key text PRIMARY KEY,
requested_at timestamptz NOT NULL DEFAULT now(),
expires_at timestamptz NOT NULL DEFAULT now() + interval '30 seconds'
);
CREATE INDEX IF NOT EXISTS idx_gateway_route_probe_requests_due
ON gateway_route_probe_requests(expires_at, requested_at);
CREATE TABLE IF NOT EXISTS gateway_pool_capacity_desires (
pool_id text PRIMARY KEY REFERENCES gateway_execution_pools(pool_id) ON DELETE CASCADE,
desired integer NOT NULL,
reason text NOT NULL DEFAULT '',
valid_until timestamptz NOT NULL,
updated_at timestamptz NOT NULL DEFAULT now(),
CONSTRAINT gateway_pool_capacity_desires_value_check CHECK (desired >= 0)
);
CREATE TABLE IF NOT EXISTS gateway_worker_execution_leases (
lease_id uuid PRIMARY KEY,
task_id uuid NOT NULL REFERENCES gateway_tasks(id) ON DELETE CASCADE,
pool_id text NOT NULL REFERENCES gateway_execution_pools(pool_id) ON DELETE RESTRICT,
worker_id text NOT NULL,
instance_id text NOT NULL REFERENCES gateway_worker_instances(instance_id) ON DELETE CASCADE,
nonce_hash text NOT NULL UNIQUE,
state text NOT NULL DEFAULT 'reserved',
expires_at timestamptz NOT NULL,
released_at timestamptz,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
CONSTRAINT gateway_worker_execution_leases_state_check
CHECK (state IN ('reserved', 'running', 'released', 'expired'))
);
CREATE INDEX IF NOT EXISTS idx_worker_execution_leases_active
ON gateway_worker_execution_leases(pool_id, instance_id, expires_at)
WHERE released_at IS NULL;
ALTER TABLE gateway_tasks
ADD COLUMN assigned_pool_id text,
ADD COLUMN assigned_worker_id text,
ADD COLUMN route_profile_key text,
ADD COLUMN routing_platform_id uuid,
ADD COLUMN routing_platform_model_id uuid,
ADD COLUMN routing_version text,
ADD COLUMN routing_reason text,
ADD COLUMN routing_snapshot jsonb,
ADD COLUMN submission_state text DEFAULT 'not_started';
ALTER TABLE gateway_tasks
ADD CONSTRAINT gateway_tasks_assigned_pool_fk
FOREIGN KEY (assigned_pool_id) REFERENCES gateway_execution_pools(pool_id) ON DELETE SET NULL,
ADD CONSTRAINT gateway_tasks_routing_platform_fk
FOREIGN KEY (routing_platform_id) REFERENCES integration_platforms(id) ON DELETE SET NULL,
ADD CONSTRAINT gateway_tasks_routing_platform_model_fk
FOREIGN KEY (routing_platform_model_id) REFERENCES platform_models(id) ON DELETE SET NULL,
ADD CONSTRAINT gateway_tasks_routing_snapshot_object_check
CHECK (routing_snapshot IS NULL OR jsonb_typeof(routing_snapshot) = 'object') NOT VALID,
ADD CONSTRAINT gateway_tasks_submission_state_not_null_check
CHECK (num_nonnulls(submission_state) = 1) NOT VALID,
ADD CONSTRAINT gateway_tasks_submission_state_check
CHECK (submission_state IN ('not_started', 'submitting', 'submitted', 'submission_confirmation_pending', 'completed')) NOT VALID;
ALTER TABLE gateway_tasks
VALIDATE CONSTRAINT gateway_tasks_submission_state_not_null_check;
ALTER TABLE gateway_tasks
VALIDATE CONSTRAINT gateway_tasks_routing_snapshot_object_check;
ALTER TABLE gateway_tasks
VALIDATE CONSTRAINT gateway_tasks_submission_state_check;
CREATE INDEX IF NOT EXISTS idx_gateway_tasks_assigned_pool_queue
ON gateway_tasks(assigned_pool_id, status, next_run_at, priority, created_at)
WHERE async_mode = true AND status = 'queued';