Back to home page

EIC code displayed by LXR

 
 

    


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

0001 # Snapper Platform — PanDA platform health component and view
0002 
0003 The health of the PanDA platform — server, database, web tier — becomes
0004 part of the recorded system history: a platform component captured in
0005 Snapper snaps, and a dedicated Platform view showing load, platform
0006 state, and consequences on one time axis, with an aggregated summary of
0007 every metric at the cut. It builds on the Snapper concepts and SWF
0008 deployment in [SNAPPER.md](SNAPPER.md), the generic package
0009 documentation
0010 ([DESIGN.md](https://github.com/BNLNPPS/snapper-ai/blob/main/docs/DESIGN.md),
0011 [INTEGRATION.md](https://github.com/BNLNPPS/snapper-ai/blob/main/docs/INTEGRATION.md)),
0012 and the error-state component in [SNAPPER_ERRORS.md](SNAPPER_ERRORS.md),
0013 whose recorded events it reuses. The server-host reporter is documented
0014 in [PANDA_SERVER_REPORTER.md](PANDA_SERVER_REPORTER.md).
0015 
0016 This is a design document; the sections below are the agreed plan of
0017 record for implementation.
0018 
0019 ## Historical questions
0020 
0021 Registration begins with the questions the record must answer
0022 (DESIGN.md, invariant 6):
0023 
0024 - At an instant, was the platform degraded — connections near the
0025   limit, requests slow or failing, daemons silent — and under what
0026   load?
0027 - Were pilot heartbeats reaching the server, at the rate the running
0028   population implies?
0029 - Which recorded quantity moves first when degradation follows load,
0030   and by how much lead?
0031 - What consequences followed — lost-heartbeat kills, worker-ended
0032   failures, finished jobs not recorded — and when relative to the
0033   platform signal?
0034 
0035 Each quantity below names the question it serves; the useful
0036 resolution is the five-minute refresh cadence throughout.
0037 
0038 ## The platform component
0039 
0040 A component, internal name `platform`, in the epicprod scope, published
0041 by a maintainer module (`monitor_app/snapper_platform.py`) beside the
0042 PanDA activity and error-state maintainers on the same 5-minute
0043 System-status refresh. Publisher identity `swf-monitor:panda-platform`,
0044 assessment policy `swf-panda-platform-v1`, schema version 1, canonical
0045 JSON bounded at 64 KiB. Every publication carries the owner's assessed
0046 values; the view derives nothing from raw records (DESIGN.md,
0047 invariants 1 and 4).
0048 
0049 The projection has five groups of quantities, each with its kind:
0050 
0051 **Database (gauges, read from the PanDA database).** Connections
0052 total, active, idle, and waiting, with `max_connections`; the longest
0053 open transaction in seconds; jobsactive4 live and dead tuples; minutes
0054 since its last autovacuum. A bounded map of connections by application
0055 and state (16 entries, remainder folded into `other`). Question: was
0056 the database the constraint, and who held its connections.
0057 
0058 **Heartbeats (assessed, from the job records).** For running jobs: the
0059 count whose last modification is older than 30, 60, and 120 minutes —
0060 scope-level and per site in a bounded map keyed as the PanDA
0061 component's site maps are — and the count of running jobs whose
0062 modification time advanced in the publication interval (heartbeats
0063 received, on the error component's interval idiom: source time
0064 advances atomically with content, so intervals tile). Heartbeat yield
0065 is published as an assessed ratio: received over expected, expected
0066 being the running population times the interval over the pilot's
0067 30-minute heartbeat period. The per-interval yield beats against the
0068 heartbeat phase — a five-minute interval sees whichever pilots' 30-minute
0069 clocks fall inside it — so the component also publishes the yield
0070 over a window of two heartbeat periods (60 minutes at the configured
0071 period) as a ratio of sums: the received and expected counts of the
0072 intervals ending inside the window, carried in the record and summed,
0073 never a mean of per-interval ratios (the running population moves
0074 between intervals). The window yield is the assessed figure the
0075 verdict and the alarm read; the per-interval yield stays in the
0076 record. Job starts in the interval complete the
0077 group. Question: were pilots being heard, and at the expected rate.
0078 
0079 **Server (gauge, measured by the maintainer).** One timed request to
0080 the PanDA server's status endpoint from the swf-monitor host, recorded as
0081 milliseconds, with a timeout recorded as a timeout, never omitted.
0082 Question: was the server answering.
0083 
0084 **pandamon (gauge, measured by the maintainer).** Two timed requests
0085 to pandamon, the PanDA monitor (BigPanDA) web face on pandamon01: its
0086 front page, and the harvester worker-stats query over the last hour —
0087 the request swf-monitor's own tools make. Recorded as the server
0088 measurement is, with its own timeout. pandamon is a distinct tier
0089 from the server: its queries are read load on the same database, so
0090 its latency is both a symptom and a cause. Question: was pandamon
0091 answering, at the cost its consumers pay.
0092 
0093 **Server host (delivered by the reporter).** Web-tier request counts
0094 per endpoint class and status class for the interval (updateJob,
0095 getJob, harvester, other; 2xx, 4xx, 5xx), error-log marker counts,
0096 per-daemon liveness and seconds since last log line, Watcher kills per
0097 interval; the WSGI tier as process count, total resident memory, and
0098 restarts in the interval; host load average, memory used and
0099 available, swap, and root and /var volume use; busy and idle web
0100 workers when mod_status is enabled. These fields are absent until the
0101 reporter runs and carry a `reported_at`; the maintainer publishes
0102 `reporter_status` as `fresh`, `stale`, or `absent` against a SysConfig
0103 threshold, and crossing that threshold is a semantic change that
0104 publishes (DESIGN.md, Maintained assessments). Question: what the
0105 server host itself saw.
0106 
0107 **swf-monitor host (measured locally by the maintainer).**
0108 swf-monitor's own tier on pandaserver02: Apache WSGI process count and
0109 resident memory, the ASGI service (swf-monitor-mcp-asgi) liveness and
0110 resident memory, the prod-ops agent's resident memory, host load
0111 average, memory and swap, root, /var, and /data volume use, and the
0112 swf-monitor database's connection count. Question: was swf-monitor itself under
0113 strain when it recorded the platform — a degraded observer is part of
0114 the evidence.
0115 
0116 Load quantities — jobs in flight by state, running cores, in-flight by
0117 site — are not recorded again: the PanDA activity component already
0118 carries them at the same cadence, and the view reads them from there.
0119 Consequences — lost-heartbeat and worker-ended kills, all faulty job
0120 events — are the error-state component's interval entries, likewise
0121 reused; finished and failed totals are the PanDA component's cumulative
0122 counters. One record per fact.
0123 
0124 Publication is unconditional each cycle: the gauges change every
0125 interval, so the component drives one snap per refresh as the PanDA
0126 component does. Registration precedes publication in the same
0127 transaction, as the error maintainer does.
0128 
0129 ### Backfill
0130 
0131 The consequence and load curves have recorded history already (the
0132 panda counter backfill on an hourly grid, the error entries backfill on
0133 the 5-minute grid). The platform gauges begin at first publication.
0134 Heartbeat staleness and received counts are not reconstructible: job
0135 modification times are overwritten, so no past instant's staleness
0136 survives in the records. The view states the record's start rather
0137 than implying earlier coverage.
0138 
0139 ## The server-host reporter
0140 
0141 The reporter agent on pandaserver01 (PANDA_SERVER_REPORTER.md) posts
0142 one record per 5-minute interval to a new authenticated ingest,
0143 `POST /api/snapper/platform/report/`, on the pattern of the episode
0144 write endpoints (token or session authentication, an authorized
0145 reporter identity in the body). The ingest validates the record against
0146 a declared shape and stores it as the current server-host report (one
0147 row, replaced per post, with `reported_at`); the maintainer reads it at
0148 publication and merges it into the component. A buffered backlog posts
0149 as a batch and the ingest keeps the newest; the record's own interval
0150 stamps stay with it. The reporter never publishes to Snapper directly:
0151 the maintainer remains the single owner of the component.
0152 
0153 ## The Platform view
0154 
0155 A dedicated focus view, `Platform`, on the focus-view mechanism the
0156 Errors and Site views use: its own clean path under the epicprod scope
0157 (`/snapper/epicprod/platform/`), a focus-sized cached series product
0158 over the `platform`, `panda`, and `errors` components' snaps, and its
0159 own detail rendering. Its families are absent from the compact scope
0160 report; the scope's front door does not grow.
0161 
0162 **Panels, in order — the platform's own quantities first, then the
0163 load and consequence panels beneath them for correlation by eye —
0164 each family's control row docked above its panel:**
0165 
0166 1. *Heartbeats* — received per interval and starts per interval.
0167    Starts are in practice a subset of the heartbeats received (a
0168    job that starts in the interval and is still running has
0169    heartbeated), so the area beneath the starts curve carries a
0170    light hatch marking the subset relation. It is a hatch rather
0171    than a solid fill because on this page a solid fill is a stacked
0172    band. Starts above received are jobs that started and left the
0173    running state within one interval, the burn-through signature,
0174    and stay visible. *Heartbeat yield* as its own small panel on a
0175    0–1 scale: the 60-minute window yield bold; the per-interval yield
0176    is a faint member that starts unticked, since its phase spikes
0177    would set the axis and flatten the window curve. The window curve
0178    is derived at series time from
0179    the recorded per-interval received and expected counts (a rolling
0180    ratio of sums over the trailing hour of snaps), so it spans the
0181    record from its first publication; it equals the record's own
0182    window figure wherever both exist.
0183 2. *Heartbeat staleness* — the 30–60, 60–120, and over-120-minute
0184    bands stacked (the recorded nested tiers plotted as exclusive
0185    bands); a staleness selector switches the panel to the over-120
0186    count by site.
0187 3. *DB activity* — active and waiting stacked, active in blue and
0188    waiting in the warning color, on their own scale. *DB connections*
0189    follows as a small panel with the pool total as one line: a drop to
0190    zero is a server restart, a climb is a leak or a second pool. Idle
0191    connections are not plotted: the PanDA server's persistent pools
0192    hold about 120 of them, a base that would compress the activity
0193    to a sliver if stacked beneath it. Idle appears on the card and in
0194    the summary. The connection limit is likewise stated on the card
0195    and in the summary, not drawn: on the plot it dwarfs both panels.
0196 4. *Server latency* — milliseconds; a timeout records at the timeout
0197    value. *pandamon latency* follows on its own panel: the front
0198    page and the worker-stats query, same treatment.
0199 5. *Web tier* — request rates by endpoint class and the 5xx count,
0200    present when the reporter reports; daemon liveness renders as lanes
0201    above the panels (per-daemon continuous lanes on the health-lane
0202    mechanism: green alive, failure color silent), so a stalled
0203    copyArchive is a red band, not a number.
0204 6. *Hosts* — per host, PanDA server and swf-monitor: load average; memory used;
0205    volume use as percent, one line per volume; WSGI, ASGI, and agent
0206    resident memory; service liveness as lanes beside the daemon lanes.
0207 7. *Jobs in flight* — the scope's in-flight jobs family (by state,
0208    stacked) with running cores as the overlay line, as the Site view
0209    draws them.
0210 8. *Faulty job events* — the error-state component's events by
0211    component (dispatcher, taskbuffer, ddm, pilot, …), event-flow
0212    binned; the terminal-state chips apply as on the Errors view.
0213 9. *Job outcomes* — finished and failed, window-relative.
0214 
0215 Units on every panel title; house state colors where a state is drawn;
0216 red only where failure lives. Member ticks stay on for the small
0217 families and off for the event-flow family, as declared on the Errors
0218 view.
0219 
0220 **The cut.** A click is a time cut. The detail section renders the
0221 platform card at that instant: the database breakdown (connections by
0222 application and state, longest transaction, table health), the
0223 heartbeat table (staleness tiers by site, received against expected,
0224 yield), the server measurement, and the server-host table (daemons
0225 with age of last log line, web-tier counts, host resources, reporter
0226 freshness). Every site is a link to the Site view at the same cut; a
0227 kills row links to the Errors view windowed to the same bounds, where
0228 the error breakdown, patterns, and attribution reading already live.
0229 The card states its interval basis once.
0230 
0231 **Thresholds** — yield floor, connection fraction, latency ceiling,
0232 staleness fraction, reporter staleness — are SysConfig keys present at
0233 their defaults; the card marks a crossed threshold in the warning color
0234 and names the key.
0235 
0236 ## The summary at the cut
0237 
0238 Below the panels, at the bottom of the page, the cut renders one
0239 aggregated summary across every metric the view plots — load,
0240 platform, and consequences in one table — so the state of the whole
0241 platform at an instant reads in one place and relationships between
0242 metrics can be judged by eye. One row per metric, in panel order, each
0243 carrying its curve swatch: the value at the cut, the change against
0244 the previous snap, the metric's minimum, mean, and maximum over the
0245 visible range with the cut value's position in that range, and a
0246 threshold mark where one is crossed. Interval metrics (heartbeats
0247 received, kills, starts) report over the detail window the event-flow
0248 cut uses; gauges report at the cut instant; window-relative counters
0249 report their accumulation from the view's left edge. The summary reads
0250 the coherent snap at the cut — the platform, PanDA activity, and
0251 error-state components of one registry cut — so its rows are
0252 simultaneous readings of one recorded state, with the components' own
0253 assessment times stated once beneath the table.
0254 
0255 The per-component detail cards (database breakdown, heartbeat table,
0256 server-host table) dock beneath their panels as on the Site view; the
0257 summary is the one section that spans them.
0258 
0259 Correlation as a computed function of the view — pairwise coefficients
0260 with lag, and ranking of predictors for a chosen response — is a later
0261 round, designed as a generic snapper-ai mechanism declared per focus
0262 view; the summary table is the first round's instrument for reading
0263 relationships.
0264 
0265 ## Detection and notice
0266 
0267 The maintainer evaluates each publication against the thresholds and
0268 records the verdicts in the component's `assessment`. Notice is the
0269 alarm engine's job ([alarms.md](alarms.md)): the
0270 `panda_platform_health` alarm reads the latest published component on
0271 each engine tick and raises one detection per metric in warning —
0272 heartbeat yield, heartbeat staleness, database connections, server
0273 latency, pandamon latency, swf-monitor volumes, swf-monitor services —
0274 plus one when the
0275 component itself is absent, unreadable, or silent beyond
0276 `stale_after_minutes`. Heartbeat verdicts are suppressed below
0277 `min_running` running jobs, where the rates are noise. The thresholds
0278 stay with the record (the `platform_*` SysConfig keys); the alarm
0279 carries only its own two parameters. The engine's state-based dedup
0280 gives the transition behaviour — an event opens when a metric enters
0281 warning and clears when it leaves — and the per-alarm email gate,
0282 recipients, and renotification window are edited on the alarms
0283 dashboard. Capcom carries nothing from this path: the alarm is the
0284 notice.
0285 
0286 A `panda-platform` System Status collector reads the latest published
0287 component so the platform state enters the System page and the health
0288 lane without a second source.
0289 
0290 ## Node health map
0291 
0292 The staleness tiers say how many running jobs are silent at each
0293 site; the node health map says which nodes. Every job record carries
0294 the worker node in `modificationhost` (`nid006841` at
0295 NERSC_Perlmutter_epic, `n388` at UM_GREX_PanDA_1, a GKE node name at
0296 BNL_ePIC_GOOGLE), so the heartbeat reading groups silent running jobs
0297 by site and node. For each site the record carries the nodes whose
0298 running jobs are all silent beyond the warning tier
0299 (`platform_stale_warn_tier_minutes`) with the job count and the age
0300 of the oldest silence, bounded to the 32 worst nodes per site by
0301 silent count with the remainder folded into a count
0302 (`heartbeats.nodes`, on the site map's bounding rule). A node whose
0303 jobs are all silent while its neighbours heartbeat is a node fault —
0304 the 2026-08-25 lost-heartbeat storm was per-node I/O stalls starting
0305 at different times on different nodes; silence across every node of a
0306 site is a site path or server fault. The distinction is the first
0307 question a site asks, and it is answered from the record without a
0308 log.
0309 
0310 The staleness panel is unchanged; the map renders at the cut as a
0311 per-site node table beneath the heartbeat table, and the
0312 `heartbeat_staleness` alarm detection names the top silent nodes and
0313 their silence onset in its detail, the text a site needs to act.
0314 Where a site publishes per-job files at a known location (the NERSC
0315 portal directory per PanDA id), the node table links the job whose
0316 silence began first on that node, since its pilot log is the
0317 discriminator (ERROR_ATTRIBUTION.md, dig triggers). The site-canary
0318 rider's fingerprint map later joins node identity to environment,
0319 so a faulty node reads with its platform, kernel, and mount state.
0320 
0321 The map also carries a node budget, the reading that catches a queue
0322 configuration before its first stall: per site, running jobs per node
0323 (the largest node's count and the median) and the resident memory per
0324 job from the trailing hour's finished jobs (`maxrss`, mean and 90th
0325 percentile), against the node memory and cores the queue definition
0326 declares. The product jobs × memory over the node's memory is
0327 published as a fraction with its own warning threshold
0328 (`platform_node_memory_warn_fraction`), evaluated per site, and it
0329 enters the alarm's detail and the summary at the cut. On 2026-08-25
0330 the NERSC_Perlmutter_epic queue ran 128 pilots per 476 GB node at
0331 5.7 GB each — a fraction of 1.5 — for hours before the first pilot
0332 went silent; the reading would have named the queue and the number
0333 at the first publication.
0334 
0335 ## Worker release for stalled jobs
0336 
0337 When the Watcher fails a running job for lost heartbeat, the job's
0338 worker is not released: PanDA's worker synchronization issues
0339 `SYNC_WORKERS_KILL` only when the pilot has reported `finished` and
0340 harvester lags behind it (worker_module.py, `get_workers_to_synchronize`),
0341 and harvester marks a job's workers for killing only on a
0342 `tobekilled` command (propagator.py), which the Watcher's failure
0343 never issues. A pilot blocked on I/O reports nothing, so its worker
0344 holds the node until the batch walltime — on 2026-08-25, up to two
0345 hours per worker after the job was already failed, on nodes that were
0346 producing nothing. The queue-level `sweepPQ` API kills every worker
0347 of a queue in a given status and is too blunt for this.
0348 
0349 The release uses the same command the sync daemon uses, with explicit
0350 worker ids: the Watcher-failed PanDA ids of the interval, joined
0351 through `harvester_rel_jobs_workers` to workers still `running` in
0352 `harvester_workers`, issued as `SYNC_WORKERS_KILL` per harvester id in
0353 shards of 100 through the server's `commandToHarvester`; harvester's
0354 sweeper marks the workers and the site's sweeper plugin cancels the
0355 batch jobs. The call is in-process on the PanDA server, so the
0356 releaser is a host-side script on pandaserver01 beside the reporter
0357 (PANDA_SERVER_REPORTER.md): standard library plus the server's own
0358 taskbuffer under the panda service environment, run on demand, never
0359 on a schedule of its own.
0360 
0361 Triggering and bounds follow the bounded-action ladder of
0362 SNAPPER_ERRORS.md. The `heartbeat_staleness` detection is the
0363 trigger; the release is first an AI-proposals action
0364 (AI_PROPOSALS.md) — the detection's detail lists the workers it
0365 would release, by site and node, and a person approves in one step —
0366 and becomes autonomous within an allowlist of sites and a per-episode
0367 cap once the proposal record shows it acting correctly. Every release
0368 is an action-stream record naming the detection, the workers, and the
0369 command ids written; a worker that is not `running` by the time the
0370 command is written is skipped and counted. The releaser never kills
0371 workers whose jobs are not already failed by the server: it releases
0372 resources the workflow has given up on, and takes no decision about
0373 the jobs.
0374 
0375 ## Retrieval
0376 
0377 The component rides the existing Snapper retrieval surface unchanged:
0378 the REST endpoints and MCP tools answer connection counts, staleness,
0379 yield, and server-host state at any instant with the standard evidence
0380 envelopes, and `changes_between` locates the transitions.
0381 
0382 The view's own products — the series (including the derived 60-minute
0383 yield curve and the window-relative counters) and the summary at the
0384 cut — are data as much as the components are, and AI clients receive
0385 them by query rather than by reading the page: the series and
0386 cut-summary tranche of the snapper-ai plan
0387 ([PLAN.md](https://github.com/BNLNPPS/snapper-ai/blob/main/docs/PLAN.md),
0388 section 9) serves the same products through REST and MCP in the
0389 standard envelope, with correlation following once the series is a
0390 query.
0391 
0392 ## Implementation notes
0393 
0394 - swf-monitor: `monitor_app/snapper_platform.py` (maintainer;
0395   registration, projection, thresholds), invoked from
0396   `scripts/refresh-system-status.py` after the error maintainer; the
0397   server-host report model and `viewdir/snapper_platform_api.py`
0398   ingest; provider additions in `snapper_providers.py` (curve
0399   extraction `plat_` ids, families, the Platform focus view declaration
0400   listing its three components, the platform card, daemon lanes through
0401   `lane_entries`); the card kind in `_snapper_cards.html`; a
0402   `panda-platform` collector in `system_status.py`; the reporter script
0403   `scripts/panda-server-reporter.py` (standard library only) and its
0404   unit file under `tools/`; the alarm module
0405   `alarms/swf_alarms/alarms/panda_platform_health.py` with its
0406   `alarm_panda_platform_health` config row; this document and the
0407   SNAPPER.md maintainer list.
0408 - snapper-ai: a focus-view declaration for a page-bottom summary
0409   section fed by the cut (the cut request already carries the view's
0410   left edge and the detail window; the summary needs the visible
0411   range's statistics, computed client-side from the loaded series and
0412   posted with the cut fetch, or computed server-side from the same
0413   product), and a `lane_entries` convention for daemon and service
0414   lanes if the health-lane path needs a second lane family; PLAN.md
0415   and INTEGRATION.md entries.
0416 - The focus series cache TTL rule gains the platform key class (live,
0417   90 s) beside the task key; the series cache version bumps with the
0418   new curve vocabulary.
0419 - Component bounds and the site map follow the PanDA component's
0420   catalog rule; the connections-by-application map folds beyond 16
0421   entries.
0422 - Order of delivery: maintainer and component first (the record starts
0423   accruing), then the view with its summary, then the reporter and
0424   ingest, then detection; then the node health map (record, card, and
0425   alarm detail), the worker releaser on pandaserver01 in its proposal
0426   form, and the dig triggers of ERROR_ATTRIBUTION.md; correlation in a
0427   later round. Each stage is usable on its own.
0428 
0429 ## Related
0430 
0431 - [SNAPPER.md](SNAPPER.md) — Snapper operations in SWF;
0432   [SNAPPER_ERRORS.md](SNAPPER_ERRORS.md) — the error-state component
0433   and view whose events this view reuses.
0434 - [PANDA_SERVER_REPORTER.md](PANDA_SERVER_REPORTER.md) — the
0435   server-host reporter's functions and access.
0436 - [ERROR_ATTRIBUTION.md](ERROR_ATTRIBUTION.md) — the label-correction
0437   service that consumes the same platform evidence.
0438 - snapper-ai
0439   [DESIGN.md](https://github.com/BNLNPPS/snapper-ai/blob/main/docs/DESIGN.md),
0440   [INTEGRATION.md](https://github.com/BNLNPPS/snapper-ai/blob/main/docs/INTEGRATION.md),
0441   [TIME_HISTORY_UI.md](https://github.com/BNLNPPS/snapper-ai/blob/main/docs/TIME_HISTORY_UI.md)
0442   — the contract, the provider seam, and the display laws.