730 lines · 22.1 KB · TypeScript
RawDownload
import { withRelayClientTracing } from "@t3tools/shared/relayTracing";import * as Cause from "effect/Cause";import * as Clock from "effect/Clock";import * as Context from "effect/Context";import * as Effect from "effect/Effect";import * as Exit from "effect/Exit";import * as Fiber from "effect/Fiber";import * as Layer from "effect/Layer";import * as Option from "effect/Option";import * as Queue from "effect/Queue";import * as Ref from "effect/Ref";import * as Scope from "effect/Scope";import * as Stream from "effect/Stream";import * as SubscriptionRef from "effect/SubscriptionRef";import * as Tracer from "effect/Tracer"; import type { ConnectionCatalogEntry } from "./catalog.ts";import * as Connectivity from "./connectivity.ts";import * as ConnectionDriver from "./driver.ts";import {  type ConnectionAttemptError,  type ConnectionTarget,  ConnectionTransientError,  type NetworkStatus,  type PreparedConnection,  type SupervisorConnectionState,} from "./model.ts";import * as RpcSession from "../rpc/session.ts";import { safeErrorLogAttributes } from "../errors/safeLog.ts";import * as ConnectionWakeups from "./wakeups.ts"; const RETRY_DELAYS_MS = [1_000, 2_000, 4_000, 8_000, 16_000] as const;const CONNECTION_ESTABLISHMENT_TIMEOUT = "15 seconds";const CONNECTION_PROBE_TIMEOUT = "15 seconds";const BACKOFF_RESET_AFTER_MS = 30_000; interface SupervisorIntent {  readonly desired: boolean;  readonly network: NetworkStatus;} type SupervisorSignal =  | { readonly _tag: "ConnectRequested" }  | { readonly _tag: "DisconnectRequested" }  | { readonly _tag: "RetryRequested" }  | { readonly _tag: "NetworkChanged"; readonly network: NetworkStatus }  | { readonly _tag: "Wakeup"; readonly reason: ConnectionWakeups.ConnectionWakeup }; interface PendingRetryTrace {  readonly previousAttempt: Tracer.Span;  readonly failureCount: number;  readonly delayMs: number;  readonly reason: ConnectionAttemptError["reason"];} interface TracedAttemptFailure {  readonly error: ConnectionAttemptError;  readonly attemptSpan: Option.Option<Tracer.Span>;} type AttemptOutcome =  | {      readonly _tag: "Interrupted";      readonly established: boolean;      readonly stable: boolean;    }  | {      readonly _tag: "Failure";      readonly established: boolean;      readonly stable: boolean;      readonly failure: TracedAttemptFailure;    }; type EstablishmentEvent =  | {      readonly _tag: "Completed";      readonly exit: Exit.Exit<        {          readonly attemptSpan: Option.Option<Tracer.Span>;          readonly lease: ConnectionDriver.EnvironmentConnectionLease;        },        TracedAttemptFailure      >;    }  | { readonly _tag: "Interrupted" }  | { readonly _tag: "TimedOut" }; function exitUnlessInterrupted<A, E, R>(  effect: Effect.Effect<A, E, R>,): Effect.Effect<Exit.Exit<A, E>, never, R> {  return Effect.matchCauseEffect(effect, {    onFailure: (cause) =>      Cause.hasInterrupts(cause) ? Effect.interrupt : Effect.succeed(Exit.failCause(cause)),    onSuccess: (value) => Effect.succeed(Exit.succeed(value)),  });} export interface EnvironmentSupervisorOptions {  readonly initiallyDesired?: boolean;} function retryDelayMs(failureCount: number): number {  return RETRY_DELAYS_MS[Math.min(failureCount, RETRY_DELAYS_MS.length - 1)] ?? 16_000;} function annotateTarget(target: ConnectionTarget) {  return Effect.annotateCurrentSpan({    "environment.id": target.environmentId,    "environment.label": target.label,    "environment.target.kind": target._tag,  });} function availableState(intent: SupervisorIntent, generation: number): SupervisorConnectionState {  return {    desired: false,    network: intent.network,    phase: "available",    stage: null,    attempt: 0,    generation,    lastFailure: null,    retryAt: null,  };} function offlineState(  intent: SupervisorIntent,  generation: number,  attempt: number,  lastFailure: ConnectionAttemptError | null,): SupervisorConnectionState {  return {    desired: true,    network: intent.network,    phase: "offline",    stage: null,    attempt,    generation,    lastFailure,    retryAt: null,  };} function connectingState(  intent: SupervisorIntent,  generation: number,  attempt: number,  lastFailure: ConnectionAttemptError | null,  stage: SupervisorConnectionState["stage"] = "preparing",): SupervisorConnectionState {  return {    desired: true,    network: intent.network,    phase: "connecting",    stage,    attempt,    generation,    lastFailure,    retryAt: null,  };} function failureFromExit<A>(  target: ConnectionTarget,  exit: Exit.Exit<A, TracedAttemptFailure>,  established: boolean,  stable: boolean,): AttemptOutcome {  if (Exit.isSuccess(exit) || Cause.hasInterruptsOnly(exit.cause)) {    return { _tag: "Interrupted", established, stable };  }  const typedFailure = exit.cause.reasons.find(Cause.isFailReason);  if (typedFailure) {    return {      _tag: "Failure",      established,      stable,      failure: typedFailure.error,    };  }  return {    _tag: "Failure",    established,    stable,    failure: {      error: new ConnectionTransientError({        reason: "transport",        detail: `${target.label} connection failed unexpectedly.`,      }),      attemptSpan: Option.none(),    },  };} export class EnvironmentSupervisor extends Context.Service<  EnvironmentSupervisor,  {    readonly target: ConnectionTarget;    readonly state: SubscriptionRef.SubscriptionRef<SupervisorConnectionState>;    readonly session: SubscriptionRef.SubscriptionRef<Option.Option<RpcSession.RpcSession>>;    readonly prepared: SubscriptionRef.SubscriptionRef<Option.Option<PreparedConnection>>;    readonly connect: Effect.Effect<void>;    readonly disconnect: Effect.Effect<void>;    readonly retryNow: Effect.Effect<void>;  }>()("@t3tools/client-runtime/connection/supervisor/EnvironmentSupervisor") {} export const make = Effect.fn("EnvironmentSupervisor.make")(function* (  entry: ConnectionCatalogEntry,  options?: EnvironmentSupervisorOptions,): Effect.fn.Return<  EnvironmentSupervisor["Service"],  never,  | Connectivity.Connectivity  | ConnectionDriver.ConnectionDriver  | Scope.Scope  | ConnectionWakeups.ConnectionWakeups> {  const target = entry.target;  yield* annotateTarget(target);   const connectivity = yield* Connectivity.Connectivity;  const driver = yield* ConnectionDriver.ConnectionDriver;  const wakeups = yield* ConnectionWakeups.ConnectionWakeups;  const initialIntent: SupervisorIntent = {    desired: options?.initiallyDesired ?? false,    network: yield* connectivity.status,  };  const intent = yield* Ref.make(initialIntent);  const signals = yield* Queue.unbounded<SupervisorSignal>();  const state = yield* SubscriptionRef.make<SupervisorConnectionState>(    !initialIntent.desired      ? availableState(initialIntent, 0)      : initialIntent.network === "offline"        ? offlineState(initialIntent, 0, 0, null)        : connectingState(initialIntent, 0, 1, null),  );  const session = yield* SubscriptionRef.make<Option.Option<RpcSession.RpcSession>>(Option.none());  const prepared = yield* SubscriptionRef.make<Option.Option<PreparedConnection>>(Option.none());   const clearLease = Effect.all(    [SubscriptionRef.set(session, Option.none()), SubscriptionRef.set(prepared, Option.none())],    { discard: true },  );   const setState = Effect.fn("EnvironmentSupervisor.setState")(function* (    next: SupervisorConnectionState,  ) {    yield* SubscriptionRef.set(state, next);  });   const signal = Effect.fn("EnvironmentSupervisor.signal")(function* (next: SupervisorSignal) {    yield* Queue.offer(signals, next);  });   const logManagedRelayAccountChange = Effect.logInfo(    "Managed relay account changed; restarting the environment connection.",  ).pipe(    Effect.annotateLogs({      "environment.id": target.environmentId,      "environment.label": target.label,    }),  );   const reportProgress = Effect.fn("EnvironmentSupervisor.reportProgress")(function* (    attempt: number,    generation: number,    lastFailure: ConnectionAttemptError | null,    progress: ConnectionDriver.ConnectionDriverProgress,  ) {    if ("prepared" in progress) {      yield* SubscriptionRef.set(prepared, Option.some(progress.prepared));    }    yield* setState(      connectingState(yield* Ref.get(intent), generation, attempt, lastFailure, progress.stage),    );  });   const establishConnection = Effect.fnUntraced(function* (    attempt: number,    generation: number,    lastFailure: ConnectionAttemptError | null,  ) {    return yield* driver.connect(entry, (progress) =>      reportProgress(attempt, generation, lastFailure, progress),    );  });   const traceRelayEstablishment = (    effect: Effect.Effect<      ConnectionDriver.EnvironmentConnectionLease,      ConnectionAttemptError,      Scope.Scope    >,    attempt: number,    generation: number,    pendingRetry: Option.Option<PendingRetryTrace>,  ) => {    const traced = Effect.gen(function* () {      const attemptSpan = yield* Effect.currentSpan.pipe(Effect.orDie);      yield* annotateTarget(target);      yield* Effect.annotateCurrentSpan({        "connection.attempt": attempt,        "connection.generation": generation,        "connection.retry.failure_count": Option.match(pendingRetry, {          onNone: () => 0,          onSome: (retry) => retry.failureCount,        }),      });      const lease = yield* effect.pipe(        Effect.mapError(          (error): TracedAttemptFailure => ({            error,            attemptSpan: Option.some(attemptSpan),          }),        ),      );      return { attemptSpan: Option.some(attemptSpan), lease };    }).pipe(Effect.withSpan("relay.connection.attempt", { root: true }));     return Option.match(pendingRetry, {      onNone: () => traced,      onSome: (retry) =>        traced.pipe(          Effect.linkSpans(retry.previousAttempt, {            "connection.retry.delay_ms": retry.delayMs,            "connection.retry.reason": retry.reason,          }),        ),    }).pipe(withRelayClientTracing);  };   const establishTracedConnection = Effect.fnUntraced(function* (    attempt: number,    generation: number,    lastFailure: ConnectionAttemptError | null,    pendingRetry: Option.Option<PendingRetryTrace>,  ) {    if (target._tag === "RelayConnectionTarget") {      return yield* traceRelayEstablishment(        establishConnection(attempt, generation, lastFailure),        attempt,        generation,        pendingRetry,      );    }    return yield* establishConnection(attempt, generation, lastFailure).pipe(      Effect.map((lease) => ({        attemptSpan: Option.none<Tracer.Span>(),        lease,      })),      Effect.mapError(        (error): TracedAttemptFailure => ({          error,          attemptSpan: Option.none(),        }),      ),    );  });   const waitForEstablishmentInterrupt = Effect.fnUntraced(function* () {    for (;;) {      const next = yield* Queue.take(signals);      switch (next._tag) {        case "DisconnectRequested":        case "RetryRequested":          return;        case "NetworkChanged":          if (next.network === "offline") {            return;          }          break;        case "ConnectRequested":          break;        case "Wakeup":          if (next.reason === "credentials-changed" && target._tag === "RelayConnectionTarget") {            yield* logManagedRelayAccountChange;            return;          }          break;      }    }  });   const monitorConnectedLease = Effect.fnUntraced(function* (    lease: ConnectionDriver.EnvironmentConnectionLease,  ) {    for (;;) {      const next = yield* Queue.take(signals);      switch (next._tag) {        case "DisconnectRequested":        case "RetryRequested":          return;        case "NetworkChanged":          if (next.network === "offline") {            return;          }          break;        case "Wakeup":          if (next.reason === "credentials-changed" && target._tag === "RelayConnectionTarget") {            yield* logManagedRelayAccountChange;            return;          }          if (next.reason === "application-active") {            const probe = yield* lease.session.probe.pipe(              Effect.timeoutOrElse({                duration: CONNECTION_PROBE_TIMEOUT,                orElse: () =>                  Effect.fail(                    new ConnectionTransientError({                      reason: "timeout",                      detail: `${target.label} did not respond to a connection health check.`,                    }),                  ),              }),              Effect.forkChild,            );            for (;;) {              const probeEvent = yield* Effect.raceFirst(                Fiber.await(probe).pipe(                  Effect.map((exit) => ({ _tag: "ProbeCompleted" as const, exit })),                ),                Queue.take(signals).pipe(                  Effect.map((signal) => ({ _tag: "Signal" as const, signal })),                ),              );              if (probeEvent._tag === "ProbeCompleted") {                yield* probeEvent.exit;                break;              }              switch (probeEvent.signal._tag) {                case "DisconnectRequested":                case "RetryRequested":                  yield* Fiber.interrupt(probe);                  return;                case "NetworkChanged":                  if (probeEvent.signal.network === "offline") {                    yield* Fiber.interrupt(probe);                    return;                  }                  break;                case "ConnectRequested":                case "Wakeup":                  break;              }            }          }          break;        case "ConnectRequested":          break;      }    }  });   const runAttempt = Effect.fnUntraced(function* (    attempt: number,    generation: number,    lastFailure: ConnectionAttemptError | null,    pendingRetry: Option.Option<PendingRetryTrace>,  ) {    yield* SubscriptionRef.set(prepared, Option.none());    const establishment = yield* Effect.raceAllFirst([      exitUnlessInterrupted(        establishTracedConnection(attempt, generation, lastFailure, pendingRetry),      ).pipe(        Effect.map(          (exit): EstablishmentEvent => ({            _tag: "Completed",            exit,          }),        ),      ),      waitForEstablishmentInterrupt().pipe(Effect.as<EstablishmentEvent>({ _tag: "Interrupted" })),      Effect.sleep(CONNECTION_ESTABLISHMENT_TIMEOUT).pipe(        Effect.as<EstablishmentEvent>({ _tag: "TimedOut" }),      ),    ]);     if (establishment._tag === "Interrupted") {      return {        _tag: "Interrupted",        established: false,        stable: false,      } satisfies AttemptOutcome;    }    if (establishment._tag === "TimedOut") {      return {        _tag: "Failure",        established: false,        stable: false,        failure: {          error: new ConnectionTransientError({            reason: "timeout",            detail: `${target.label} did not respond during connection setup.`,          }),          attemptSpan: Option.none(),        },      } satisfies AttemptOutcome;    }

Lines 1–500 of 730.