quilltap 4.10.0-dev.22 → 4.10.0-dev.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/quilltap.js +48 -55
- package/package.json +1 -1
package/bin/quilltap.js
CHANGED
|
@@ -484,39 +484,6 @@ function verifyPidIsNode(pid, expectedArgv0) {
|
|
|
484
484
|
}
|
|
485
485
|
}
|
|
486
486
|
|
|
487
|
-
const HEARTBEAT_FRESH_MS = 5 * 60 * 1000;
|
|
488
|
-
|
|
489
|
-
/**
|
|
490
|
-
* Decide whether a lock record belongs to a live process.
|
|
491
|
-
*
|
|
492
|
-
* A hostname that differs from ours does NOT mean a different machine: macOS
|
|
493
|
-
* derives gethostname() dynamically when `scutil --get HostName` is unset, so
|
|
494
|
-
* one Mac reports e.g. "MacBook-Pro.local" and "Mac" at different times. So
|
|
495
|
-
* check PID liveness regardless of the recorded name, and fall back to
|
|
496
|
-
* heartbeat freshness for any environment rather than only for containers.
|
|
497
|
-
* See bug 126.
|
|
498
|
-
*/
|
|
499
|
-
function assessLock(lock, hostname) {
|
|
500
|
-
const sameHost = lock.hostname === hostname;
|
|
501
|
-
const alive = isPidAlive(lock.pid);
|
|
502
|
-
const isNode = alive ? verifyPidIsNode(lock.pid, lock.processArgv0 || '') : false;
|
|
503
|
-
const heartbeatAgeMs = lock.lastHeartbeat
|
|
504
|
-
? Date.now() - new Date(lock.lastHeartbeat).getTime()
|
|
505
|
-
: Infinity;
|
|
506
|
-
const heartbeatFresh = heartbeatAgeMs < HEARTBEAT_FRESH_MS;
|
|
507
|
-
|
|
508
|
-
return {
|
|
509
|
-
sameHost,
|
|
510
|
-
alive,
|
|
511
|
-
isNode,
|
|
512
|
-
heartbeatAgeMs,
|
|
513
|
-
heartbeatFresh,
|
|
514
|
-
// Held by something running: a confirmed local process, or any holder
|
|
515
|
-
// still refreshing its heartbeat.
|
|
516
|
-
live: (alive && isNode) || heartbeatFresh,
|
|
517
|
-
};
|
|
518
|
-
}
|
|
519
|
-
|
|
520
487
|
/**
|
|
521
488
|
* Handle --lock-status, --lock-clean, --lock-override commands.
|
|
522
489
|
*/
|
|
@@ -551,20 +518,32 @@ function handleLockCommand(dataDir, opts) {
|
|
|
551
518
|
return;
|
|
552
519
|
}
|
|
553
520
|
|
|
554
|
-
const
|
|
555
|
-
|
|
521
|
+
const sameHost = lock.hostname === hostname;
|
|
522
|
+
const alive = sameHost && isPidAlive(lock.pid);
|
|
523
|
+
const isNode = alive ? verifyPidIsNode(lock.pid, lock.processArgv0 || '') : false;
|
|
556
524
|
|
|
557
525
|
// Status line
|
|
558
526
|
let status;
|
|
559
527
|
if (alive && isNode) {
|
|
560
528
|
status = '\x1b[32mACTIVE\x1b[0m (process confirmed running)';
|
|
561
|
-
} else if (heartbeatFresh) {
|
|
562
|
-
const ageStr = Math.round(heartbeatAgeMs / 1000) + 's';
|
|
563
|
-
status = `\x1b[32mACTIVE\x1b[0m (${lock.environment || 'unknown'}, heartbeat ${ageStr} ago)`;
|
|
564
529
|
} else if (alive && !isNode) {
|
|
565
530
|
status = '\x1b[33mSUSPECT\x1b[0m (PID alive but does not look like Quilltap — possible PID reuse)';
|
|
566
531
|
} else if (!sameHost) {
|
|
567
|
-
|
|
532
|
+
// Different hostname — could be a container on this machine
|
|
533
|
+
const isVMOrContainer = lock.environment === 'docker';
|
|
534
|
+
const heartbeatAgeMs = lock.lastHeartbeat
|
|
535
|
+
? Date.now() - new Date(lock.lastHeartbeat).getTime()
|
|
536
|
+
: Infinity;
|
|
537
|
+
const heartbeatFreshMs = 5 * 60 * 1000;
|
|
538
|
+
|
|
539
|
+
if (isVMOrContainer && heartbeatAgeMs < heartbeatFreshMs) {
|
|
540
|
+
const ageStr = Math.round(heartbeatAgeMs / 1000) + 's';
|
|
541
|
+
status = `\x1b[32mACTIVE (${lock.environment}, heartbeat ${ageStr} ago)\x1b[0m`;
|
|
542
|
+
} else if (isVMOrContainer) {
|
|
543
|
+
status = `\x1b[33mSTALE (${lock.environment}, no recent heartbeat)\x1b[0m — will be auto-claimed on next startup`;
|
|
544
|
+
} else {
|
|
545
|
+
status = '\x1b[33mSTALE (different host)\x1b[0m — will be auto-claimed on next startup';
|
|
546
|
+
}
|
|
568
547
|
} else {
|
|
569
548
|
status = '\x1b[31mSTALE (process dead)\x1b[0m — will be auto-claimed on next startup';
|
|
570
549
|
}
|
|
@@ -572,7 +551,7 @@ function handleLockCommand(dataDir, opts) {
|
|
|
572
551
|
console.log(`Instance Lock Status: ${status}`);
|
|
573
552
|
console.log();
|
|
574
553
|
console.log(` PID: ${lock.pid}`);
|
|
575
|
-
console.log(` Hostname: ${lock.hostname}${sameHost ? ' (this host)' :
|
|
554
|
+
console.log(` Hostname: ${lock.hostname}${sameHost ? ' (this host)' : ' (different host)'}`);
|
|
576
555
|
console.log(` Environment: ${lock.environment || 'unknown'}`);
|
|
577
556
|
console.log(` Process: ${lock.processTitle || 'unknown'}`);
|
|
578
557
|
console.log(` Started: ${lock.startedAt || 'unknown'}`);
|
|
@@ -620,23 +599,37 @@ function handleLockCommand(dataDir, opts) {
|
|
|
620
599
|
return;
|
|
621
600
|
}
|
|
622
601
|
|
|
623
|
-
const
|
|
624
|
-
|
|
602
|
+
const sameHost = lock.hostname === hostname;
|
|
603
|
+
const alive = sameHost && isPidAlive(lock.pid);
|
|
625
604
|
|
|
626
|
-
if (alive
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
console.log(`Lock references PID ${lock.pid} which is alive but does NOT look like a Quilltap process.`);
|
|
637
|
-
console.log('This is likely a stale lock with a reused PID. Removing.');
|
|
605
|
+
if (alive) {
|
|
606
|
+
const isNode = verifyPidIsNode(lock.pid, lock.processArgv0 || '');
|
|
607
|
+
if (isNode) {
|
|
608
|
+
console.log(`Lock is held by a live Quilltap process (PID ${lock.pid}). Cannot clean.`);
|
|
609
|
+
console.log('Stop the running instance first, or use --lock-override to force.');
|
|
610
|
+
process.exit(1);
|
|
611
|
+
} else {
|
|
612
|
+
console.log(`Lock references PID ${lock.pid} which is alive but does NOT look like a Quilltap process.`);
|
|
613
|
+
console.log('This is likely a stale lock with a reused PID. Removing.');
|
|
614
|
+
}
|
|
638
615
|
} else if (!sameHost) {
|
|
639
|
-
|
|
616
|
+
// Different hostname — check if it's a container with a recent heartbeat
|
|
617
|
+
const isVMOrContainer = lock.environment === 'docker';
|
|
618
|
+
const heartbeatAgeMs = lock.lastHeartbeat
|
|
619
|
+
? Date.now() - new Date(lock.lastHeartbeat).getTime()
|
|
620
|
+
: Infinity;
|
|
621
|
+
const heartbeatFreshMs = 5 * 60 * 1000;
|
|
622
|
+
|
|
623
|
+
if (isVMOrContainer && heartbeatAgeMs < heartbeatFreshMs) {
|
|
624
|
+
const ageStr = Math.round(heartbeatAgeMs / 1000) + 's';
|
|
625
|
+
console.log(`Lock is held by a live ${lock.environment} instance (heartbeat ${ageStr} ago). Cannot clean.`);
|
|
626
|
+
console.log('Stop the other instance first, or use --lock-override to force.');
|
|
627
|
+
process.exit(1);
|
|
628
|
+
} else if (isVMOrContainer) {
|
|
629
|
+
console.log(`Lock was held by ${lock.environment} (${lock.hostname}) with no recent heartbeat. Removing stale lock.`);
|
|
630
|
+
} else {
|
|
631
|
+
console.log(`Lock was held by a different host (${lock.hostname}). Removing stale lock.`);
|
|
632
|
+
}
|
|
640
633
|
} else {
|
|
641
634
|
console.log(`Lock was held by PID ${lock.pid} which is no longer running. Removing stale lock.`);
|
|
642
635
|
}
|