


























@@ -52,9 +52,12 @@ const REPORT_ONLY_STALE_LOCK_REASONS = new Set(["too-old", "hold-exceeded"]);
5252function yieldEventLoop(): Promise<void> {
5353return new Promise<void>((resolve) => setImmediate(resolve));
5454}
55-// A payload-less lock can be left behind if shutdown lands between open("wx")
56-// and the owner metadata write. Keep the grace short so 10s callers recover.
57-const ORPHAN_LOCK_PAYLOAD_GRACE_MS = 5_000;
55+// A payload-less lock can be left behind during the window between open("wx")
56+// and the owner metadata write if the owner is suspended (CPU pressure,
57+// container freeze, I/O stall, GC pause). 30 s covers realistic system
58+// pauses while staying well below DEFAULT_TIMEOUT_GRACE_MS (120 s).
59+const ORPHAN_LOCK_PAYLOAD_GRACE_MS = 30_000;
60+const SHORT_TIMEOUT_ORPHAN_LOCK_PAYLOAD_GRACE_MS = 5_000;
58615962type CleanupState = {
6063registered: boolean;
@@ -565,6 +568,7 @@ async function shouldReclaimContendedLockFile(
565568details: LockInspectionDetails,
566569staleMs: number,
567570nowMs: number,
571+orphanPayloadGraceMs = ORPHAN_LOCK_PAYLOAD_GRACE_MS,
568572): Promise<boolean> {
569573if (!details.stale) {
570574return false;
@@ -575,13 +579,20 @@ async function shouldReclaimContendedLockFile(
575579try {
576580const stat = await fs.stat(lockPath);
577581const ageMs = Math.max(0, nowMs - stat.mtimeMs);
578-return ageMs > Math.min(staleMs, ORPHAN_LOCK_PAYLOAD_GRACE_MS);
582+return ageMs > Math.min(staleMs, orphanPayloadGraceMs);
579583} catch (error) {
580584const code = (error as { code?: string } | null)?.code;
581585return code !== "ENOENT";
582586}
583587}
584588589+function resolveOrphanLockPayloadGraceMs(timeoutMs: number): number {
590+if (timeoutMs < ORPHAN_LOCK_PAYLOAD_GRACE_MS) {
591+return SHORT_TIMEOUT_ORPHAN_LOCK_PAYLOAD_GRACE_MS;
592+}
593+return ORPHAN_LOCK_PAYLOAD_GRACE_MS;
594+}
595+585596async function shouldRemoveLockDuringCleanup(
586597lockPath: string,
587598details: LockInspectionDetails,
@@ -779,6 +790,7 @@ export async function acquireSessionWriteLock(params: {
779790});
780791const staleMs = resolvePositiveMs(params.staleMs, defaultOptions.staleMs);
781792const maxHoldMs = resolvePositiveMs(params.maxHoldMs, defaultOptions.maxHoldMs);
793+const orphanPayloadGraceMs = resolveOrphanLockPayloadGraceMs(timeoutMs);
782794const sessionFile = path.resolve(params.sessionFile);
783795const sessionDir = path.dirname(sessionFile);
784796const normalizedSessionFile = await resolveNormalizedSessionFile(sessionFile);
@@ -816,7 +828,13 @@ export async function acquireSessionWriteLock(params: {
816828readOwnerProcessArgs: readProcessArgsSync,
817829respectMaxHold: !heldByThisProcess,
818830});
819-return await shouldReclaimContendedLockFile(lockPath, inspected, staleMs, nowMs);
831+return await shouldReclaimContendedLockFile(
832+lockPath,
833+inspected,
834+staleMs,
835+nowMs,
836+orphanPayloadGraceMs,
837+);
820838},
821839shouldRemoveStaleLock: async ({ lockPath, normalizedTargetPath, payload }) => {
822840await yieldEventLoop();
@@ -831,7 +849,13 @@ export async function acquireSessionWriteLock(params: {
831849readOwnerProcessArgs: readProcessArgsSync,
832850respectMaxHold: !heldByThisProcess,
833851});
834-return await shouldReclaimContendedLockFile(lockPath, inspected, staleMs, nowMs);
852+return await shouldReclaimContendedLockFile(
853+lockPath,
854+inspected,
855+staleMs,
856+nowMs,
857+orphanPayloadGraceMs,
858+);
835859},
836860});
837861return { release: lock.release };
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。