← back to AbramsEgo
serialize AbramsEgo PM2 fleet reads
8156792d515cf190f6ee0ccdd9cf992e0da2b6f2 · 2026-08-30 19:20:07 -0700 · Steve
Files touched
Diff
commit 8156792d515cf190f6ee0ccdd9cf992e0da2b6f2
Author: Steve <steve@designerwallcoverings.com>
Date: Sun Aug 30 19:20:07 2026 -0700
serialize AbramsEgo PM2 fleet reads
---
server.js | 26 +++++++++++++++++++++++++-
1 file changed, 25 insertions(+), 1 deletion(-)
diff --git a/server.js b/server.js
index bacf197c..2771885b 100644
--- a/server.js
+++ b/server.js
@@ -97,6 +97,30 @@ function sh(cmd, args, timeoutMs = 8000) {
});
});
}
+
+// TK-10970: serialize local PM2 CLI access with the same lock used by the
+// fleet keepalive and microsite crawler. Concurrent `pm2 jlist` calls can fork
+// a socket-less God daemon during a transient rpc.sock access race.
+const PM2_CLI_LOCK = path.join(process.env.PM2_HOME || path.join(HOME, '.pm2'), 'cli.lock.d');
+async function withPm2CliLock(fn) {
+ const deadline = Date.now() + 10000;
+ const staleMs = 8000;
+ let held = false;
+ while (Date.now() < deadline) {
+ try { await fs.promises.mkdir(PM2_CLI_LOCK); held = true; break; }
+ catch (e) {
+ if (e.code !== 'EEXIST') break;
+ try {
+ const st = await fs.promises.stat(PM2_CLI_LOCK);
+ if (Date.now() - st.mtimeMs > staleMs) { await fs.promises.rmdir(PM2_CLI_LOCK); continue; }
+ } catch {}
+ await new Promise(resolve => setTimeout(resolve, 80));
+ }
+ }
+ if (!held) return { ok: false, out: '', err: 'pm2 CLI lock unavailable' };
+ try { return await fn(); }
+ finally { try { await fs.promises.rmdir(PM2_CLI_LOCK); } catch {} }
+}
async function fetchJSON(url, timeoutMs = 5000) {
const ctrl = new AbortController();
const t = setTimeout(() => ctrl.abort(), timeoutMs);
@@ -213,7 +237,7 @@ async function collectLocalFleet() {
const inCooldown = Date.now() - jlistFailedAt < JLIST_COOLDOWN_MS;
const r = inCooldown
? { ok: false, out: '', err: 'jlist cooldown after recent failure' }
- : await sh(pm2Bin, ['jlist'], 20000);
+ : await withPm2CliLock(() => sh(pm2Bin, ['jlist'], 7000));
let list = null;
if (r.ok) { try { list = JSON.parse(r.out); } catch (e) { /* fall through to cache */ } }
if (!inCooldown && !list) jlistFailedAt = Date.now();
← 474d93ee auto-data-snapshot: 2026-08-30T19:03:41 (1 data files) — dat
·
back to AbramsEgo
·
auto-data-snapshot: 2026-08-30T19:40:35 (1 data files) — dat d8ef96bc →