Decides whether a work-list that was fanned out across N workers actually completed. Every job is expected to record its own exit status; this reads that record against the list of jobs that were supposed to run and returns a verdict the caller can act on.
Arguments
- rc
A data frame with a
jobcolumn and anrccolumn, both character. One row per job that actually reported. Anrcthat isNA, empty, or not a run of digits counts as a failure, never as a neutral:as.integer("abc")isNA,NA != 0isNA, andwhich()silently drops it.- expected
Character vector of job ids that were supposed to run. No default — see Details.
- label
Character. Name for the phase, used in the message.
- allow_empty
Logical. Whether an empty
expectedis acceptable. DefaultFALSE.- quiet
Logical. Suppress the summary message. Default
FALSE.
Value
Invisibly, a list with ok, status, label, the counts
(n_expected, n_ran, n_ok, n_failed, n_missing, n_unexpected),
the id vectors (failed, missing, unexpected), the input rc,
plus problems and a formatted message.
Details
Written for the post-consolidate recompute pool in
data-raw/study_area_run.sh (link#250), which has no shell test harness —
the predicate lives here because testthat can prove what shell cannot, the
same reasoning that put lnk_preflight_vintage() and
lnk_preflight_parity() in R. data-raw/fanout_judge.R is the
shell-callable wrapper.
Why it takes names, not a count
A count answers "how many finished" and cannot answer "which one didn't". Passing the expected job ids lets the verdict name the jobs that never reported, and lets it notice a job that reported but was never asked for — which is a harness bug, not a work failure, and needs a different fix.
expected deliberately has no default. Judging a result table on its
own terms produces an affirmative claim of success about jobs that never
reported at all — the failure mode this function exists to prevent. Same
doctrine as n_expected in lnk_preflight_parity().
Statuses
Evaluated in order, so the first that applies wins:
none_expectedexpectedis empty. A fan-out over nothing exits 0 exactly like a successful one, so this is a failure unless the caller passesallow_empty = TRUE.none_ranNothing reported. Distinct from
all_failed: no job got far enough to record anything, which points at the harness rather than at the work.all_failedEvery job that reported failed, and none succeeded. Reported separately from how many are missing — both counts are always available.
okEvery expected job reported, every status was zero, and there were no duplicate or unexpected ids.
partialAnything else — some ran, some did not, or some failed.
Examples
ran <- data.frame(job = c("BULK", "PARS", "ADMS"),
rc = c("0", "0", "0"),
stringsAsFactors = FALSE)
res <- lnk_fanout_judge(ran, expected = c("BULK", "PARS", "ADMS"))
#> [fanout] fanout - OK: 3/3 job(s) succeeded
res$status
#> [1] "ok"
# A job that failed and a job that never reported are different problems.
partial <- data.frame(job = c("BULK", "PARS"), rc = c("0", "1"),
stringsAsFactors = FALSE)
bad <- lnk_fanout_judge(partial, expected = c("BULK", "PARS", "ADMS"),
label = "recompute", quiet = TRUE)
bad$failed
#> [1] "PARS"
bad$missing
#> [1] "ADMS"
