maxpool 1.5.43 → 1.5.44
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/package.json +1 -1
- package/src/index.js +22 -4
- package/src/updater.js +8 -0
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -12,7 +12,7 @@ import { loginOAuth, fetchProfile, refreshAccessToken, isTokenExpiringSoon, toke
|
|
|
12
12
|
import { TUI } from './tui.js';
|
|
13
13
|
import { RestartController } from './restart-controller.js';
|
|
14
14
|
import { resolveAccounts } from './account-config.js';
|
|
15
|
-
import { maybeCheckForUpdate, getCurrentVersion, markApplied } from './updater.js';
|
|
15
|
+
import { maybeCheckForUpdate, getCurrentVersion, markApplied, clearQuarantine } from './updater.js';
|
|
16
16
|
import {
|
|
17
17
|
runReloadBaton,
|
|
18
18
|
RELOAD_SWAPPED, RELOAD_ROLLED_BACK,
|
|
@@ -23,10 +23,20 @@ import {
|
|
|
23
23
|
const args = process.argv.slice(2);
|
|
24
24
|
const command = args[0];
|
|
25
25
|
const SERVER_RESTART_EXIT_CODE = 75;
|
|
26
|
+
// Reload readiness handshake budget. On a loaded machine (Bitdefender, many node procs,
|
|
27
|
+
// a local LLM) a fresh worker can need well over 10s to boot + signal ready; too tight →
|
|
28
|
+
// the seamless reload rolls back and the update/restart silently never lands. Generous +
|
|
29
|
+
// env-tunable; covers BOTH the ready and the takeover waits in runReloadBaton.
|
|
30
|
+
const RELOAD_READY_MS = Math.max(10_000, Number(process.env.MAXPOOL_RELOAD_READY_MS) || 30_000);
|
|
26
31
|
// If a seamless reload doesn't release us (→ take over) within this window, the new
|
|
27
|
-
// worker rolled back — self-heal admission so we don't 503 forever.
|
|
28
|
-
// readiness
|
|
29
|
-
|
|
32
|
+
// worker rolled back — self-heal admission so we don't 503 forever. Must sit ABOVE the
|
|
33
|
+
// baton readiness budget + margin so it never fires WHILE the baton is still handshaking.
|
|
34
|
+
// Default (RELOAD_READY_MS + 20s = 50s) sits comfortably above the readiness budget, so
|
|
35
|
+
// the self-heal never fires mid-handshake. An explicit MAXPOOL_RELOAD_SELFHEAL_MS override
|
|
36
|
+
// is honored down to a 15s floor (used by the reload integration test) — a sub-ready
|
|
37
|
+
// override only risks a spurious "rolled back" log + brief admission flap (no corruption:
|
|
38
|
+
// the new worker holds no lease until takeover), never a false double-writer.
|
|
39
|
+
const RELOAD_ROLLBACK_SELFHEAL_MS = Math.max(15_000, Number(process.env.MAXPOOL_RELOAD_SELFHEAL_MS) || RELOAD_READY_MS + 20_000);
|
|
30
40
|
// Seamless-reload drain cap. On a seamless reload the NEW worker already serves
|
|
31
41
|
// ALL new traffic while the OLD worker only finishes its own in-flight requests,
|
|
32
42
|
// so a long old-worker drain has zero request-facing cost — let a long streaming
|
|
@@ -326,6 +336,10 @@ async function supervisorCommand() {
|
|
|
326
336
|
// listener to cover the cutover gap, then hand THAT live handle to the
|
|
327
337
|
// new worker. The new worker's MSG_PRIMARY then closes it again.
|
|
328
338
|
prepareHandle: async () => { await relistenMaster(); return masterServer; },
|
|
339
|
+
// Generous, env-tunable readiness/takeover budgets — the 10s default rolled back
|
|
340
|
+
// on this user's loaded Mac (the update never landed). See RELOAD_READY_MS.
|
|
341
|
+
readyTimeoutMs: RELOAD_READY_MS,
|
|
342
|
+
takeoverTimeoutMs: RELOAD_READY_MS,
|
|
329
343
|
log: msg => console.log(`[Maxpool] ${msg}`),
|
|
330
344
|
});
|
|
331
345
|
|
|
@@ -1083,6 +1097,10 @@ async function serverWorkerCommand() {
|
|
|
1083
1097
|
if (updateInFlight) { notifyUpdate('Update check already running'); return; }
|
|
1084
1098
|
if (!hasLease) { notifyUpdate('Updates run on the primary worker only'); return; }
|
|
1085
1099
|
if (config?.updateCheck === false) { notifyUpdate('Update checks are disabled in config'); return; }
|
|
1100
|
+
// An EXPLICIT manual apply always re-attempts — clear any quarantine a prior auto-reload
|
|
1101
|
+
// left (e.g. a rollback from a too-tight readiness timeout), so 'u'→'c' can't dead-end on
|
|
1102
|
+
// "already attempted — will retry only a newer release".
|
|
1103
|
+
clearQuarantine();
|
|
1086
1104
|
notifyUpdate('Checking for updates…');
|
|
1087
1105
|
const r = await runUpdateCheck({ announce: true, apply: applyNow, forceInstall: true });
|
|
1088
1106
|
if (r && !r.hasUpdate) notifyUpdate('Already on the latest version');
|
package/src/updater.js
CHANGED
|
@@ -95,6 +95,14 @@ export function markApplied(version) {
|
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
/** Clear the applied-version quarantine floor. The AUTO path quarantines a version it
|
|
99
|
+
* ATTEMPTED (markApplied, before the reload) so a genuinely boot-broken release can't
|
|
100
|
+
* reload-loop — but a reload that rolled back for a NON-version reason (e.g. a slow
|
|
101
|
+
* readiness handshake on a loaded machine) then wrongly strands a perfectly-good version
|
|
102
|
+
* ("already attempted — will retry only a newer release"). An EXPLICIT manual apply calls
|
|
103
|
+
* this first so the user can always re-attempt the current latest. */
|
|
104
|
+
export function clearQuarantine() { _lastAttemptedTarget = null; }
|
|
105
|
+
|
|
98
106
|
/**
|
|
99
107
|
* Check for an update; with `config.autoUpdate` also self-install; with
|
|
100
108
|
* `config.autoApply` SIGNAL that the caller should seamlessly reload to APPLY it
|