Transport internals

The transport is the part of the SDK that puts events on the wire. Its contract is identical across JavaScript, Python, and Ruby: send each event immediately, queue only on failure, retry with backoff, and never block or crash your app. This page documents exactly how it behaves so you can reason about delivery under load and failure.

Event lifecycle

From capture to delivery, an event moves through these steps:

  1. Build — the stack is parsed, SDK metadata attached, the wire-format object assembled.
  2. Scrub — sensitive keys and (optionally) URL query strings are redacted. See Privacy & data.
  3. beforeSend — your hook can mutate or drop the event.
  4. Send — an HTTP POST with the DSN in the X-Tinymon-Key header is issued right away.
  5. Settle — on success the event is discarded; on failure it enters the retry queue.

There is no timer between steps 4 and 5. Capturing one event triggers exactly one send attempt immediately.

Status code handling

ResponseActionRationale
2xxDelivered, discardedSuccess.
4xxDropped, no retryA bad DSN or invalid payload won't succeed on retry — retrying would loop forever and waste the queue.
5xxQueued for retryServer-side/transient — worth resending.
Network error / timeoutQueued for retryConnection refused, DNS failure, offline — transient.

Retry queue & backoff

Failed events go into a bounded FIFO queue. A drain runs on a backoff schedule starting at 1 second and doubling after each failed drain up to a 30 second ceiling. The first success resets the backoff to 1 second. The queue is capped at 30 events; when full, the oldest event is dropped so a prolonged outage can never grow memory without bound.

Bounded by design. An SDK that buffers unboundedly during an outage becomes the outage. tinymon caps the buffer and prefers dropping the oldest events to threatening your process's memory.

Browser unload

In the browser, a normal fetch can be cancelled when the tab is closing, which would drop any event still in flight or queued. On the pagehide event the transport switches to navigator.sendBeacon, which the browser guarantees to deliver even as the page unloads. Because beacons can't set custom headers, the DSN rides along as a ?key= query parameter instead of the X-Tinymon-Key header.

Workers per language

The contract is the same everywhere; the concurrency primitive differs to match each runtime:

In every case the worker is a daemon/background thread, so it never keeps your process alive on its own and never blocks your request path.

Limits & tuning

ParameterValueMeaning
Max retry queue30 eventsOldest dropped on overflow.
Backoff start1 sDelay before the first retry drain.
Backoff max30 sCeiling for the doubling schedule.
Request timeout5 sPer send attempt (Python/Ruby).
flush() timeout2 s defaultCaller-overridable; see Delivery & flush().

These are deliberately not configurable knobs — they're tuned so the SDK stays invisible. If your use case needs different behavior, drop the event in beforeSend or front the SDK with your own queue.