Skip to content
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

fix(sdk): use lockfiles to prevent multiple simulators on the same state directory #6911

Merged
Show file tree
Hide file tree
Changes from 3 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
2 changes: 2 additions & 0 deletions docs/api/04-standard-library/aws/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -1747,6 +1747,8 @@ Add a Lambda layer to the function.

- *Type:* str

The ARN of the layer.

---

#### Properties <a name="Properties" id="Properties"></a>
Expand Down
47 changes: 44 additions & 3 deletions libs/wingsdk/src/simulator/simulator.ts
skyrpex marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { existsSync } from "fs";
import { mkdir, rm } from "fs/promises";
import {
mkdir,
rm,
open as openFileHandle,
type FileHandle,
} from "fs/promises";
import type { Server, IncomingMessage, ServerResponse } from "http";
import { join, resolve } from "path";
import { makeSimulatorClient } from "./client";
Expand Down Expand Up @@ -225,9 +230,13 @@ export class Simulator {
// keeps the running state of all resources.
private runningState: Record<string, ResourceRunningState> = {};

private lockfileFilename: string;
private lockfile: FileHandle | undefined;

constructor(props: SimulatorProps) {
const simdir = resolve(props.simfile);
this.statedir = props.stateDir ?? join(simdir, ".state");
this.lockfileFilename = join(this.statedir, "lock");

this._running = "stopped";
this._handles = new HandleManager();
Expand Down Expand Up @@ -303,9 +312,9 @@ export class Simulator {
}
this._running = "starting";

await this.startServer();

try {
await this.acquireStateLock();
await this.startServer();
await this.startResources();
} catch (err: any) {
this.stopServer();
Expand All @@ -314,6 +323,36 @@ export class Simulator {
}
}

private async acquireStateLock() {
if (this.lockfile) {
return; // already locked
}

await mkdir(this.statedir, { recursive: true });
try {
this.lockfile = await openFileHandle(this.lockfileFilename, "wx");
skyrpex marked this conversation as resolved.
Show resolved Hide resolved
} catch (error) {
if (
error instanceof Error // && error.code === "EEXIST"
) {
throw new Error(
"Another instance of the simulator is already running. Please stop the current simulation before starting a new one."
);
}
throw error;
}
}

private async releaseStateLock() {
if (!this.lockfile) {
return; // not locked
}

await this.lockfile.close();
await rm(this.lockfileFilename);
this.lockfile = undefined;
}

private async startResources() {
const retries: Record<string, number> = {};
const queue = this._model.graph.nodes.map((n) => n.path);
Expand Down Expand Up @@ -413,6 +452,8 @@ export class Simulator {

this.stopServer();

await this.releaseStateLock();

this._handles.reset();
this._running = "stopped";
}
Expand Down
Loading