Timeouts and Retries
timeout and retries use the same names in both bindings but do different things. Read the section for your binding before turning either on.
Timeouts
| Python | Node.js | |
|---|---|---|
| Unit | Whole seconds (int) | Milliseconds, rounded up to whole seconds |
| Default | 0, which means no timeout | 30_000 (30 seconds) |
| Allowed range | 0 or more | More than 0 and at most 86_400_000 (24 hours). Other values throw RangeError. |
xqdb applies the timeout in two places:
- Connecting. It is the total budget for TCP connection attempts across every address the host name resolves to.
- Each socket read and write. A read that waits longer than the timeout fails. This includes waiting for a query result, so a q query that runs longer than the timeout before replying fails even though the server is healthy.
When a timeout fires, the call fails with an I/O error (XqdbIOError, or code XQDB_IO in Node.js), and xqdb closes the socket. The next sync() or asyn() opens a new connection. receive() does not, so call connect() and subscribe again first.
Node.js defaults to 30 seconds
Any Node.js request whose reply takes more than 30 seconds to arrive fails with XQDB_IO unless you raise timeout. Set it above your slowest expected query, up to 24 hours. Subscription connections need it above the longest quiet period of the feed.
Python defaults to no timeout
With timeout=0, a call can block its thread indefinitely if the server or the network stops responding. Set a timeout for request/response connections. A subscription connection that waits on a quiet feed can keep 0.
Retries in Python
retries wraps sync(), asyn(), and receive(). It does not wrap connect(). Only XqdbIOError triggers a retry. Authentication failures, q errors, and conversion errors are raised immediately.
With retries=N greater than 0, xqdb makes at most N attempts in total. After failed attempt k (counting from 0) it sleeps 2k seconds, and it also sleeps after the final failure before raising it:
retries | Attempts | Sleep before the error is raised |
|---|---|---|
0 (default) | 1 | none |
1 | 1 | 1 s |
2 | 2 | 1 s + 2 s = 3 s |
3 | 3 | 1 s + 2 s + 4 s = 7 s |
Each retry logs an INFO message through Python's root logging logger.
A retried request is sent again
On each retry, xqdb opens a new connection and sends the same request again. If the first attempt failed after q had already received it, for example because the connection dropped while q was running the query, q runs the request twice. Use retries only for requests that are safe to repeat, such as reads. For inserts, upserts, and other side effects, keep retries=0 and handle XqdbIOError yourself.
receive() retries reconnect before sleeping, and the new connection has no subscription. If that reconnect fails, its error is raised straight away. For feeds, keep retries=0 and resubscribe explicitly, as shown in Queries and Subscriptions.
To retry only reads, write the loop yourself:
import time
import xqdb
conn = xqdb.Q("localhost", 1800, timeout=30)
def read(query: str, attempts: int = 3):
for attempt in range(attempts):
try:
return conn.sync(query)
except xqdb.XqdbIOError:
if attempt == attempts - 1:
raise
time.sleep(2**attempt)
trades = read("select from trade where date=.z.d")Retries in Node.js
retries applies only to explicit connect() calls, including the one inside Q.connect(options):
retries: Nallows up to N more connection attempts after the first one fails.- Attempts follow each other immediately, with no delay.
- Only
XQDB_IOfailures are retried. TLS handshake failures count asXQDB_IO. Authentication failures (XQDB_AUTH) are not retried. - Queries are never retried or re-sent. When
sync()orasyn()connects automatically, it makes one attempt.
To wait between attempts, call connect() in your own loop with a delay.
Cancelling a call
Release 0.1.8 has no cancellation API. Use timeout to bound how long a call can wait.
- Python.
sync(),asyn(), andreceive()block the calling thread until they finish, fail, or time out. - Node.js. A pending promise cannot be cancelled.
disconnect()is queued behind commands already running on that connection, so adisconnect()issued during a pendingreceive()resolves only after thereceive()gets a message or times out. Sizetimeoutso that this wait is acceptable.
