Connections and TLS
A connection is a Q object that owns at most one socket to one q process. Python and Node.js accept the same settings under slightly different names:
| Setting | Python xqdb.Q(...) | Node.js QOptions | Default |
|---|---|---|---|
| Host | host | host | Required. Empty means 127.0.0.1. |
| Port | port | port | Required. Node.js checks for an integer from 1 to 65535. |
| Username | user | user | "" |
| Password | passwd | password | "" |
| TLS | enable_tls | tls | Off |
| Socket timeout | timeout, whole seconds, 0 for none | timeout, milliseconds | Python 0, Node.js 30_000 |
| Retries | retries | retries | 0 |
| Invalid UTF-8 text | symbol_encoding | symbolEncoding | "strict" |
| DataFrame backend | backend | Not applicable | "pyarrow" |
timeout and retries behave differently in the two bindings. See Timeouts and Retries. symbol_encoding is covered in Values, Nulls, and Text.
import xqdb
conn = xqdb.Q(
"q.example.com",
5001,
user="analyst",
passwd="secret",
enable_tls=True,
timeout=30,
)import { Q } from "@xbbg/xqdb";
const q = await Q.connect({
host: "q.example.com",
port: 5001,
user: "analyst",
password: "secret",
tls: true,
timeout: 30_000,
});
await q.disconnect();Lifecycle
| Action | Python | Node.js |
|---|---|---|
| Create | Q(...) stores the settings and opens no socket. | new Q(options) validates options and throws RangeError if one is invalid. It opens no socket. |
| Connect | conn.connect(). Does nothing if already connected. | await q.connect(), or await Q.connect(options) to create and connect in one step. |
| Query | sync() and asyn() connect automatically when no socket is open. | sync() and asyn() connect automatically when no socket is open. |
| Receive | receive() does not open a socket. Without one it raises XqdbIOError("Not Connected."). With retries set, a failed receive() reconnects before it retries. | receive() does not open a socket. Without one it rejects with XQDB_IO. |
| Disconnect | conn.disconnect() raises XqdbIOError if no socket is open. | await q.disconnect() can be called any number of times. The same Q can connect again afterwards. |
After an I/O error, including a timeout, xqdb closes the socket. The next sync() or asyn() opens a new one. Server-side state tied to the old connection is gone, for example a tickerplant subscription.
A Python Q is not a context manager. Close it in finally, and guard disconnect() if the socket may already be closed:
import xqdb
conn = xqdb.Q("localhost", 1800)
try:
print(conn.sync("1+1"))
finally:
try:
conn.disconnect()
except xqdb.XqdbIOError:
pass # already disconnected, for example after an I/O errorIn Node.js, call disconnect() in finally and do not rely on garbage collection. A collected Q does close its socket and stop its worker thread, but only whenever the garbage collector gets to it.
Local hosts
In both bindings an empty host means 127.0.0.1. Python also replaces a host equal to this machine's own hostname (socket.gethostname()) with 127.0.0.1. Keep this in mind with TLS, because the certificate is then checked against 127.0.0.1.
Authentication
During the q handshake, xqdb sends user:password. It requests IPC capability 6 and accepts any capability of 1 or higher in the reply. q rejects bad credentials by closing the connection. xqdb reports that as XqdbAuthError, with code XQDB_AUTH in Node.js and the message Wrong credential. Authentication failures are never retried.
Credentials travel in clear text without TLS
q IPC authentication sends the username and password unencrypted. Enable TLS for any connection that carries credentials, unless another trusted transport such as a VPN or SSH tunnel already protects the socket.
TLS
Set enable_tls=True (Python) or tls: true (Node.js) when the q process listens with TLS enabled. xqdb uses rustls with TLS 1.2 or 1.3 and verifies:
- the certificate chain, against the operating system's trust store, and
- the
hostyou connected to, which must match a DNS name or IP address in the certificate.
An untrusted, expired, or mismatched certificate fails the connection. The handshake failure is reported as an I/O error, XqdbIOError in Python or XQDB_IO in Node.js, for example invalid peer certificate: UnknownIssuer. Because it is an I/O error, retries repeats the attempt. With TLS enabled, Node.js also requires host to be a valid DNS name or IP address.
Release 0.1.8 has no options for TLS beyond on and off:
- Custom certificate authorities. There is no CA file option. If your q servers use a private CA, add it to the operating system's trust store (the Windows certificate store, the macOS keychain, or the system CA bundle on Linux).
- Client certificates. xqdb presents no client certificate, so q servers that require mutual TLS reject it.
- Disabling verification. Certificate and hostname checks cannot be turned off.
Threads and concurrency
- Python. One
Qserves one thread at a time. xqdb releases the GIL during network I/O and decoding, so other threads keep running during a query. A second thread that calls the sameQwhile a call is in progress getsRuntimeError: Already borrowed. Give each thread its ownQ. - Node.js. Calls on one
Qrun in order on that connection's native worker thread and never block the event loop. AQaccepts one running command plus eight waiting commands, then rejects new calls withXQDB_BACKPRESSURE. Open severalQobjects to run requests in parallel. See Node.js Workers and Backpressure.
In both bindings, keep subscriptions on their own connection. sync() reads the next message on the socket, and on a subscribed connection that can be a pushed update instead of the reply. See Queries and Subscriptions.
