fix(gateway): split profile identity from scenario

This commit is contained in:
2026-08-13 16:03:41 +00:00
parent 826d17da81
commit 6478a9f478
26 changed files with 399 additions and 83 deletions
@@ -0,0 +1,71 @@
-- Keep profile_name stable because it is referenced by operations, runtime
-- actions, permission scopes, process names, Redis namespaces, and routes.
-- instance_key identifies the immutable slot while current_scenario records the
-- mutable game selection. The legacy scenario column remains during the
-- expansion phase so the previous Gateway release can still be restored.
ALTER TABLE "gateway_profile"
ADD COLUMN "instance_key" TEXT,
ADD COLUMN "current_scenario" TEXT;
DO $$
BEGIN
IF EXISTS (
SELECT 1
FROM "gateway_profile"
WHERE left("profile_name", length("profile") + 1) <> "profile" || ':'
) THEN
RAISE EXCEPTION 'gateway_profile.profile_name must start with profile followed by a colon';
END IF;
END
$$;
UPDATE "gateway_profile"
SET
"instance_key" = substring("profile_name" FROM length("profile") + 2),
"current_scenario" = NULLIF("scenario", 'default');
ALTER TABLE "gateway_profile"
ALTER COLUMN "instance_key" SET NOT NULL;
DROP INDEX "gateway_profile_profile_scenario_key";
ALTER TABLE "gateway_profile"
ADD CONSTRAINT "gateway_profile_profile_instance_key_key" UNIQUE ("profile", "instance_key"),
ADD CONSTRAINT "gateway_profile_identity_check"
CHECK (
length("instance_key") > 0
AND "profile_name" = "profile" || ':' || "instance_key"
);
CREATE FUNCTION "sync_gateway_profile_scenario_compat"()
RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
IF NEW."instance_key" IS NULL THEN
IF left(NEW."profile_name", length(NEW."profile") + 1) <> NEW."profile" || ':' THEN
RAISE EXCEPTION 'gateway_profile.profile_name must start with profile followed by a colon';
END IF;
NEW."instance_key" := substring(NEW."profile_name" FROM length(NEW."profile") + 2);
END IF;
IF TG_OP = 'INSERT' THEN
IF NEW."current_scenario" IS NULL THEN
NEW."current_scenario" := NULLIF(NEW."scenario", 'default');
ELSE
NEW."scenario" := NEW."current_scenario";
END IF;
ELSIF NEW."current_scenario" IS DISTINCT FROM OLD."current_scenario" THEN
NEW."scenario" := COALESCE(NEW."current_scenario", 'default');
ELSIF NEW."scenario" IS DISTINCT FROM OLD."scenario" THEN
NEW."current_scenario" := NULLIF(NEW."scenario", 'default');
END IF;
RETURN NEW;
END
$$;
CREATE TRIGGER "gateway_profile_scenario_compat"
BEFORE INSERT OR UPDATE ON "gateway_profile"
FOR EACH ROW
EXECUTE FUNCTION "sync_gateway_profile_scenario_compat"();
+5 -1
View File
@@ -208,6 +208,10 @@ model LegacyRootKeyValue {
model GatewayProfile {
profileName String @id @map("profile_name")
profile String
instanceKey String @map("instance_key")
currentScenario String? @map("current_scenario")
/// Legacy compatibility mirror. The database trigger keeps this synchronized
/// with currentScenario while the previous Gateway release remains rollbackable.
scenario String
apiPort Int @map("api_port")
status GatewayProfileStatus
@@ -229,7 +233,7 @@ model GatewayProfile {
operations GatewayOperation[]
runtimeActions GatewayRuntimeAction[]
@@unique([profile, scenario])
@@unique([profile, instanceKey])
@@map("gateway_profile")
}