chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* isSplitEnabled() is the Wave-0 gate. The entire migration/routing/FK-drop family
|
||||
* MUST be unreachable when this returns false. Default is false (single-DB). Never
|
||||
* infer split-vs-single from URL string-equality — distinctness is proven by the
|
||||
* runtime sentinel.
|
||||
*/
|
||||
import { env } from "~/env.server";
|
||||
import { logger } from "~/services/logger.server";
|
||||
import { probeDistinctDatabases as defaultProbe } from "./distinctDbSentinel.server";
|
||||
|
||||
export type SplitModeConfig = {
|
||||
flagEnabled: boolean;
|
||||
legacyUrl?: string;
|
||||
newUrl?: string;
|
||||
};
|
||||
|
||||
export type SplitModeDeps = {
|
||||
probe?: typeof defaultProbe;
|
||||
logger?: { warn: (msg: string, meta?: Record<string, unknown>) => void };
|
||||
};
|
||||
|
||||
export async function computeSplitEnabled(
|
||||
config: SplitModeConfig,
|
||||
deps: SplitModeDeps = {}
|
||||
): Promise<boolean> {
|
||||
// Hard gate #1: explicit positive opt-in. OFF by default -> never probe.
|
||||
if (!config.flagEnabled) {
|
||||
return false;
|
||||
}
|
||||
// Both URLs are required to even consider a split.
|
||||
if (!config.legacyUrl || !config.newUrl) {
|
||||
deps.logger?.warn(
|
||||
"RUN_OPS_SPLIT_ENABLED is on but RUN_OPS_LEGACY_DATABASE_URL / RUN_OPS_DATABASE_URL are not both set; staying single-DB."
|
||||
);
|
||||
return false;
|
||||
}
|
||||
// Hard gate #2: runtime sentinel must confirm physically-distinct DBs.
|
||||
const probe = deps.probe ?? defaultProbe;
|
||||
const result = await probe(config.legacyUrl, config.newUrl, { logger: deps.logger });
|
||||
return result.distinct === true;
|
||||
}
|
||||
|
||||
export type SplitRealtimeInterlockConfig = {
|
||||
splitEnabled: boolean;
|
||||
nativeRealtimeEnabled: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Boot-time realtime interlock (pure predicate). Split mode puts NEW-resident
|
||||
* (run-ops id) runs on the dedicated run-ops DB, but Electric replicates only from the
|
||||
* control-plane DB — with the native realtime backend OFF those runs are invisible
|
||||
* and every realtime subscription hangs. Refuse split unless native is on; split-off
|
||||
* is always allowed regardless of the realtime backend.
|
||||
*/
|
||||
export function assertSplitRealtimeInterlock(config: SplitRealtimeInterlockConfig): void {
|
||||
if (!config.splitEnabled) {
|
||||
return;
|
||||
}
|
||||
if (!config.nativeRealtimeEnabled) {
|
||||
throw new Error(
|
||||
"RUN_OPS_SPLIT_ENABLED is on but the native realtime backend (REALTIME_BACKEND_NATIVE_ENABLED) is not enabled — Electric cannot serve NEW-resident runs; refusing to enable split."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let cached: Promise<boolean> | undefined;
|
||||
|
||||
export function isSplitEnabled(): Promise<boolean> {
|
||||
if (!cached) {
|
||||
cached = computeSplitEnabled(
|
||||
{
|
||||
flagEnabled: env.RUN_OPS_SPLIT_ENABLED,
|
||||
legacyUrl: env.RUN_OPS_LEGACY_DATABASE_URL,
|
||||
newUrl: env.RUN_OPS_DATABASE_URL,
|
||||
},
|
||||
{ logger }
|
||||
);
|
||||
}
|
||||
return cached;
|
||||
}
|
||||
|
||||
export function __resetSplitModeCacheForTests(): void {
|
||||
cached = undefined;
|
||||
}
|
||||
Reference in New Issue
Block a user