Skip to content

feat(monitoring): probe the Umami analytics collector every 15 minutes (#5565)#5567

Merged
koala73 merged 1 commit into
mainfrom
worktree-soft-seeking-bubble
Jul 25, 2026
Merged

feat(monitoring): probe the Umami analytics collector every 15 minutes (#5565)#5567
koala73 merged 1 commit into
mainfrom
worktree-soft-seeking-bubble

Conversation

@koala73

@koala73 koala73 commented Jul 24, 2026

Copy link
Copy Markdown
Owner

Summary

Adds a 15-minute scheduled probe of the self-hosted Umami analytics collector, closing items 3–4 of #5565.

On 2026-07-20 17:13 UTC the collector's Node process OOM-died. Railway neither restarted it nor flipped the deployment off SUCCESS, so nothing alerted for 4 days — ~1.1M events dropped across every tracked funnel (gate-hit, the #4931 checkout funnel, sign-up, and #5534's brand-new activation events, which shipped into a dead collector). The gap was found by hand, while asking an unrelated question about whether anyone had used the #5534 onboarding.

The service side is already mitigated (NODE_OPTIONS=--max-old-space-size=8192, restartPolicyType: ALWAYS). This PR is the part that makes the next failure visible in ≤15 minutes instead of ≤4 days.

Design decisions

  • Probes the collector directly, never Railway's deployment status — that status was green for the entire outage. Trusting it is what failed.
  • Three probes, because HTTP 200 on one path proves less than it looks like.
    probe expect proves
    /api/heartbeat 200 app process alive (this returned 502-after-15s for 4 days)
    /script.js 200 and body contains /api/send browsers can load the real tracker — an error page or proxy interstitial can answer 200
    GET /api/send 405 or 400 the write path is mounted
  • GET on the ingest route is deliberate. A mounted route rejects the wrong verb without recording anything, while a dead origin 5xxs. Never POST — that would pollute production analytics with synthetic monitor traffic. A test asserts the source contains no POST.
  • The named User-Agent is load-bearing, not cosmetic. Cloudflare's WAF 403s a bare curl/* agent on this host (verified: curl default → 403, named agent → 200). A probe without one alerts on a healthy collector — so a 403 is classified with its own message pointing at the probe rather than the collector.
  • Three attempts per probe before alerting, so a deploy restart or edge hiccup cannot page anyone. The failure this exists for lasted four days; it does not need a hair trigger.
  • Deliberately NOT gated on a green main, unlike seed-freshness-monitor.yml. The collector's health is independent of repo state, and a gate is one more way for the monitor to go quiet — the precise failure mode being fixed.

Validation

  • 8/8 unit tests (node --test), including the outage's exact signature (502 on all three paths), the 200-but-not-the-tracker case, the WAF-403 misclassification guard, and a workflow-wiring assertion.
  • Ran the real script against the live collector — green, 3 probes OK.
  • Proved it actually goes red, not just that it can pass:
    • dead origin (127.0.0.1:9) → exit 1, all three probes request failed
    • live-but-wrong origin (example.com) → exit 1, all three HTTP 404
  • docs:check (80 doc claims match code), docs:stats regenerated and committed (workflowCount 22→23), biome, markdownlint all clean.

Note: schedule/workflow_dispatch only fire from the default branch, so the workflow's first real run is post-merge. The script itself is verified end-to-end above.

Follow-up (not in this PR)

The memory bump is a runway extension, not a fix. The upstream sessionData.updateMany() P2002 flood (umami-software/umami#4183) resumed at full rate on restart — ~12 errors in 51s. At the observed retention rate 8 GB buys roughly 8–10 months. Upgrading the image is not an escape hatch: #4183 is still open and reported crash-looping on v3.2.0, so autoUpdates: {type: "patch"} should stay. This monitor is what makes the recurrence a 15-minute incident instead of a silent one.

Refs #5565


Claude Code

https://claude.ai/code/session_017LEK5hk1YCDNTwrvJkYV2k

#5565)

The collector's Node process OOM-died on 2026-07-20 and Railway neither
restarted it nor flipped the deployment off SUCCESS, so nothing alerted.
~1.1M events were dropped over 4 days and the gap was only found by hand
while asking an unrelated question about the #5534 activation funnel.

Probes the collector directly rather than trusting Railway's deployment
status, which stayed green throughout:

- heartbeat  /api/heartbeat -> 200      (app process alive)
- tracker    /script.js     -> 200 + must contain /api/send
                                        (browsers can load the real tracker,
                                         not an error page answering 200)
- ingest     GET /api/send  -> 405/400  (write path mounted; GET is the wrong
                                         verb on purpose so probing records no
                                         synthetic events)

Three attempts per probe before alerting so a deploy restart cannot page
anyone. A named User-Agent is mandatory, not cosmetic: Cloudflare's WAF 403s
a bare curl agent on this host, which would alert on a healthy collector —
that case is classified separately so it reads as a monitor bug.

Unlike seed-freshness-monitor.yml this is NOT gated on a green main: the
collector's health is independent of repo state, and a gate is one more way
for the monitor to go quiet.

Verified against production: green on the live collector, red (exit 1) against
a dead origin and against an origin that 200s but is not the collector.

Claude-Session: https://claude.ai/code/session_017LEK5hk1YCDNTwrvJkYV2k
@vercel

vercel Bot commented Jul 24, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
worldmonitor Ready Ready Preview, Comment Jul 24, 2026 8:52pm

Request Review

@mintlify

mintlify Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
WorldMonitor 🟢 Ready View Preview Jul 24, 2026, 8:50 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@koala73
koala73 merged commit cefe2a4 into main Jul 25, 2026
28 checks passed
@koala73
koala73 deleted the worktree-soft-seeking-bubble branch July 25, 2026 03:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant