Skip to content

Commit 935f078

Browse files
committed
perf(gateway): skip force port scan when free
1 parent 72072d8 commit 935f078

2 files changed

Lines changed: 14 additions & 8 deletions

File tree

src/cli/ports.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,10 @@ export async function forceFreePortAndWait(
266266
let killed: PortProcess[] = [];
267267
let useFuserFallback = false;
268268

269+
if (!(await isPortBusy(port))) {
270+
return { killed, waitedMs: 0, escalatedToSigkill: false };
271+
}
272+
269273
try {
270274
killed = forceFreePort(port);
271275
} catch (err) {

src/cli/program.force.test.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ describe("gateway --force helpers", () => {
3232
originalKill = process.kill.bind(process);
3333
originalPlatform = process.platform;
3434
tryListenOnPortMock.mockReset();
35+
tryListenOnPortMock.mockRejectedValue(
36+
Object.assign(new Error("in use"), { code: "EADDRINUSE" }),
37+
);
3538
// Pin to linux so all lsof tests are platform-invariant.
3639
Object.defineProperty(process, "platform", { value: "linux", configurable: true });
3740
});
@@ -59,12 +62,8 @@ describe("gateway --force helpers", () => {
5962
expect(listPortListeners(18789)).toEqual([]);
6063
});
6164

62-
it("does not re-scan lsof when no listeners were killed", async () => {
63-
(execFileSync as unknown as Mock).mockImplementation(() => {
64-
const err = new Error("no matches") as NodeJS.ErrnoException & { status?: number };
65-
err.status = 1; // lsof uses exit 1 for no matches
66-
throw err;
67-
});
65+
it("skips lsof when the port is already bindable", async () => {
66+
tryListenOnPortMock.mockResolvedValue(undefined);
6867

6968
const result = await forceFreePortAndWait(18789, { timeoutMs: 500, intervalMs: 100 });
7069

@@ -73,7 +72,7 @@ describe("gateway --force helpers", () => {
7372
waitedMs: 0,
7473
escalatedToSigkill: false,
7574
});
76-
expect(execFileSync).toHaveBeenCalledOnce();
75+
expect(execFileSync).not.toHaveBeenCalled();
7776
});
7877

7978
it("throws when lsof missing", () => {
@@ -181,7 +180,9 @@ describe("gateway --force helpers", () => {
181180
}
182181
return "18789/tcp: 4242\n";
183182
});
184-
tryListenOnPortMock.mockResolvedValue(undefined);
183+
tryListenOnPortMock
184+
.mockRejectedValueOnce(Object.assign(new Error("in use"), { code: "EADDRINUSE" }))
185+
.mockResolvedValue(undefined);
185186

186187
const result = await forceFreePortAndWait(18789, { timeoutMs: 500, intervalMs: 100 });
187188

@@ -216,6 +217,7 @@ describe("gateway --force helpers", () => {
216217
.mockRejectedValueOnce(busyErr)
217218
.mockRejectedValueOnce(busyErr)
218219
.mockRejectedValueOnce(busyErr)
220+
.mockRejectedValueOnce(busyErr)
219221
.mockResolvedValueOnce(undefined);
220222

221223
const promise = forceFreePortAndWait(18789, {

0 commit comments

Comments
 (0)