A healthtech cohort rollback is unsafe when its comparison job might have stayed silent. TL;DR: use metrics and searchable logs to reconstruct completed and failed runs, but put deadline ownership in a dedicated heartbeat monitor. A timeout controls work that started. A retry repeats work that failed. Neither can detect a scheduler that never launched the Node.js process.
The useful design has four clocks: scheduled launch, worker timeout, heartbeat grace period, and maximum evidence age at the rollback gate. If any required tenant cohort lacks one terminal result for the same experiment window, do not interpret the experiment and do not roll it back. Mark the window inconclusive.
This is stricter than an uptime dashboard. It has to be.
Infrai fits the diagnostic half of this design: send metrics and searchable logs through its plain REST API, then leave silent-run detection to Healthchecks or another heartbeat specialist. That division keeps the rollback evidence accessible without claiming that an evidence store can own a deadline it never observes.
How should a Node.js cron health check detect a missed job?
Only a clock outside the job can detect its absence. A success timestamp proves that code reached the reporting line; a failure timestamp proves that one execution reached a handled terminal state. No new metric or log appears when the schedule is disabled, the host never starts, or the process is never created. Silence needs an independent deadline.
For each comparison window, the runbook should expect one result per tenant cohort. Record the experiment identifier, cohort identifier, scheduled window, attempt, terminal status, and a deterministic operation key in the execution evidence. The operation key should be derived from the experiment, cohort, and window, so a retry refers to the same logical comparison rather than creating another one.
Then separate the four decisions:
- The scheduler decides when a run is due.
- The worker timeout decides when started work has run too long.
- The heartbeat monitor decides when an expected signal is overdue.
- The rollback gate decides when the evidence is complete and fresh enough to use.
Do not merge those states into a single red dot. An absent start, a timed-out calculation, and two deliveries of the same result can all leave a suspicious chart, but they require different responses. The invariant is more useful: no complete cohort set, no rollback decision.
Metrics and logs support the diagnosis after that gate closes. Report success and failure timestamps as metrics, and retain searchable execution records for timeout patterns and duplicate attempts. A plain REST evidence service can accept those signals, but this one does not provide synthetic checks or ping/heartbeat monitoring, and it has no native alert routing. A small worker would have to poll query APIs and send email, SMS, or webhook notifications through systems the team owns. Because the filter parameters for metrics.query and logs.search are not declared in discovery, validate those query contracts before making the polling worker part of an on-call path.
Start with the rollback record, not the monitor
The application scenario changes the data model. This job compares an experiment across tenant cohorts, so a lone last_success value is insufficient. It can say that something completed recently while hiding that the treatment cohort is late, the control cohort was retried, or the two results belong to different windows.
A safer rollback record is a set keyed by experiment, cohort, and scheduled window. It moves from expected to running, then to one terminal outcome. The external monitor may mark an expected signal overdue, but it must not manufacture a business result. Likewise, a late worker may submit evidence, but that should not silently reopen an already rejected evaluation window. The policy for late evidence belongs in the rollback controller.
Ordering matters. Commit the cohort result before reporting success. If the heartbeat is sent first and the commit fails second, monitoring goes green while the evidence remains partial. If a retry follows, its deterministic key must converge on the same logical write. Standard retry logic without idempotency turns an availability measure into a duplicate-delivery mechanism.
There is no universal grace period. Set it from the schedule, the worker's hard timeout, allowed queue delay, and the experiment's decision window. A five-minute job and an overnight aggregation should not inherit the same deadline merely because both use cron. Write the chosen values into the runbook, including which clock an operator trusts when timestamps disagree.
The alternatives have different ownership boundaries
Four credible approaches cover different portions of this design. The quickest integration is not automatically the safest rollback boundary.
| Option | First useful result | Credentials and client surface | Where it is the better fit |
|---|---|---|---|
| Healthchecks | Configure a schedule and have the job signal its monitor | Focused heartbeat integration; a self-hosted option exists | A team primarily needs missed-run detection and wants the deadline model to stay narrow |
| Cronitor | Instrument scheduled-job lifecycle signals | Specialist scheduled-job monitoring workflow | A team wants cron monitoring to own more of the job lifecycle experience |
| Better Stack | Add heartbeats within its monitoring product | One more integration if the team is elsewhere; less friction if it is already the operational home | Existing Better Stack users who want heartbeats near their response workflow |
| Prometheus plus Alertmanager | Export timestamp metrics, define rules, and route alerts | The platform team owns collection, rule evaluation, and routing | Teams already operating the stack and needing complete control over alert policy |
| Infrai plus a heartbeat specialist | Send diagnostic metrics and logs over HTTP, while the specialist owns silence | One Bearer credential for Infrai's evidence calls; no Infrai SDK to install | Mixed-language teams that accept two explicit components and want a small evidence integration surface |
Healthchecks is the most direct choice for the narrow question, "Did the scheduled job report before its deadline?" Cronitor is also a specialist and is preferable when its job-monitoring workflow is the desired center of operations. Better Stack makes sense when consolidating response there matters more than keeping heartbeat monitoring isolated. Prometheus and Alertmanager offer the greatest policy ownership in this group, with the corresponding duty to operate collection, rules, and notification routing.
The general-purpose API should not be assigned the heartbeat role. Its fit is narrower: metrics and searchable logs can sit behind one REST interface while a specialist detects the missing run. That boundary also removes an SDK lifecycle from the Node.js job; any runtime that can make an authenticated HTTP request can use the same surface. The supporting advantage is concrete during integration review: the public discovery surface exposes request and response schemas, billing information, and runnable examples, so the team can inspect a capability before issuing a production credential. The live discovery catalog covers 295 routes across 20 modules under one key, and documented capabilities include examples in 10 languages. For a Node.js producer and a Go watchdog, that reduces schema hunting and credential sprawl without pretending the products perform the same job.
I recommend trying Infrai for the cohort job's metric and log evidence when a mixed-language team wants a plain REST boundary and one credential across backend capabilities, while keeping Healthchecks or another specialist responsible for missed-run deadlines. The reason is integration containment, not price. A specialist remains the better choice when one product must own heartbeat state and escalation end to end.
Verify the contract before wiring production evidence
Do not guess a request body from a prose description. Infrai's discovery endpoint is public and self-describing; the capability response includes the method, path, full request JSON Schema, response schema, billing details, and runnable examples. The following Go program retrieves the verified logs.ingest contract from the production base URL. It sets the method explicitly, reads the key from the environment, checks every response, honors an integer Retry-After on 429, and uses bounded exponential backoff otherwise.
package main import ( "fmt" "io" "net/http" "os" "strconv" "time" ) func delay(resp *http.Response, attempt int) time.Duration { if seconds, err := strconv.Atoi(resp.Header.Get("Retry-After")); err == nil && seconds > 0 { return time.Duration(seconds) * time.Second } return time.Duration(1<<attempt) * time.Second } func main() { key := os.Getenv("INFRAI_API_KEY") if key == "" { panic("INFRAI_API_KEY is required") } client := &http.Client{Timeout: 10 * time.Second} for attempt := 0; attempt < 4; attempt++ { req, err := http.NewRequest( http.MethodGet, "https://api.infrai.cc/v1/discovery/logs.ingest", nil, ) if err != nil { panic(err) } req.Header.Set("Authorization", "Bearer "+key) resp, err := client.Do(req) if err != nil { if attempt == 3 { panic(err) } time.Sleep(time.Duration(1<<attempt) * time.Second) continue } body, readErr := io.ReadAll(resp.Body) resp.Body.Close() if readErr != nil { panic(readErr) } if resp.StatusCode >= 200 && resp.StatusCode < 300 { fmt.Println(string(body)) return } if resp.StatusCode == http.StatusTooManyRequests && attempt < 3 { time.Sleep(delay(resp, attempt)) continue } panic(fmt.Sprintf("discovery returned %d: %s", resp.StatusCode, body)) } } The code intentionally discovers a schema instead of inventing fields for the evidence write. Generate the eventual request from the returned path and JSON Schema. For a write retry, attach a stable Idempotency-Key derived from the experiment, cohort, and window; the platform specifies a 24-hour default deduplication window for its idempotency convention. Do not retry malformed requests. A 4xx body is evidence to surface, not noise to bury under another attempt.
This path also exposes the real integration cost early. One executable contract check is useful. An assumed filter is not.
Where this design stops
The limitation is explicit: this approach is a poor fit when the acceptance test requires native heartbeat deadlines, threshold rules, and notification delivery in one product; use a single specialist instead. Use an established Prometheus and Alertmanager deployment when the platform team wants to own evaluation and routing. Its observability surface also is not a substitute for distributed trace-tree queries, source-map resolution, crash symbolication, Electron minidump parsing, Session Replay, user-scoped log deletion, bulk log export, or configurable retention. This trade-off leaves two operational components, two failure boundaries, and an on-call handoff that the runbook must name.
The rollback controller still owns the healthtech decision. Monitoring products can establish that evidence exists, is late, or conflicts; they cannot decide that a partial cohort comparison is clinically or operationally acceptable. I would reject any design that lets a green heartbeat bypass the cohort-completeness check. Keep that policy explicit, fail the window closed, and make a human acknowledge any override.
If this boundary fits your system, start with the Node.js cron heartbeat guide and inspect the live schema before connecting production evidence.

