@try-works/dsh-memory 0.1.11 → 0.1.12
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/tasks.js +44 -2
package/package.json
CHANGED
package/src/tasks.js
CHANGED
|
@@ -400,9 +400,13 @@ export function readScheduleStore(path) {
|
|
|
400
400
|
* A task whose kind we do not recognise is SKIPPED, not written: storing it would mean restoring
|
|
401
401
|
* it later without understanding it. It is reported, so the omission is visible.
|
|
402
402
|
*/
|
|
403
|
-
export function backupTasks(root, { schedulePath, now = Date.now(), nowIso } = {}) {
|
|
403
|
+
export function backupTasks(root, { schedulePath, now = Date.now(), nowIso, sessions } = {}) {
|
|
404
404
|
const at = nowIso ?? new Date(now).toISOString()
|
|
405
405
|
const live = readScheduleStore(schedulePath ?? defaultSchedulePath())
|
|
406
|
+
// Read once per pass, not per task: it is a directory walk, and every row asks the same question.
|
|
407
|
+
// Injectable so a test can state the world instead of inheriting this machine's: `null` means
|
|
408
|
+
// the question cannot be answered and no row is called orphaned.
|
|
409
|
+
const sessionSet = sessions === undefined ? sessionIdsOnDisk() : sessions
|
|
406
410
|
const before = listTasks(root)
|
|
407
411
|
if (live.error !== null) {
|
|
408
412
|
// The index is still rewritten, so a page from an earlier pass cannot sit on disk describing a
|
|
@@ -429,7 +433,11 @@ export function backupTasks(root, { schedulePath, now = Date.now(), nowIso } = {
|
|
|
429
433
|
// A held-back task stays held back across every backup: that state is the user's decision, and
|
|
430
434
|
// a routine pass must not overwrite it with a derived one.
|
|
431
435
|
const held = previous && previous.stateKnown && previous.state === TASK_STATES.PAUSED
|
|
432
|
-
|
|
436
|
+
// Without this the ORPHANED state exists in the vocabulary and is never produced by any real
|
|
437
|
+
// pass - reachable from a unit test and from nothing else. `sessions === null` means the
|
|
438
|
+
// sessions directory is absent and we cannot tell, so the row is left as active.
|
|
439
|
+
const ownerResolvable = sessionSet === null || task.sessionId === undefined ? true : sessionSet.has(task.sessionId)
|
|
440
|
+
const derived = deriveState({ stored: held ? TASK_STATES.PAUSED : null, live: task, now, ownerResolvable })
|
|
433
441
|
writeTask(root, record, derived.state, previous ? previous.backedUpAt : at)
|
|
434
442
|
if (previous) refreshed += 1
|
|
435
443
|
else written += 1
|
|
@@ -542,3 +550,37 @@ export function planRestore(root, { liveTasks = [], now = Date.now() } = {}) {
|
|
|
542
550
|
}
|
|
543
551
|
return { add, present, paused, refused, unreadable }
|
|
544
552
|
}
|
|
553
|
+
|
|
554
|
+
/** The directory DSH keeps session records in, beside the schedule store's home. */
|
|
555
|
+
export function sessionsRoot(home = homedir()) {
|
|
556
|
+
const configured = process.env.DSH_HOME
|
|
557
|
+
const dshHome = configured && configured !== '' ? configured : join(home, '.dsh')
|
|
558
|
+
return join(dshHome, 'sessions')
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
/**
|
|
562
|
+
* Every session id that still has a record on disk.
|
|
563
|
+
*
|
|
564
|
+
* Used to tell ORPHANED from ACTIVE. A live task is armed against the session that created it, and
|
|
565
|
+
* the runtime resolves an agent from that id before every delivery (schedule runtime.ts:101) - so
|
|
566
|
+
* a task whose session is gone is a row that will never fire while still occupying the store. The
|
|
567
|
+
* backup cannot see the runtime, but it can see whether the session still exists.
|
|
568
|
+
*
|
|
569
|
+
* @returns a Set, or null when the sessions directory is absent
|
|
570
|
+
*
|
|
571
|
+
* Null rather than an empty set on purpose: no sessions directory means we cannot tell, and an
|
|
572
|
+
* empty set would mark every task on the machine orphaned. Refusing to guess is the rule here.
|
|
573
|
+
*/
|
|
574
|
+
export function sessionIdsOnDisk(home = homedir()) {
|
|
575
|
+
const root = sessionsRoot(home)
|
|
576
|
+
if (!existsSync(root)) return null
|
|
577
|
+
const ids = new Set()
|
|
578
|
+
let projects
|
|
579
|
+
try { projects = readdirSync(root) } catch { return null }
|
|
580
|
+
for (const project of projects) {
|
|
581
|
+
let sessions
|
|
582
|
+
try { sessions = readdirSync(join(root, project)) } catch { continue }
|
|
583
|
+
for (const id of sessions) ids.add(id)
|
|
584
|
+
}
|
|
585
|
+
return ids
|
|
586
|
+
}
|