diff --git a/src/main/services/team/fileLock.ts b/src/main/services/team/fileLock.ts index 9152bbda..56a1c7de 100644 --- a/src/main/services/team/fileLock.ts +++ b/src/main/services/team/fileLock.ts @@ -69,18 +69,34 @@ function removeLockPath(lockPath: string): void { } } +function writeLockFile(lockPath: string): void { + const fd = fs.openSync(lockPath, 'wx'); + fs.writeSync(fd, `${process.pid}\n${Date.now()}\n`); + fs.closeSync(fd); +} + function tryAcquire(lockPath: string, options: Required): boolean { try { - const dir = path.dirname(lockPath); - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }); - } - const fd = fs.openSync(lockPath, 'wx'); - fs.writeSync(fd, `${process.pid}\n${Date.now()}\n`); - fs.closeSync(fd); + // Fast path: assume the lock directory already exists (the common case once a + // team dir is created). This drops an existsSync(dir) stat from EVERY acquire, + // which adds up across the many lock cycles during a team launch. + writeLockFile(lockPath); return true; } catch (err) { const code = (err as NodeJS.ErrnoException).code; + if (code === 'ENOENT') { + // Lock directory missing — create it lazily and acquire in the same call, so + // first-acquire latency in a fresh dir is unchanged. + try { + fs.mkdirSync(path.dirname(lockPath), { recursive: true }); + writeLockFile(lockPath); + return true; + } catch { + // Lost a race (another process created the dir/lock) or still failing — + // fall through to a normal retry on the next loop iteration. + return false; + } + } if (code === 'EEXIST' || code === 'EISDIR') { if (shouldBreakExistingLock(lockPath, options.staleTimeoutMs)) { removeLockPath(lockPath);