Repository navigation
fix(client): a thread whose subscription dies on a defect resubscribes instead of staying blank #25919
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
fix(client): a thread whose subscription dies on a defect resubscribes instead of staying blank #25919
Changes from all commits
6e353ff
16e2e5c
63bb057
a7c8c09
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -32,7 +32,7 @@ import * as ConnectionWakeups from "../connection/wakeups.ts"; | |
| import * as Persistence from "../platform/persistence.ts"; | ||
| import { runCachePersistence } from "./cachePersistence.ts"; | ||
| import * as ManagedRelay from "../relay/managedRelay.ts"; | ||
| import { request, subscribeDynamic } from "../rpc/client.ts"; | ||
| import { request, subscribeDynamic, subscriptionRetryDelay } from "../rpc/client.ts"; | ||
| import { parseThreadKey, threadKey } from "./entities.ts"; | ||
| import { applyOrchestrationV2ProjectionEvent } from "./orchestrationV2Projection.ts"; | ||
| import { THREAD_SNAPSHOT_IDLE_TTL_MS } from "./threadRetention.ts"; | ||
|
|
@@ -55,6 +55,8 @@ import { | |
| type EnvironmentThreadStatus, | ||
| } from "./threadState.ts"; | ||
|
|
||
| const THREAD_SUBSCRIPTION_RETRY_DELAY = "250 millis"; | ||
|
|
||
| function statusWithoutLiveData( | ||
| data: Option.Option<OrchestrationV2ThreadProjection>, | ||
| ): EnvironmentThreadStatus { | ||
|
|
@@ -878,7 +880,12 @@ export const makeEnvironmentThreadState = Effect.fn("EnvironmentThreadState.make | |
| }); | ||
|
|
||
| yield* markSynchronizing; | ||
| yield* Effect.forkScoped( | ||
| let defectRetries = 0; | ||
| const followThread = (): Effect.Effect< | ||
| void, | ||
| never, | ||
| EnvironmentSupervisor.EnvironmentSupervisor | ||
| > => | ||
| subscribeDynamic( | ||
| ORCHESTRATION_V2_WS_METHODS.subscribeThread, | ||
| Effect.fn("EnvironmentThreadState.makeSubscribeInput")(function* (session) { | ||
|
|
@@ -982,11 +989,37 @@ export const makeEnvironmentThreadState = Effect.fn("EnvironmentThreadState.make | |
| { | ||
| onDefect: () => setStreamError("Could not synchronize the thread."), | ||
| onExpectedFailure: (cause) => setStreamError(formatThreadError(cause)), | ||
| retryExpectedFailureAfter: "250 millis", | ||
| retryExpectedFailureAfter: THREAD_SUBSCRIPTION_RETRY_DELAY, | ||
| resubscribe: foregroundResubscriptions, | ||
| }, | ||
| ).pipe(Stream.runForEachArray(applyItems)), | ||
| ); | ||
| ).pipe( | ||
| Stream.runForEachArray((items) => | ||
| applyItems(items).pipe( | ||
| Effect.andThen( | ||
| Effect.sync(() => { | ||
| defectRetries = 0; | ||
| }), | ||
| ), | ||
| ), | ||
| ), | ||
| // A defect (an undecodable snapshot, or one applyItems cannot handle) ends the | ||
| // subscription for good; resubscribe the way reselecting the thread would. | ||
| Effect.catchCause((cause) => { | ||
| if (Cause.hasInterrupts(cause)) return Effect.interrupt; | ||
| const retryDelay = subscriptionRetryDelay(THREAD_SUBSCRIPTION_RETRY_DELAY, defectRetries); | ||
| defectRetries += 1; | ||
| return Effect.logWarning("Thread subscription failed; resubscribing.", { | ||
| cause: Cause.pretty(cause), | ||
| environmentId, | ||
| threadId, | ||
| }).pipe( | ||
| Effect.andThen(setStreamError("Could not synchronize the thread.")), | ||
| Effect.andThen(Effect.sleep(retryDelay)), | ||
| Effect.andThen(Effect.suspend(followThread)), | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| ); | ||
| }), | ||
| ); | ||
| yield* Effect.forkScoped(followThread()); | ||
|
|
||
| return state; | ||
| }); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove raw defect text from the warning annotation.
If a decode error contains a rejected thread payload,
Cause.pretty(cause)copies that payload into logs. The annotation is also unbounded. Log a bounded failure category instead. As per coding guidelines, “Attributes and log annotations stay bounded: no raw payloads … or arbitrary defect text.” (raw.githubusercontent.com)Source: Coding guidelines