Skip to content

Commit c877949

Browse files
committed
fix(e2e): guard destroy() and serialize state file writes
- destroy() now checks pid/appDir before calling awaitableTreekill/fs.rm, safely handling cases where init() never ran or failed - Wrap addLongRunningApp with a file lock to prevent concurrent read-modify-write races when multiple apps initialize in parallel
1 parent 72e025a commit c877949

1 file changed

Lines changed: 29 additions & 14 deletions

File tree

integration/models/longRunningApplication.ts

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -179,16 +179,23 @@ export const longRunningApplication = (params: LongRunningApplicationParams) =>
179179
pid = serveResult.pid;
180180
appDir = app.appDir;
181181
log(`Serve complete: port=${port}, serverUrl=${serverUrl}, pid=${pid}`);
182-
stateFile.addLongRunningApp({
183-
port,
184-
serverUrl,
185-
pid,
186-
id,
187-
appDir,
188-
env: params.env.toJson(),
189-
clerkFapi: process.env.CLERK_FAPI,
190-
clerkTestingToken: process.env.CLERK_TESTING_TOKEN,
191-
});
182+
// Serialize state file writes across all apps to prevent concurrent
183+
// read-modify-write from clobbering entries written by other workers.
184+
const releaseStateFileLock = await acquireProcessLock('__state-file__');
185+
try {
186+
stateFile.addLongRunningApp({
187+
port,
188+
serverUrl,
189+
pid,
190+
id,
191+
appDir,
192+
env: params.env.toJson(),
193+
clerkFapi: process.env.CLERK_FAPI,
194+
clerkTestingToken: process.env.CLERK_TESTING_TOKEN,
195+
});
196+
} finally {
197+
releaseStateFileLock();
198+
}
192199
} catch (error) {
193200
console.error('Error during app serve:', error);
194201
throw error;
@@ -236,11 +243,19 @@ export const longRunningApplication = (params: LongRunningApplicationParams) =>
236243
// will be called by global.teardown.ts
237244
destroy: async () => {
238245
readFromStateFile();
246+
if (!pid && !appDir) {
247+
console.log(`Skipping destroy for ${name}: no pid or appDir`);
248+
return;
249+
}
239250
console.log(`Destroying ${serverUrl}`);
240-
await awaitableTreekill(pid, 'SIGKILL');
241-
// TODO: Test whether this is necessary now that we have awaitableTreekill
242-
await new Promise(res => setTimeout(res, 2000));
243-
await fs.rm(appDir, { recursive: true, force: true });
251+
if (pid) {
252+
await awaitableTreekill(pid, 'SIGKILL');
253+
// TODO: Test whether this is necessary now that we have awaitableTreekill
254+
await new Promise(res => setTimeout(res, 2000));
255+
}
256+
if (appDir) {
257+
await fs.rm(appDir, { recursive: true, force: true });
258+
}
244259
},
245260
// read the persisted state and behave like an app
246261
commit: () => {

0 commit comments

Comments
 (0)