Skip to content

Commit 3b02915

Browse files
penalosapetebacondarwindevin-ai-integration[bot]
authored
[wrangler] Avoid stale remote binding sessions (#15142)
Co-authored-by: Pete Bacon Darwin <pete@bacondarwin.com> Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
1 parent d0c976c commit 3b02915

4 files changed

Lines changed: 70 additions & 21 deletions

File tree

.changeset/tidy-wolves-listen.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"wrangler": patch
3+
---
4+
5+
Fix remote binding sessions reusing stale binding configurations
6+
7+
Starting a new remote bindings session that reuses a Worker name no longer picks up the bindings from a previous session, which could cause `Binding "..." not found` errors.

packages/remote-bindings/src/startDevWorker/DevEnv.test.ts

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,30 +19,41 @@ const config: StartDevWorkerOptions = {
1919
};
2020

2121
describe("DevEnv", () => {
22-
it("changes the uploaded source on every update", ({ expect }) => {
23-
const devEnv = new DevEnv(config);
24-
const onBundleComplete =
25-
vi.fn<RemoteRuntimeController["onBundleComplete"]>();
26-
devEnv.proxy = { pause: vi.fn() } as unknown as ProxyController;
27-
devEnv.runtime = {
28-
onUpdateStart: vi.fn(),
29-
onBundleComplete,
30-
} as unknown as RemoteRuntimeController;
22+
it("changes the uploaded source for every update", ({ expect }) => {
23+
function captureUpdates(devEnv: DevEnv) {
24+
const onBundleComplete =
25+
vi.fn<RemoteRuntimeController["onBundleComplete"]>();
26+
devEnv.proxy = { pause: vi.fn() } as unknown as ProxyController;
27+
devEnv.runtime = {
28+
onUpdateStart: vi.fn(),
29+
onBundleComplete,
30+
} as unknown as RemoteRuntimeController;
31+
return onBundleComplete;
32+
}
3133

34+
const devEnv = new DevEnv(config);
35+
const firstDevEnvUpdates = captureUpdates(devEnv);
3236
devEnv.update(config);
3337
devEnv.update(config);
3438

35-
const [firstCall, secondCall] = onBundleComplete.mock.calls;
36-
if (!firstCall || !secondCall) {
39+
const otherDevEnv = new DevEnv(config);
40+
const otherDevEnvUpdates = captureUpdates(otherDevEnv);
41+
otherDevEnv.update(config);
42+
43+
const [firstCall, secondCall] = firstDevEnvUpdates.mock.calls;
44+
const [otherCall] = otherDevEnvUpdates.mock.calls;
45+
if (!firstCall || !secondCall || !otherCall) {
3746
throw new Error("Expected two bundle updates");
3847
}
39-
const firstBundle = firstCall[0].bundle;
40-
const secondBundle = secondCall[0].bundle;
41-
expect(firstBundle.entrypointSource).toBe(
42-
"export default {};\n// remote-bindings-update:1"
43-
);
44-
expect(secondBundle.entrypointSource).toBe(
45-
"export default {};\n// remote-bindings-update:2"
46-
);
48+
const firstSource = firstCall[0].bundle.entrypointSource;
49+
const secondSource = secondCall[0].bundle.entrypointSource;
50+
const otherSource = otherCall[0].bundle.entrypointSource;
51+
52+
for (const source of [firstSource, secondSource, otherSource]) {
53+
expect(source).toMatch(
54+
/^export default \{\};\n\/\/ remote-bindings-update:[0-9a-f-]+$/
55+
);
56+
}
57+
expect(new Set([firstSource, secondSource, otherSource]).size).toBe(3);
4758
});
4859
});

packages/remote-bindings/src/startDevWorker/DevEnv.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { randomUUID } from "node:crypto";
12
import { EventEmitter } from "node:events";
23
import { UserError } from "@cloudflare/workers-utils";
34
import { MiniflareCoreError } from "miniflare";
@@ -11,7 +12,6 @@ export class DevEnv extends EventEmitter {
1112
runtime: RemoteRuntimeController;
1213
proxy: ProxyController;
1314
#bundle: Bundle;
14-
#bundleVersion = 0;
1515
#config: StartDevWorkerOptions;
1616

1717
start() {
@@ -29,7 +29,7 @@ export class DevEnv extends EventEmitter {
2929
bundle: {
3030
...this.#bundle,
3131
// Ensure binding-only updates cannot reuse the previous edge-preview artifact.
32-
entrypointSource: `${this.#bundle.entrypointSource}\n// remote-bindings-update:${++this.#bundleVersion}`,
32+
entrypointSource: `${this.#bundle.entrypointSource}\n// remote-bindings-update:${randomUUID()}`,
3333
},
3434
});
3535
}

packages/wrangler/e2e/remote-binding/remote-bindings-api.test.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { resolve } from "node:path";
2+
import { fetch } from "undici";
23
import { assert, beforeAll, describe, test } from "vitest";
34
import { CLOUDFLARE_ACCOUNT_ID } from "../helpers/account-id";
45
import {
@@ -102,6 +103,36 @@ describe.skipIf(!CLOUDFLARE_ACCOUNT_ID)(
102103
await remoteProxySession.dispose();
103104
});
104105

106+
test("handles different bindings across fresh sessions with the same Worker name", async ({
107+
expect,
108+
}) => {
109+
for (let i = 0; i < 2; i++) {
110+
const bindingName =
111+
i % 2 === 0 ? "REMOTE_WORKER_A" : "REMOTE_WORKER_B";
112+
const remoteProxySession = await startRemoteProxySession(
113+
{
114+
[bindingName]: {
115+
type: "service",
116+
service: remoteWorkerName,
117+
},
118+
},
119+
{ workerName: "remote-bindings-fresh-session-stress-test" }
120+
);
121+
122+
try {
123+
const response = await fetch(
124+
remoteProxySession.remoteProxyConnectionString,
125+
{ headers: { "MF-Binding": bindingName } }
126+
);
127+
expect(await response.text(), `iteration ${i}`).toBe(
128+
"Hello from a remote worker"
129+
);
130+
} finally {
131+
await remoteProxySession.dispose();
132+
}
133+
}
134+
});
135+
105136
test("user provided incorrect auth data", async ({ expect }) => {
106137
let error: unknown;
107138
try {

0 commit comments

Comments
 (0)