Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion engine/packages/guard-core/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,6 @@ fn is_retryable_guard_http_error(code: &str) -> bool {
| "tunnel_request_aborted"
| "tunnel_message_timeout"
| "tunnel_response_closed"
| "gateway_response_start_timeout"
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { actor } from "rivetkit";

export const responseStartTimeoutActor = actor({
state: { runs: 0 },
createVars: () => ({ waiting: [] as Array<() => void> }),
actions: {
// Only the first run waits for `release`, so a duplicate run reaches the
// caller as a result instead of a timeout.
waitForRelease: async (c) => {
c.state.runs += 1;
if (c.state.runs === 1) {
await new Promise<void>((resolve) =>
c.vars.waiting.push(resolve),
);
}
return c.state.runs;
},
release: (c) => {
for (const resolve of c.vars.waiting.splice(0)) resolve();
},
runs: (c) => c.state.runs,
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import {
} from "./destroy";
import { customTimeoutActor, errorHandlingActor } from "./error-handling";
import { fileSystemHibernationCleanupActor } from "./file-system-hibernation-cleanup";
import { responseStartTimeoutActor } from "./gateway-response-start-timeout";
import { hibernationActor, hibernationSleepWindowActor } from "./hibernation";
import { kvActor } from "./kv";
import { largePayloadActor, largePayloadConnActor } from "./large-payloads";
Expand Down Expand Up @@ -276,6 +277,8 @@ export const registry = setup({
manyQueueRunParentActor,
// From action-inputs.ts
inputActor,
// From gateway-response-start-timeout.ts
responseStartTimeoutActor,
// From action-timeout.ts
shortTimeoutActor,
longTimeoutActor,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,7 @@ function isRetryableGuardGatewayHttpError(code: string): boolean {
code === "actor_stopped_while_waiting" ||
code === "tunnel_request_aborted" ||
code === "tunnel_message_timeout" ||
code === "tunnel_response_closed" ||
code === "gateway_response_start_timeout"
code === "tunnel_response_closed"
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { describe, expect, test } from "vitest";
import { describeDriverMatrix } from "./shared-matrix";
import { setupDriverTest } from "./shared-utils";

describeDriverMatrix(
"Gateway Response Start Timeout",
(driverTestConfig) => {
describe("gateway response start timeout", () => {
test("an action that outlives the timeout runs once, and the caller gets the timeout", async (c) => {
const { client } = await setupDriverTest(c, driverTestConfig);
const actor = client.responseStartTimeoutActor.getOrCreate([
"runs-once",
]);

// The action reached the actor, so its result is unknown and
// neither the gateway nor the client may send it again.
await expect(actor.waitForRelease()).rejects.toMatchObject({
group: "guard",
code: "gateway_response_start_timeout",
});
await actor.release();
expect(await actor.runs()).toBe(1);
});
});
},
{
runtimes: ["native"],
encodings: ["bare"],
sqliteBackends: ["remote"],
config: { engine: { gatewayResponseStartTimeoutMs: 2_000 } },
},
);
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ describe("lifecycle error retry classification", () => {
"tunnel_request_aborted",
"tunnel_message_timeout",
"tunnel_response_closed",
"gateway_response_start_timeout",
])("classifies guard.%s as retryable", (code) => {
expect(
isRetryableLifecycleRequestError(
Expand Down
26 changes: 18 additions & 8 deletions rivetkit-typescript/packages/rivetkit/tests/shared-engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export interface SharedTestEngine {

export interface SharedTestEngineOptions {
gateway3?: boolean;
gatewayResponseStartTimeoutMs?: number;
}

interface SharedEngineFiles {
Expand All @@ -57,7 +58,10 @@ let sharedEngineProfile: string | undefined;
let sharedEngineFiles: SharedEngineFiles | undefined;

function resolveSharedEngineProfile(options: SharedTestEngineOptions): string {
return JSON.stringify({ gateway3: options.gateway3 ?? false });
return JSON.stringify({
gateway3: options.gateway3 ?? false,
gatewayResponseStartTimeoutMs: options.gatewayResponseStartTimeoutMs,
});
}

function resolveSharedEngineFiles(profile: string): SharedEngineFiles {
Expand Down Expand Up @@ -289,13 +293,19 @@ async function spawnSharedEngine(
},
},
},
pegboard: options.gateway3
? {
gateway_update_ping_interval_ms: 250,
gateway_tunnel_ping_timeout_ms: 2_000,
gateway_response_start_timeout_ms: 3_000,
}
: undefined,
// JSON.stringify drops undefined fields, and an empty pegboard
// section is the same as none.
pegboard: {
...(options.gateway3
? {
gateway_update_ping_interval_ms: 250,
gateway_tunnel_ping_timeout_ms: 2_000,
}
: {}),
gateway_response_start_timeout_ms:
options.gatewayResponseStartTimeoutMs ??
(options.gateway3 ? 3_000 : undefined),
},
features: options.gateway3
? {
guard_gateway_v3: {
Expand Down
Loading