iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
A ten-minute Node.js poller can alert on recorded game-job failures, but polling alone cannot prove that a job which produced no log ever started. The dependable design combines four signals: an error/log API cursor, node-cron lifecycle events, start-and-finish heartbeats, and a webhook delivery queue with retries and idempotency.
On each interval, the worker reads records newer than its last cursor (with a small overlap), deduplicates them, applies a player-impact threshold, sends a structured alert, and advances the cursor only after a successful query. A late heartbeat covers silent stoppage, while an external process monitor handles crashes that Node.js cannot safely recover from inside the process.
What this alerting pattern detects—and what polling misses
Polling an error or log API is effective when the game service records an exception, timeout, failed deployment hook, or other explicit event. The poller groups records by game, job, error class, and occurrence window, then emits one actionable notification instead of one message per log line.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Clear out junk files and repair common Windows errors3Scan for outdated or missing drivers - takes under a minuteThere is a critical blind spot: a process crash, removed schedule, disabled worker, or deployment that prevents startup may create no new error record. Pair the poller with a heartbeat sent at job start and completion. If the expected start or completion check-in is late, alert on the missing signal rather than waiting for an error record that will never arrive.
#1 Best Overall
The ten-minute interval is an operating choice, not a published reliability statistic. Use a shorter interval for jobs whose player impact rises quickly, and a longer one when the provider API is rate-limited or the job is low urgency.
Reference architecture
| Component | Responsibility | State to retain |
|---|---|---|
| node-cron scheduler | Starts one polling run every ten minutes and exposes missed, failed, and overlapping execution signals. | Task name, run ID, scheduled time |
| Log/error API client | Reads records after the last cursor, using a bounded timeout and retry policy. | Cursor, query window, provider event IDs |
| Normalizer and threshold | Converts provider-specific records into stable keys and decides when an alert is warranted. | First seen, latest seen, count, error class |
| Delivery worker | Sends a webhook, chat, or email request and retries transient failures. | Idempotency key, attempts, response status |
| Heartbeat endpoint | Receives start and completion check-ins so silent stoppage is visible. | Expected schedule, last start, last completion, duration |
| Durable store | Separates query progress from notification progress. | Cursor, delivery status, dedupe key, retention time |
For more than one Node.js instance, use a named distributed task or an external lock so only one instance polls a given interval. Configure noOverlap when a run can exceed its interval, and set an explicit IANA timezone instead of relying on the host setting. The scheduling options are documented at nodecron.com scheduling options.
Prepare the worker and its data model
Required inputs
- An API credential with read access to game errors or logs.
- A webhook, chat endpoint, or mail gateway that accepts the alert payload.
- Durable storage for a cursor and delivery records; process memory is not sufficient after a restart.
- A heartbeat receiver that can distinguish a start check-in from a completion check-in.
- A runbook URL or identifier for the person receiving the alert.
Use a stable event key
Prefer the provider’s immutable event ID. If none exists, construct a deterministic key from the game identifier, job identifier, normalized error class, and a fixed occurrence window. Store the key with the latest occurrence and count. This prevents an overlapping query window from sending the same failure repeatedly.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteKeep query and delivery state separate
Advance the cursor only after the API query has completed successfully and its records have been durably processed. Do not advance it merely because a webhook request was attempted. A delivery outage should leave a visible pending notification while allowing the next poll to continue from the correct position.
Rank #2
The ten-minute operating loop
- At minute 0, create a run ID and send a start heartbeat. Include the scheduled time, deployment version, host or instance ID, and expected completion deadline.
- Load the last successful cursor. Query from that cursor minus a small overlap. The overlap protects against provider indexing delay; deduplication makes it safe.
- Fetch and normalize records. Capture provider event ID when available, otherwise derive the stable key. Preserve the original timestamp and a redacted error message.
- Classify and threshold. Group by game and job, count occurrences, and decide whether one failure, a burst, or a sustained rate represents player impact.
- Deliver one structured alert per incident key. Include game, job, first-seen and latest-seen times, count, latest error, deployment version, run ID, and runbook reference.
- Persist the cursor after a successful query. Persist delivery status independently, including attempts and the last response status.
- Send a completion heartbeat. Include duration, number of records examined, number of incidents raised, and the cursor now stored.
- Escalate missing signals. Alert when the completion heartbeat is late, node-cron reports a failed or missed execution, an overlap is detected, or the process disappears.
Implementing a resilient poller
The following example uses a generic error API and webhook. Adapt the field names to the game service, but retain the ordering: timeout the request, normalize records, write durable state, then deliver.
import cron from 'node-cron';
import fetch from 'node-fetch';
const interval = '*/10 * * * *';
const overlapMs = 60_000;
function incidentKey(row) {
if (row.id) return String(row.id);
const bucket = Math.floor(new Date(row.occurredAt).getTime() / 300000);
return [row.game, row.job, row.errorClass, bucket].join(':');
}
async function requestJson(url, options = {}, timeoutMs = 8000) {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), timeoutMs);
try {
return await fetch(url, { ...options, signal: controller.signal });
} catch (error) {
if (error.name === 'AbortError') throw error;
throw error;
} finally {
clearTimeout(timer);
}
}
async function poll() {
const runId = crypto.randomUUID();
await heartbeat('start', { runId, scheduledAt: new Date().toISOString() });
const state = await store.readPollState();
const from = new Date(new Date(state.cursor).getTime() - overlapMs).toISOString();
const response = await requestJson(`${process.env.GAME_ERROR_API}?from=${encodeURIComponent(from)}`);
if (!response.ok) throw new Error(`error API returned ${response.status}`);
const rows = await response.json();
const incidents = aggregate(rows, incidentKey);
for (const incident of incidents) {
if (!passesThreshold(incident)) continue;
const key = `game-failure:${incident.key}`;
await deliverWithRetry(key, buildPayload(incident, runId));
}
const newest = newestTimestamp(rows, state.cursor);
await store.writePollState({ cursor: newest, runId });
await heartbeat('complete', { runId, count: rows.length, cursor: newest });
}
cron.schedule(interval, () => poll().catch(error => reportRunFailure(error)), {
name: 'game-error-poller',
noOverlap: true,
timezone: 'UTC'
});
Use a real durable implementation for store, heartbeat, aggregate, and deliverWithRetry. The cursor must be written transactionally with the normalized records or an equivalent idempotent operation; otherwise a crash between those writes can lose an incident.
Timeouts, retries, and safe webhook delivery
Every outbound API call can hang, fail at the network layer, return a throttling response, or return a non-success status. Bound it with an abort timeout. Classify an AbortError separately from an operational FetchError; record the status code and a safely truncated response body without storing secrets.
Recommended Free Tools
Retry only transient conditions, such as connection resets, timeouts, and selected 5xx or rate-limit responses. Use exponential backoff with a cap and a maximum attempt count. Include an idempotency key based on the incident key and alert version. The receiver can then accept a retry without creating a second incident.
Rank #3
async function deliverWithRetry(idempotencyKey, payload) {
let delay = 1000;
for (let attempt = 1; attempt <= 5; attempt += 1) {
try {
const response = await requestJson(process.env.ALERT_WEBHOOK, {
method: 'POST',
headers: {
'content-type': 'application/json',
'idempotency-key': idempotencyKey
},
body: JSON.stringify(payload)
});
const body = (await response.text()).slice(0, 2000);
await store.recordDelivery({ idempotencyKey, attempt, status: response.status, body });
if (response.ok) return;
if (![408, 429, 500, 502, 503, 504].includes(response.status)) break;
} catch (error) {
await store.recordDelivery({ idempotencyKey, attempt, error: error.name });
}
await new Promise(resolve => setTimeout(resolve, delay));
delay = Math.min(delay * 2, 30000);
}
await store.markDeliveryFailed(idempotencyKey);
throw new Error(`delivery failed for ${idempotencyKey}`);
}
A failed notification must itself be observable. Export a metric or send the pending item to a durable queue that an operator can replay. Never silently discard it after the final attempt.
Heartbeat checks for jobs that never ran
Send a start heartbeat immediately before the game job begins and a completion heartbeat in a finally path after success or failure. The monitor should know the expected cadence and a grace period. A missing start means the scheduler or process did not launch the job; a missing completion means the job hung, was killed, or failed before reporting.
Include a monotonic run ID, scheduled time, actual start time, completion time, duration, deployment version, and item count. Reject stale heartbeats from an old deployment, and sign requests or authenticate them so an arbitrary client cannot mark a dead job healthy.
HTTP telemetry is a common implementation for background jobs, websites, and APIs. A hosted monitor can reduce maintenance, but verify its retention, outage behavior, data residency, and program terms before depending on it for escalation.
Rank #4
Use node-cron lifecycle events as signals
node-cron documents execution:failed, execution:missed, execution:overlap, and task:failed events. Subscribe to them and route each event through the same incident pipeline as an API error. Include the event name, task name, scheduled timestamp, run ID when available, and error details.
The library also supports custom info, warn, error, and debug logging. Send structured records to your normal log sink so an operator can correlate scheduler events with API queries and webhook attempts.
For CPU-heavy or long-running work, node-cron background tasks execute in a forked process and support executeTimeout and startTimeout. Forward timeout and child-process failures to the same alert path; otherwise a worker can disappear while the parent scheduler still appears healthy.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Process crashes and deployment safety
Do not try to continue normal operation after an uncaught exception. Node.js states: “It is not safe to resume normal operation after ‘uncaughtException’.” Treat the process as compromised, exit, and let an external supervisor restart it. The supervisor must also generate an alert when the process stops, because no in-process heartbeat can run after a crash.
Best Value
During deployment, drain or pause the old instance before starting the new one, preserve the durable cursor, and prevent both versions from polling simultaneously. A named distributed lock should have an expiry and owner token so a dead instance cannot hold it forever. Test clock changes and daylight-saving transitions even when production runs in UTC.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Alert payload that an on-call engineer can act on
{
"type": "game_job_failure",
"incidentKey": "game-failure:abc123",
"game": "arena-eu",
"job": "match-settlement",
"errorClass": "DatabaseTimeout",
"firstSeen": "2026-10-18T12:00:04Z",
"latestSeen": "2026-10-18T12:08:51Z",
"count": 7,
"deployment": "2026.10.18-4",
"runId": "poll-run-id",
"latestError": "connection timed out after 5000 ms",
"runbook": "https://ops.example.invalid/runbooks/match-settlement"
}
Redact player identifiers, tokens, and full request bodies. Keep the latest error useful enough to select a runbook, but cap its length and normalize stack traces so deduplication is stable.
Choosing a deployment model
| Model | Detection coverage | Delivery reliability | Operational trade-off |
|---|---|---|---|
| Self-built in-process poller | Recorded errors plus scheduler events; heartbeat is required for silent stoppage. | You control retries, storage, and idempotency. | Requires on-call ownership, durable state, locking, and crash supervision. |
| Forked background task | Adds child-process start and execution timeouts. | Uses the same delivery path but needs child failure handling. | Useful for CPU-heavy work; more process lifecycle complexity. |
| Hosted HTTP monitor | Strong heartbeat and endpoint visibility; still depends on your job sending check-ins. | Provider usually supplies persistence and notification retries. | Less maintenance, but review provider availability, retention, data residency, and terms. |
| Framework-integrated monitoring | Can capture framework cron errors and apply alert rules. | Often offers webhook, chat, or email channels. | Convenient for an existing framework, but tied to that deployment and configuration model. |
For teams already using NestJS, NestJS Observe documents automatic cron-run error capture and alert rules that can notify through webhooks, Slack, or email. Confirm current capabilities and pricing before adopting it.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Testing and troubleshooting checklist
- Duplicate alerts: replay the same API page and verify that the provider event ID or derived key suppresses duplicates.
- Lost alerts after restart: kill the process between query and cursor write; confirm the overlap re-reads records and idempotency prevents duplicate delivery.
- Silent stoppage: stop the scheduler without creating an error record; verify the missing-start or missing-completion heartbeat fires.
- Slow provider: delay the API beyond the abort timeout and confirm the run is marked failed without advancing the cursor.
- Webhook outage: return 500, 429, and malformed responses; check capped retries, stored response details, and a visible pending state.
- Overlapping runs: make one poll last longer than ten minutes and verify
noOverlapor the distributed lock blocks a second run. - Crash recovery: raise an uncaught exception and ensure the external supervisor restarts the process and alerts on the downtime.
- Deployment race: run two instances with the same task name and verify only the lock owner polls.
- Time-zone changes: test the configured IANA timezone around a daylight-saving transition and confirm the intended schedule.
Operational costs and retention decisions
The direct cost of a self-built poller is usually storage, API calls, log volume, and engineering/on-call time rather than a fixed per-alert statistic. Retain cursor and delivery records long enough to investigate missed incidents, but apply a finite retention policy to error bodies and payloads containing sensitive data. A hosted monitor trades engineering effort for vendor availability and data-retention considerations; it does not remove the need for a heartbeat in the game job.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

