fix: address follow-up review regressions
This commit is contained in:
parent
330fe7e63c
commit
6b15e68fc3
|
|
@ -6,6 +6,7 @@ import {
|
|||
openSync,
|
||||
closeSync,
|
||||
constants,
|
||||
statSync,
|
||||
} from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { homedir } from "node:os";
|
||||
|
|
@ -24,6 +25,7 @@ const STATE_FILE = join(STATE_DIR, "running.json");
|
|||
const STATE_LOCK_FILE = join(STATE_DIR, "running.lock");
|
||||
const STARTUP_LOCK_FILE = join(STATE_DIR, "startup.lock");
|
||||
const MAX_LOCK_AGE_MS = 30 * 60 * 1000;
|
||||
const UNPARSEABLE_LOCK_GRACE_MS = 5_000;
|
||||
|
||||
interface LockMetadata {
|
||||
pid: number;
|
||||
|
|
@ -62,10 +64,20 @@ function readLockMetadata(lockFile: string): LockMetadata | null {
|
|||
}
|
||||
|
||||
function isStaleLockOwner(owner: LockMetadata): boolean {
|
||||
if (isProcessAlive(owner.pid)) return false;
|
||||
const acquiredAt = Date.parse(owner.acquiredAt);
|
||||
return Number.isFinite(acquiredAt) && Date.now() - acquiredAt > MAX_LOCK_AGE_MS;
|
||||
}
|
||||
|
||||
function isStaleUnparseableLock(lockFile: string): boolean {
|
||||
try {
|
||||
const mtimeMs = statSync(lockFile).mtimeMs;
|
||||
return Date.now() - mtimeMs > UNPARSEABLE_LOCK_GRACE_MS;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Try to create the lockfile atomically. Returns a release function on success, null on failure. */
|
||||
function tryAcquire(lockFile: string): (() => void) | null {
|
||||
try {
|
||||
|
|
@ -110,7 +122,8 @@ async function acquireLock(
|
|||
if (release) return release;
|
||||
|
||||
const owner = readLockMetadata(lockFile);
|
||||
if (owner && (isStaleLockOwner(owner) || !isProcessAlive(owner.pid))) {
|
||||
if ((!owner && isStaleUnparseableLock(lockFile))
|
||||
|| (owner && (isStaleLockOwner(owner) || !isProcessAlive(owner.pid)))) {
|
||||
try { unlinkSync(lockFile); } catch { /* ignore */ }
|
||||
const retryRelease = tryAcquire(lockFile);
|
||||
if (retryRelease) return retryRelease;
|
||||
|
|
|
|||
|
|
@ -394,7 +394,7 @@ describe("API Routes", () => {
|
|||
expect(mockSessionManager.listCached).toHaveBeenCalledWith("my-app");
|
||||
});
|
||||
|
||||
it("omits dead orchestrators from project-scoped orchestrator payloads when none are live", async () => {
|
||||
it("keeps dead orchestrators as the fallback project-scoped payload when none are live", async () => {
|
||||
const deadLifecycle = createInitialCanonicalLifecycle("orchestrator", new Date("2026-04-19T11:00:00.000Z"));
|
||||
deadLifecycle.session.state = "terminated";
|
||||
deadLifecycle.session.reason = "runtime_missing";
|
||||
|
|
@ -423,7 +423,9 @@ describe("API Routes", () => {
|
|||
const data = await res.json();
|
||||
|
||||
expect(data.orchestratorId).toBeNull();
|
||||
expect(data.orchestrators).toEqual([]);
|
||||
expect(data.orchestrators).toEqual([
|
||||
{ id: "my-app-orchestrator-0", projectId: "my-app", projectName: "My App" },
|
||||
]);
|
||||
expect(data.sessions).toEqual([]);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -52,6 +52,16 @@ function listLiveDashboardOrchestrators(
|
|||
);
|
||||
}
|
||||
|
||||
function listPreferredProjectOrchestrators(
|
||||
sessions: Parameters<typeof listDashboardOrchestrators>[0],
|
||||
projects: Parameters<typeof listDashboardOrchestrators>[1],
|
||||
) {
|
||||
const liveOrchestrators = listLiveDashboardOrchestrators(sessions, projects);
|
||||
return liveOrchestrators.length > 0
|
||||
? liveOrchestrators
|
||||
: listDashboardOrchestrators(sessions, projects);
|
||||
}
|
||||
|
||||
export async function GET(request: Request) {
|
||||
const correlationId = getCorrelationId(request);
|
||||
const startedAt = Date.now();
|
||||
|
|
@ -69,7 +79,7 @@ export async function GET(request: Request) {
|
|||
const coreSessions = await sessionManager.listCached(requestedProjectId);
|
||||
const visibleSessions = filterProjectSessions(coreSessions, projectFilter, config.projects);
|
||||
const orchestrators = requestedProjectId
|
||||
? listLiveDashboardOrchestrators(visibleSessions, config.projects)
|
||||
? listPreferredProjectOrchestrators(visibleSessions, config.projects)
|
||||
: listDashboardOrchestrators(visibleSessions, config.projects);
|
||||
const orchestratorId = requestedProjectId
|
||||
? selectPreferredOrchestratorId(visibleSessions, config.projects)
|
||||
|
|
|
|||
Loading…
Reference in New Issue