8b0385c429
The SDK discovers an external deployment id at runtime (explicit TRIGGER_EXTERNAL_DEPLOYMENT_ID always; platform commit-SHA variables and generic fallbacks when TRIGGER_AUTOMATIC_SKEW_VERSION_PROTECTION=1) and sends it alongside lockToVersion; the server resolves precedence (version > external id > current). An id held by a deployed deployment pins the run to that worker; an in-flight or unknown id parks the run in PENDING_VERSION with the id in TaskRun.annotations, wakes it pinned when a deployment carrying the id finalizes (ClickHouse candidates, Postgres authoritative), and expires it after a deadline that re-checks Postgres before acting. Parking outranks delaying and preserves delayUntil. The id is projected to ClickHouse task_runs_v2.external_deployment_id during replication. Redis cache for id-to-worker resolution, guarded version-aware writes. Ids are not unique. Several deployments can hold one id - a --force rebuild is the ordinary way to get there - so resolution always picks the highest version among the candidates, never the newest by timestamp. The rule is applied identically on both paths that can bind a run to a worker: resolveExternalDeployment at trigger time, and PendingVersionSystem when a landing deployment wakes a parked run. Version comparison is numeric on the counter half, so 20260807.10 outranks 20260807.9. A run whose id never lands expires at the deadline with EXTERNAL_DEPLOYMENT_NOT_FOUND and an error naming the id it waited for, which is what a failed build or a typo looks like from the caller. Default deadline is one hour (EXTERNAL_DEPLOYMENT_PARK_DEADLINE_MS). Debounce registration happens in both the parked and the delayed branch through one helper, so a debounced run that parks still binds its debounce key; without it every later trigger for the same key created another parked run, and all of them executed when the deployment landed. The two DELAYED-only status checks in DebounceSystem also accept PENDING_VERSION, without which the lock-contention fallback would rethrow a 5xx the SDK retries and amplifies, and the fast path would push every trigger on a parked key through the redlock. Resolution is skipped in development. A dev environment cannot hold a WorkerDeployment - trigger dev registers a BackgroundWorker with nothing behind it, and deploy --env refuses dev - so an external deployment id there could only ever park, and the parked run then expired against the dev TTL while a connected dev worker sat idle. The id is still annotated so the dashboard shows what the app sent (TRI-13000).
39 lines
1.3 KiB
TypeScript
39 lines
1.3 KiB
TypeScript
import { defaultReconnectOnError } from "@internal/redis";
|
|
import Redis from "ioredis";
|
|
import { env } from "~/env.server";
|
|
import { singleton } from "~/utils/singleton";
|
|
import {
|
|
type ExternalDeploymentCache,
|
|
NoopExternalDeploymentCache,
|
|
RedisExternalDeploymentCache,
|
|
} from "./externalDeploymentCache.server";
|
|
|
|
export const externalDeploymentCacheInstance: ExternalDeploymentCache = singleton(
|
|
"externalDeploymentCacheInstance",
|
|
initializeExternalDeploymentCache
|
|
);
|
|
|
|
function initializeExternalDeploymentCache(): ExternalDeploymentCache {
|
|
if (!env.EXTERNAL_DEPLOYMENT_CACHE_REDIS_HOST) {
|
|
return new NoopExternalDeploymentCache();
|
|
}
|
|
|
|
const redis = new Redis({
|
|
connectionName: "externalDeploymentCache",
|
|
host: env.EXTERNAL_DEPLOYMENT_CACHE_REDIS_HOST,
|
|
port: env.EXTERNAL_DEPLOYMENT_CACHE_REDIS_PORT,
|
|
username: env.EXTERNAL_DEPLOYMENT_CACHE_REDIS_USERNAME,
|
|
password: env.EXTERNAL_DEPLOYMENT_CACHE_REDIS_PASSWORD,
|
|
keyPrefix: "tr:",
|
|
enableAutoPipelining: true,
|
|
reconnectOnError: defaultReconnectOnError,
|
|
...(env.EXTERNAL_DEPLOYMENT_CACHE_REDIS_TLS_DISABLED === "true" ? {} : { tls: {} }),
|
|
});
|
|
|
|
return new RedisExternalDeploymentCache({
|
|
redis,
|
|
ttlSeconds: env.EXTERNAL_DEPLOYMENT_CACHE_TTL_SECONDS,
|
|
missingTtlSeconds: env.EXTERNAL_DEPLOYMENT_CACHE_MISSING_TTL_SECONDS,
|
|
});
|
|
}
|