Batching and Retries

How the SDK queues events, sends them, retries failures and behaves when the queue fills or the process exits.

All four SDKs (version 1.1) share one delivery model. They queue events in memory, send them in batches in the background, and never block or fail a request in your application.

Defaults

| Setting | Default | Notes | | --- | --- | --- | | batchSize | 100 | Events that trigger a flush | | flushIntervalMs | 2000 | A timer also flushes, with up to 500 ms of random jitter | | Largest request | 500 events | The server accepts up to 1,000, the SDK stays below that | | maxQueueSize | 10,000 | Events held in memory | | maxInFlight | 4 | Concurrent requests. The Python SDK sends from one thread | | Request timeout | 10 seconds | Per attempt |

Each batch is gzip compressed and carries an X-Nolax-Batch-Id header (a new UUID per batch, reused on every retry of that batch) and an X-Nolax-SDK header such as node/1.1.0.

How each response is handled

| Response | What the SDK does | | --- | --- | | 2xx | The batch is delivered | | 400 | Drops the batch. Retrying cannot fix an invalid payload | | 401 | Drops the batch and pauses sending (see below) | | 403 with PLAN_LIMIT_EXCEEDED | Drops the batch and pauses sending | | Other 403, other 4xx except 408 | Drops the batch | | 413 | Splits the batch in half and sends each half once. A half that is still too large is dropped | | 429 with PLAN_LIMIT | Drops the batch and pauses sending | | 429 with RATE_LIMITED, 503 | Waits for the Retry-After value (at most 30 seconds), then retries | | 408, other 5xx, network error | Retries with exponential backoff | | 3xx | Drops the batch. The SDK does not follow redirects |

A batch is sent at most 3 times. Backoff starts at 1 second and doubles, with up to 25 percent random jitter added so that many clients do not retry in step.

Pausing

A rejected API key or a reached plan limit will not fix itself in seconds, so the SDK stops sending and discards new events while paused. The first pause lasts 60 seconds. If the next attempt is rejected again the pause doubles, up to 15 minutes. When a pause ends the SDK tries again, and one accepted batch resets the pause length. The SDK writes one warning to the console when it pauses, and calls your onError callback for each rejected batch.

When the queue is full

The queue holds maxQueueSize events. When it is full, the oldest event is dropped to make room for the newest. The SDK counts every drop and reports the counts to Nolax in the next batch, under meta.sdk (dropped, queued, sendFailures), so a lost event is visible rather than silent.

Server hints

A 2xx, 429 or 503 response can carry an sdk object that tunes the client. The SDK applies the values it knows and ignores the rest, clamped to safe ranges:

| Hint | Range | | --- | --- | | maxBatch | 1 to 500 | | flushIntervalMs | 500 to 30,000 | | maxInFlight | 1 to 8 | | backoffMs | 250 to 30,000 |

This lets Nolax slow clients down during an incident without a new SDK release.

Flushing on shutdown

Events still in the queue when the process ends would be lost, so each SDK flushes on the way out, waiting at most two seconds. A stalled server cannot hold your process open past that limit.

| SDK | What triggers the flush | | --- | --- | | Node.js | The beforeExit event, or a call to flush() on the middleware | | Python | Interpreter exit, or a call to flush() on the integration object | | .NET | The host stopping | | Java | The servlet container destroying the filter |

Process managers that kill a process without warning, such as SIGKILL, give no chance to flush. Events from the last few seconds can be lost in that case.

Related pages

See Node.js, Python, .NET and Java for each SDK's options, and What the SDK sends for the payload.