Production hardening
Battle-tested patterns for production Drumbeats integrations. Retries, timeouts, payload limits, observability hooks, and failure isolation.
This page collects the patterns that turn a basic Drumbeats integration into a production-grade one. The language guides show the happy-path wiring; this page covers what happens when the network is slow, the job is flaky, or the payload is huge.
The advice is language-agnostic. Wherever a code sample appears it uses Node.js or shell for clarity — the equivalents in Python, Go, PHP, and Shell are straightforward.
Time-bound every ping
A slow Drumbeats request should never block your job. Cap the HTTP timeout at 3 seconds. The exact number matters less than having one — a worker that hangs because a side-channel observability call is slow is the worst kind of incident.
await fetch(url, { signal: AbortSignal.timeout(3_000) }).catch(() => {});await fetch(url, { signal: AbortSignal.timeout(3_000) }).catch(() => {});The trailing .catch(() => {}) is intentional. A failed ping should never propagate — your job is the priority. If the ping fails, the next one will surface the state. Treat Drumbeats as eventually consistent observability, not as part of the critical path.
Cap payload size
Each ping payload is stored against the run. Drumbeats includes up to 25KB of payload at no extra beat cost; everything beyond costs additional beats in 25KB chunks (see Beats and usage).
Production jobs that error rarely send small payloads but sometimes send 10MB of stack trace. Truncate before sending:
const MAX = 20_000;
const truncated = raw.length > MAX ? `${raw.slice(0, MAX)}…` : raw;const MAX = 20_000;
const truncated = raw.length > MAX ? `${raw.slice(0, MAX)}…` : raw;A 20KB cap leaves headroom for JSON envelope overhead while staying inside the free tier.
Use run_id correlation
Drumbeats correlates start and success / failure pings by the run_id query parameter. Without it, parallel runs of the same job race — success from run A can match start from run B.
For batch jobs that are not parallel, run_id is optional but recommended. The dashboard surfaces each run as a separate record so you can replay history.
For Event-driven monitors handling concurrent messages, run_id is mandatory. Suggested patterns:
- Time-based:
"job-${Date.now()}-${random}" - UUID:
"job-${randomUUID()}" - Domain ID:
"order-${order.id}"— natural correlation with your existing logs
Send progress logs for long jobs
A 30-minute backup that sends one start and one success shows up on the timeline as two events. A backup that sends log events every five minutes shows a heartbeat of progress — when the incident timeline becomes a root-cause investigation, the log entries are the most useful artifact you have.
curl -sf --max-time 3 -X POST "$API/log?run_id=$RUN_ID" \
-H 'Content-Type: application/json' \
-d '{"payload": "Compressed dump (45MB)"}'curl -sf --max-time 3 -X POST "$API/log?run_id=$RUN_ID" \
-H 'Content-Type: application/json' \
-d '{"payload": "Compressed dump (45MB)"}'Each log event costs one beat. For a job that runs nightly with five progress logs, that is 150 beats/month — cheap insurance.
Isolate ping failures from job logic
Failure modes to design against:
- Drumbeats unreachable. Network partition between your worker and
api.drumbeats.io. Pings fail; the job should still run. The.catch(() => {})pattern handles this. - Drumbeats slow. The 3-second timeout above handles this. Without a timeout a hung TCP connection can take minutes to fail.
- Drumbeats returns a 4xx. Likely a stale monitor ID or a malformed run_id. Treat as a logged warning, not a failure of the wrapped function.
The wrappers in the language guides already do this. If you write your own, do not call response.raise_for_status() (Python) or check response.ok (Node.js) on the ping — log the response, do not throw.
Multi-environment via slug
The same code path runs in staging and production. Three options:
- One monitor per environment, separate IDs. Pass the ID via env var:
DRUMBEATS_MONITOR=.... Simple but ID juggling is error-prone. - Slug-based ping endpoint.
/v1/s-ping/<project>/<slug>/<event>routes by project + slug. Code is identical in both environments — the project ID changes. Recommended for non-trivial deployments. - No staging monitors. Sometimes the right answer. Staging is noisy; you do not need Drumbeats to alert on every transient staging failure.
See Ping API: scheduled pings for the slug endpoint shape.
Observe the observer
Add the ping wrapper to your existing observability:
- Metrics. Count
pings_sent_total{event=start|success|failure}per job. A spike in failure pings is a leading indicator before the on-call sees them on Drumbeats. - Tracing. Wrap the ping in the same span as the work. The trace shows when in the request lifecycle the ping fired.
- Logs. Log the ping's response status code at debug level. Use it when investigating "why didn't Drumbeats alert?".
Pair max_duration_seconds with start
If your monitor type supports max_duration_seconds (Cron, Heartbeat, Event-driven), set it. A worker that crashes mid-run never sends failure — only Drumbeats' server-side hang detection catches the outage. Without max_duration_seconds, a hung worker is invisible until the next scheduled window.
What to put on call
Pair Drumbeats with three notification surfaces:
- Slack / Telegram for active incidents — interactive, fast acknowledgement.
- Email or PagerDuty for escalation — for incidents that stay open past business hours.
- Status page — for customers, so they know before they file a ticket. See Status pages.
The same monitor can route to all three via a single notification group.
Related guides
- Monitor types — pick the right one before hardening.
- Ping API: payloads — payload format and size details.
- Notifications: alert logic — cooldown and re-page rules.
- Alternatives — how Drumbeats compares to Cronitor and Healthchecks.io on production-readiness.