Back to home page

EIC code displayed by LXR

 
 

    


Warning, /swf-monitor/docs/SNAPPER_ERRORS.md is written in an unsupported language. File is not indexed.

0001 # Snapper Errors — error-state component and errors view
0002 
0003 PanDA job errors become part of the recorded system history: a curated
0004 error-state component captured in Snapper snaps, and a dedicated errors
0005 view over that history showing the time development of errors by
0006 category — error floods as they rise — with drilldown from any moment
0007 into the error breakdown and the underlying jobs. A per-task reading of
0008 the same history is a filter on the view, and the PanDA task page links
0009 to the filtered view, answering "when did this error show up in this
0010 task" directly.
0011 
0012 This is a design document; the sections below are the agreed plan of
0013 record for implementation. It builds on the Snapper concepts and SWF
0014 deployment in [SNAPPER.md](SNAPPER.md) and the generic package
0015 documentation in the snapper-ai repository
0016 ([INTEGRATION.md](https://github.com/BNLNPPS/snapper-ai/blob/main/docs/INTEGRATION.md)).
0017 The error machinery it records is the PanDA monitoring layer of
0018 swf-monitor — platform infrastructure per the architecture map
0019 ([ARCHITECTURE_MAP.md](https://github.com/BNLNPPS/swf-epicprod/blob/main/docs/ARCHITECTURE_MAP.md)),
0020 serving every PanDA-using domain.
0021 
0022 ## Error categorization
0023 
0024 The category vocabulary is the foundation; the view reports whatever
0025 the vocabulary distinguishes.
0026 
0027 PanDA records job errors in seven component fields, each an error code
0028 plus a diagnostic string: brokerage, ddm, executor, dispatcher, pilot,
0029 supervisor, and taskbuffer (`ERROR_COMPONENTS`,
0030 `monitor_app/panda/constants.py`). The existing error summary
0031 (`error_summary`, `monitor_app/panda/queries.py`; the
0032 `/panda/errors/` page and `panda_error_summary` MCP tool) aggregates
0033 failed jobs into patterns of component × code × leading diagnostic
0034 text, with a classified mode that attributes each job to its first
0035 nonzero component so one job lands in one category.
0036 
0037 The Snapper category is **component × error code**:
0038 
0039 - The code is the stable classification the producing subsystem
0040   itself assigned; it is available on every failed job record with no
0041   latency.
0042 - Category labels come from the component code catalogs — the pilot's
0043   `errorcodes.py` message table and the equivalent server-side code
0044   definitions — so categories read as named conditions, not bare
0045   numbers.
0046 - Diagnostic strings vary per job (paths, hostnames, identifiers) and
0047   are therefore moment detail presented in the breakdown, never part
0048   of category identity.
0049 
0050 Every failed job row carries `jeditaskid` alongside the error fields,
0051 so the same categorization aggregates per task, per site, or per any
0052 other job attribute without additional classification work.
0053 
0054 ### Terminal states
0055 
0056 The job's terminal status is a second classification axis, recorded
0057 per entry. PanDA's own kill path assigns it with distinct semantics
0058 (`job_complex_module.py`, panda-server):
0059 
0060 - **failed** — the job ran and ended in error: an actual error.
0061 - **cancelled** — a person or controlling system deliberately killed
0062   the job.
0063 - **closed** — the server disposed of the job for its own workflow
0064   reasons: pending expiry, reassignment, rebrokerage, task-done
0065   kills. By design not an actual error; a flood of closures signals
0066   an infrastructure condition (a stalled daemon, generation
0067   outrunning dispatch), not a payload problem.
0068 
0069 Error presentations therefore exclude closed jobs by default and
0070 offer the terminal states as a filter, discovered from the recorded
0071 data with per-state counts always visible — a closure storm announces
0072 itself in its count without displacing the actual errors. The
0073 recorded history and the retrieval surface carry every state; the
0074 default applies to presentation only.
0075 
0076 ### Progressive refinement
0077 
0078 Two refinement tiers improve the vocabulary over time without changing
0079 the recorded history's structure:
0080 
0081 - **Log-derived classification.** The Bamboo
0082   ([BNLNPPS/bamboo-mcp](https://github.com/BNLNPPS/bamboo-mcp))
0083   `classify_failure` analysis — already applied per job in
0084   `panda_study_job` — fetches a pilot-log excerpt and keyword-matches
0085   it together with the error fields into semantic failure categories
0086   such as `stagein_timeout`. Applied once per new pattern signature
0087   (one representative job, result cached), it annotates categories
0088   with a log-informed reading at a latency the 5-minute capture
0089   cadence absorbs.
0090 - **Error-state knowledge base.** Accumulated category annotations,
0091   representative cases, and their resolutions form the error
0092   knowledge base foreseen for the site-canary buildout. The snap
0093   history is its evidence store; nothing in this design needs to
0094   change to feed it.
0095 
0096 ## The error-state component
0097 
0098 A component, internal name `errors`, in the epicprod scope, published
0099 by a maintainer module beside the existing PanDA activity maintainer
0100 (`monitor_app/snapper_panda.py`) on the same 5-minute System-status
0101 refresh. Five minutes is the floor; the cadence is governed by the
0102 existing SysConfig capture policy and is raised, not lowered, if the
0103 component proves heavy.
0104 
0105 Each publication records the error events of one interval:
0106 
0107 - **Interval** — the half-open interval (start, end] the publication
0108   covers, running from the previous publication's source time to this
0109   one's.
0110 - **Entries** — one row per job that ended faulty in the interval:
0111   PanDA job id, JEDI task id, category, event time, and terminal
0112   status, as arrays in a declared column order. The event time is the
0113   job's end time, with one exception: a lost-heartbeat failure
0114   (dispatcher code 100) records the last heartbeat as its end time
0115   and the failure instant only as its modification time, so the
0116   failure instant is its event time — otherwise a kill storm appears
0117   on the plots hours before it happened. A job reports errors once,
0118   upon completion, so each failed job appears in exactly one
0119   interval.
0120 - **Overflow** — absent normally. An interval exceeding the entry bound
0121   (2,000 rows) keeps the earliest rows and folds the exact remainder
0122   into counts keyed `category@status`, so status-resolved aggregate
0123   counts never lose a job while the per-job listing stays bounded in
0124   storm floods.
0125 
0126 The publisher is stateless: each pass reads the interval's faulty jobs
0127 from the PanDA job records. Counts over any period are sums of entry
0128 counts over the intervals it spans, and per-task readings filter the
0129 same entries by task id — no counters are stored. An interval with no
0130 errors is affirmed unchanged, advancing the source time with no new
0131 snap, so quiet periods cost nothing while the interval chain stays
0132 gapless. A missed or delayed publication loses nothing: the following
0133 interval covers the gap.
0134 
0135 ### Backfill
0136 
0137 The recorded job history carries end times, terminal statuses, and
0138 error fields for every failed job, so the interval record is
0139 reconstructible for any past period. The backfill script (`scripts/backfill-errors-entries.py`)
0140 writes synthetic errors snaps on the 5-minute grid over the trailing
0141 30 days: one snap per non-empty interval, with capture policy
0142 `backfill-errors-v1` marking reconstructed evidence as distinct from
0143 observed snaps. The backfilled record tiles exactly against the start
0144 of the first live interval, so each failed job lands in exactly one
0145 interval across the seam. The script is idempotent — a re-run
0146 replaces prior backfill — and dry-run by default. The deployment
0147 order is maintainer first, backfill after the first live publication.
0148 
0149 ## The errors view
0150 
0151 A dedicated Snapper page for error history, using the focus-view
0152 mechanism that serves the campaign page: its own clean path under the
0153 epicprod scope, its own curve families, and its own detail rendering.
0154 The error families are not added to the epicprod Time history report
0155 page, which carries its own distinct information; dashboard
0156 compositions may combine elements of both.
0157 
0158 **Plot.** The category flood quilt: recorded error events by
0159 category, stacked. The server bins events once, by each job's end
0160 time, into sparse bins at the native 5-minute cadence; the page bins
0161 those into the display rung — the smallest of 5, 10, 15, 20, 30, and
0162 60 minutes keeping the plotted extent at or under 720 columns — and
0163 re-bins in place as the view zooms, down to the native bins, with no
0164 further server work. Every rung is an exact sum of native bins. A
0165 grouping selector switches between the seven-component grouping and
0166 the full component × code categories. Member tick boxes are omitted:
0167 identification lives in hover and the breakdown below.
0168 
0169 **Share donut.** Category shares of the accruals within the display
0170 bounds, rendered with the annular SVG donut the site view's detail
0171 section established (`_site_outcomes_pie`,
0172 `monitor_app/snapper_providers.py`) — the at-a-glance signal beside
0173 the tables. The donut follows the display bounds and the active
0174 filters.
0175 
0176 **Detail below the plot: the error breakdown.** A click on the plot is
0177 a time cut. The detail section renders the error breakdown around
0178 that moment, integrated over a window at least an hour wide — a
0179 single 5-minute interval is too sparse to read — organized by
0180 category:
0181 
0182 - error counts and shares per category within the window;
0183 - the window's top diagnostic patterns, aggregated live from the job
0184   records;
0185 - representative job links — the job page, payload log, and job study;
0186 - the affected tasks, as detail within each category's section;
0187 - a link to the `/panda/errors/` pattern table windowed to the same
0188   bounds, for the full aggregation over live job records.
0189 
0190 The breakdown is organized by error, not by task; the task reading
0191 comes from the filter.
0192 
0193 **Terminal-state filter.** A chip row beside the grouping selector
0194 filters the view by terminal state. The chips are discovered from the
0195 loaded data — a state appears exactly when the record holds it — and
0196 each carries its count over the visible range in parentheses, so an
0197 excluded closure storm remains visible in its chip while the plot
0198 shows the actual errors. Closed is off by default (see Terminal
0199 states above); rows recorded before the status column report as
0200 `unrecorded` and age out of the window. The selection lives in the
0201 URL, filters client-side from the per-state breakdowns the bins
0202 carry (no refetch), and the breakdown, donut, and diagnostic
0203 patterns follow it.
0204 
0205 **Task filter.** A task selection (URL parameter, so the view is
0206 bookmarkable and linkable) narrows the plot, donut, and breakdown to
0207 that task's events — the per-task error history is the overall view
0208 filtered, not a separate surface. The parameter is open: any task id
0209 reached by link is valid, and no task list is offered on the view
0210 itself. The PanDA task page (`panda/tasks/<jeditaskid>/`) links to
0211 its filtered errors view. Refinements specific to the per-task
0212 reading come later; the filter is the mechanism from the start.
0213 
0214 ## Proactive storm response
0215 
0216 Planned, the next stage of this design: the recorded error stream is
0217 the trigger surface for automatic storm response — detection,
0218 notification, and bounded automatic investigation that prepares
0219 information for human evaluation. Investigation latency is accepted
0220 by design; the alternative cost is operator time.
0221 
0222 ### Storm detection
0223 
0224 A detector rides the component publisher: every publication counts
0225 one interval, and the detector evaluates it against the trailing
0226 baseline. Detection covers every terminal state, closed included —
0227 the presentation default that filters closures out of the errors
0228 view never applies here, because a closure flood is itself an
0229 infrastructure signal. A storm starts when an interval's errors exceed the larger
0230 of an absolute floor and a multiple of the trailing median; it ends
0231 after a run of quiet intervals. Detection is stateful and emits on
0232 transitions — storm start, escalation, storm end — never per
0233 interval. Thresholds are SysConfig keys, present at their defaults.
0234 The detector makes the maintainer a canary in place: it already
0235 reads every interval of the production error stream, so no separate
0236 sentinel is needed.
0237 
0238 Silence is itself a signal. An infrastructure incident can stall the
0239 maintainer's own pass, and the canary then reports nothing precisely
0240 when it matters most. A freshness watch on the errors publication
0241 emits through the same router when the component goes quiet beyond a
0242 few cycles, at a severity distinct from a storm.
0243 
0244 ### Notice and alarm
0245 
0246 Storm start emits a Capcom notice through the notice router carrying
0247 the attribution reading — the category, task, and site concentration
0248 verdicts the breakdown card computes — and a link to the errors view
0249 windowed to the storm. Storm end reports totals. An alarm fires only
0250 above a second, higher threshold, itself a configuration value.
0251 
0252 ### Bounded drilldown
0253 
0254 Storm start also triggers the deterministic investigation tier
0255 through the production-operations agent: Bamboo log classification
0256 (the classify_failure analysis of the refinement tiers above) on one
0257 representative job per top diagnostic pattern, with results entering
0258 the action stream and enriching the notice. Directed canary probes
0259 join this tier when canary jobs exist (site-canary). The work is
0260 bounded per storm: a fixed number of representative jobs, one pass
0261 per transition.
0262 
0263 ### AI analysis tier
0264 
0265 An AI pass over the mined material — summarizing, hypothesizing,
0266 drafting the evaluation brief — is the tier where analysis beyond
0267 deterministic tooling can add value, applied only where the
0268 deterministic tiers stop. The apparatus exists: a threshold-gated,
0269 case-specific action launches a corun-ai evaluation of the storm
0270 dossier — the attribution reading, the diagnostic patterns, and the
0271 drilldown results — and the returned evaluation document links from
0272 the notice for human review. Spend and gating remain explicit
0273 operator configuration; the tiers above stand on their own without
0274 it.
0275 
0276 ### Bounded action
0277 
0278 The escalation path beyond evaluation: the storm AI first notifies,
0279 then proposes, then — within explicit bounds — acts. The proposal
0280 stage uses the established AI-proposals pattern (AI_PROPOSALS.md):
0281 the evaluation concludes with a concrete deterministic action, such
0282 as pausing the dominant task, that a human approves in one step.
0283 Bounded autonomous action comes later and stays within a curated
0284 vocabulary of reversible operations — task pause is the model case,
0285 and the task-operation set already excludes irreversible actions —
0286 gated by its own thresholds and an explicit allowlist. The goal
0287 condition: within minutes of a major failure, the flow of jobs into
0288 a failing configuration is stopped, and the human reviews an action
0289 that one step undoes.
0290 
0291 ## Retrieval
0292 
0293 The component rides the existing Snapper retrieval surface unchanged:
0294 the REST endpoints and MCP tools (`snapper_component_history`,
0295 `snapper_state_at`, `snapper_changes_between`) answer when a category
0296 appeared, grew, or stopped in a scope or a task, with the standard
0297 evidence envelopes. Assessment harnesses and other AI consumers read
0298 the same history the view renders.
0299 
0300 ## Implementation notes
0301 
0302 - Maintainer module `monitor_app/snapper_errors.py`, invoked from the
0303   System-status refresh beside `publish_panda_activity`; publication
0304   errors fail visibly per the existing maintainer convention.
0305 - Provider additions in `monitor_app/snapper_providers.py`: event
0306   extraction from the interval entries into category, component, and
0307   per-task curves; the errors focus view with its grouping selector
0308   and open task parameter; the breakdown card; the donut context.
0309 - Generic mechanisms in the snapper-ai package: the event_values
0310   provider hook and event-flow rendering (`snapper_ai/series.py` and
0311   the observatory template), and the open_option focus hook
0312   (`snapper_ai/views.py`).
0313 - Curve identifiers carry the category vocabulary; any vocabulary
0314   change bumps the series cache version per the standing rule.
0315 - Component bounds: entries capped per interval with exact overflow
0316   folding. The component stays curated, bounded JSON per the Snapper
0317   design contract.
0318 
0319 ## Related
0320 
0321 - [SNAPPER.md](SNAPPER.md) — Snapper operations in SWF: capture
0322   scheduler, component maintainers, web presentation.
0323 - [ARCHITECTURE_MAP.md](https://github.com/BNLNPPS/swf-epicprod/blob/main/docs/ARCHITECTURE_MAP.md)
0324   — platform placement of the PanDA monitoring layer.
0325 - `monitor_app/panda/queries.py` — `error_summary`, `study_job`; the
0326   live-record aggregations the view links into.
0327 - [bamboo-mcp](https://github.com/BNLNPPS/bamboo-mcp) — the
0328   log-analysis classification used for progressive refinement.