querysub 0.575.0 → 0.576.0

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "querysub",
3
- "version": "0.575.0",
3
+ "version": "0.576.0",
4
4
  "main": "index.js",
5
5
  "license": "MIT",
6
6
  "note1": "note on node-forge fork, see https://github.com/digitalbazaar/forge/issues/744 for details",
@@ -71,7 +71,7 @@
71
71
  "node-forge": "https://github.com/sliftist/forge#e618181b469b07bdc70b968b0391beb8ef5fecd6",
72
72
  "pako": "^2.1.0",
73
73
  "peggy": "^5.0.6",
74
- "sliftutils": "^1.7.43",
74
+ "sliftutils": "^1.7.45",
75
75
  "socket-function": "^1.2.26",
76
76
  "terser": "^5.31.0",
77
77
  "typenode": "^6.6.1",
@@ -104,6 +104,16 @@ export async function findProcessRecordByPid(folder: string, pid: string): Promi
104
104
  return (await readFolderRecords(folder)).find(x => x.pid === pid && x.deadTime === undefined);
105
105
  }
106
106
 
107
+ /** Marks a process dead now, for a process we killed ourselves. Without this the record reads as running until the next resync happens to look, which is a long time to show something that we know is gone. */
108
+ export async function markProcessDead(pid: string, now: number): Promise<void> {
109
+ for (let record of await listProcessRecords()) {
110
+ if (record.pid !== pid || record.deadTime !== undefined) continue;
111
+ record.deadTime = now;
112
+ console.log(`Process ${record.pid} (${record.screenName}) was killed, marking it dead. Its log is deleted at ${new Date(record.deadTime + DEAD_PROCESS_RETENTION).toISOString()}`);
113
+ await writeProcessRecord(record);
114
+ }
115
+ }
116
+
107
117
  export async function writeProcessRecord(record: ProcessRecord): Promise<void> {
108
118
  await fs.promises.mkdir(path.join(record.folder, PROCESS_FOLDER), { recursive: true });
109
119
  await fs.promises.writeFile(getRecordPath(record.folder, record.pid, record.startTime), JSON.stringify(record));
@@ -13,7 +13,7 @@ import { forceRemoveNode } from "../-f-node-discovery/NodeDiscovery";
13
13
  import { fsExistsAsync } from "../fs";
14
14
  import { PromiseObj } from "../promise";
15
15
  import { SERVICE_FOLDER, SERVICE_NODE_FILE_NAME } from "./machineSchema";
16
- import { ProcessRecord, getLogPipeScript, getLogTailScript, getProcessLogPath, getPipeScriptPath, getTailScriptPath, findProcessRecordByPid, writeProcessRecord } from "./processLogs";
16
+ import { ProcessRecord, getLogPipeScript, getLogTailScript, getProcessLogPath, getPipeScriptPath, getTailScriptPath, findProcessRecordByPid, writeProcessRecord, markProcessDead } from "./processLogs";
17
17
  import { setPreciseTimeout } from "../misc";
18
18
 
19
19
  // Running, inspecting and killing the tmux screens services run in. This layer only knows "here is a configuration, run it" - which version should be running when is the deploy logic's problem.
@@ -256,9 +256,11 @@ export const runScreenCommand = measureWrap(async function runScreenCommand(conf
256
256
  let screenName = config.screenName;
257
257
 
258
258
  // Always a brand new session, so the pane is always new - and the pane's pid is what makes this a distinct process with its own log. A session already under this name is debris from a start that never got taken over.
259
- if (await getScreenPanePid(screenName)) {
259
+ let leftoverPid = await getScreenPanePid(screenName);
260
+ if (leftoverPid) {
260
261
  console.log(red(`Removing the leftover session ${screenName} from a start that never completed`));
261
262
  await runPromise(`${prefix}tmux kill-session -t ${screenName}`);
263
+ await markProcessDead(leftoverPid, Date.now());
262
264
  }
263
265
  await runPromise(`${prefix}tmux new -s ${screenName} -d`);
264
266
 
@@ -339,10 +341,12 @@ export async function retireCurrentScreen(config: {
339
341
  let retiringScreenName = getRetiringScreenName(canonicalScreenName, killTime);
340
342
  console.log(green(`Retiring ${canonicalScreenName} as ${retiringScreenName}`));
341
343
  await runPromise(`${getTmuxPrefix()}tmux rename-session -t ${canonicalScreenName} ${retiringScreenName}`);
342
- let record = await findProcessRecordByPid(folder, outgoingPid);
343
- if (record) {
344
- await writeProcessRecord({ ...record, screenName: retiringScreenName });
345
- }
344
+ void (async () => {
345
+ let record = await findProcessRecordByPid(folder, outgoingPid);
346
+ if (record) {
347
+ await writeProcessRecord({ ...record, screenName: retiringScreenName });
348
+ }
349
+ })();
346
350
  if (nodeId) {
347
351
  // The file in the folder belongs to its replacement now, so only the registration goes
348
352
  void removeServiceNode({ folder, nodeId, ownsFile: false });
@@ -353,9 +357,11 @@ export async function retireCurrentScreen(config: {
353
357
 
354
358
  /** Ends a session at once, with no notice - for a screen that should not exist at all, so there is nothing to wind down gracefully. Finding one is always a surprise, so the reason it was not supposed to be there is logged. */
355
359
  export async function killScreenNow(screenName: string, reason: string): Promise<void> {
356
- if (!await getScreenPanePid(screenName)) return;
360
+ let panePid = await getScreenPanePid(screenName);
361
+ if (!panePid) return;
357
362
  console.log(red(`Unexpected screen ${screenName}: ${reason}. Killing it immediately.`));
358
363
  await runPromise(`${getTmuxPrefix()}tmux kill-session -t ${screenName}`);
364
+ await markProcessDead(panePid, Date.now());
359
365
  }
360
366
 
361
367
  // Kills a screen and nothing else. Which node the process registered as, and whether that registration should be dropped, is the deploy logic's business - it reads the node id off the process's record, which was taken before any replacement could overwrite the file.
@@ -364,10 +370,15 @@ export const killScreen = measureWrap(async function killScreen(config: {
364
370
  }) {
365
371
  console.log(red(`Killing screen ${config.screenName} (Ctrl+C, then killing the session in ${formatTime(SHUTDOWN_GRACE_TIME)})`));
366
372
  let prefix = getTmuxPrefix();
373
+ // Taken before the grace period, as the process is free to exit on the Ctrl+C and take the pane's pid with it
374
+ let panePid = await getScreenPanePid(config.screenName);
367
375
  // Purely a notice to the process that it is going away, so it can wind down. We never check whether it worked and we never reuse the screen - the session is killed either way.
368
376
  void runPromise(`${prefix}tmux send-keys -t ${config.screenName} 'C-c' Enter`);
369
377
  await delay(SHUTDOWN_GRACE_TIME);
370
378
  await runPromise(`${prefix}tmux kill-session -t ${config.screenName}`);
379
+ if (panePid) {
380
+ await markProcessDead(panePid, Date.now());
381
+ }
371
382
  });
372
383
 
373
384
  /** Streams one process's log: everything already in it, then everything appended after. One process, one file - nothing here has to reason about which process a byte came from. */