kdb_codec - Kdb+ IPC Codec Library
Cancellation-safe Rust codec + client for the kdb+ IPC wire protocol (q/kdb+).
Docs: https://yshing.github.io/kdb_codec/
Key features
- Cancellation-safe message framing via
tokio-util::codec::Framed - IPC encode/decode, including kdb+ compression (
-18!/-19!) - Async client API (
QStream) and lower-level codec API (KdbCodec) - Multiple connection methods: TCP / TLS / Unix Domain Socket
- Ergonomic
Kvalue type for building/inspecting q objects
Quick start
use ;
use *;
use TcpStream;
use Framed;
async
Datatype coverage (IPC)
This project prioritizes safety when decoding untrusted IPC bytes (no panics, no OOM).
- Supported: basic atoms/lists (0–19), mixed lists, table (98), dictionary (99/127), null (101), error (-128)
- Not supported: enums (20–76), nested/other types (77+), function/derived types (100–112), foreign (112) let mut dict = k!(dict: k!(sym: vec!["x"]) => k!(long: vec![42])); dict[1] = k!(long: vec![100]); // Replace values
### Table Column Access
Access table columns by name using string indices:
```rust
use kdb_codec::*;
// Create a table
let table = k!(table: {
"fruit" => k!(sym: vec!["apple", "banana", "cherry"]),
"price" => k!(float: vec![1.5, 2.3, 3.8]),
"quantity" => k!(long: vec![100, 150, 75])
});
// Access columns by name
let fruits = &table["fruit"];
let prices = &table["price"];
let quantities = &table["quantity"];
println!("Fruits: {}", fruits); // `apple`banana`cherry
println!("Prices: {}", prices); // 1.5 2.3 3.8
println!("Quantities: {}", quantities); // 100 150 75
// Mutable access
let mut table = k!(table: {
"price" => k!(float: vec![1.5, 2.3])
});
table["price"] = k!(float: vec![2.0, 2.5]); // Update prices
Safe Access Methods
For production code, use the safe try_* methods that return Result instead of panicking:
use *;
let dict = k!;
// Safe dictionary access
match dict.try_index
// Try accessing out of bounds - won't panic
if dict.try_index.is_err
// Safe table column access
let table = k!;
match table.try_column
// Check if column exists before accessing
if table.try_column.is_err
Compound List Access
Access elements in compound (heterogeneous) lists:
use *;
let list = k!;
// Safe access to list elements
if let Ok = list.try_index
if let Ok = list.try_index
Benefits:
- ✅ Ergonomic
[]syntax familiar to Rust developers - ✅ Type-safe with compile-time borrow checking
- ✅ Both panicking (
[]) and safe (try_*) variants available - ✅ Works seamlessly with mutable access
- ✅ Supports dictionaries, tables, and compound lists
See examples/index_trait_demo.rs for more examples.
Connection Methods
As for connect method, usually client interfaces of q/kdb+ do not provide a listener due to its protocol. However, sometimes Rust process is connecting to an upstream and q/kdb+ starts afterward or is restarted more frequently. Then providing a listener method is a natural direction and it was achieved here. Following ways are supported to connect to kdb+:
- TCP
- TLS
- Unix domain socket
Furthermore, in order to improve inter-operatability some casting, getter and setter methods are provided.
Environmental Variables
This crate uses q-native or crate-specific environmental variables.
-
KDBPLUS_ACCOUNT_FILE: A file path to a credential file which an acceptor loads in order to manage access from a q client. This file contains a user name and SHA-1 hashed password in each line which are delimited by':'without any space. For example, a file containing two credentials"mattew:oracle"and"reluctant:slowday"looks like this:mattew:431364b6450fc47ccdbf6a2205dfdb1baeb79412 reluctant:d03f5cc1cdb11a77410ee34e26ca1102e67a893cThe hashed password can be generated with q using a function
.Q.sha1:q).Q.sha1 "slowday" 0xd03f5cc1cdb11a77410ee34e26ca1102e67a893c -
KDBPLUS_TLS_KEY_FILEandKDBPLUS_TLS_KEY_FILE_SECRET: The pkcs12 file and its password which TLS acceptor uses. -
QUDSPATH(optional): q-native environmental variable to define an astract namespace. This environmental variable is used by UDS acceptor too. The abstract nameapace will be@${QUDSPATH}/kx.[server process port]if this environmental variable is defined; otherwise it will be@/tmp/kx.[server process port].
Notes:
- Messages will be sent with OS native endian.
- When using this crate for a TLS client you need to set two environmental variables
KX_SSL_CERT_FILEandKX_SSL_KEY_FILEon q side to make q/kdb+ to work as a TLS server. For details, see the KX website.
Type Mapping
All types are expressed as K struct which is quite similar to the K struct of api module but its structure is optimized for IPC
usage and for the convenience to interact with. The table below shows the input types of each q type which is used to construct K object.
Note that the input type can be different from the inner type. For example, timestamp has an input type of chrono::DateTime<Utc> but
the inner type is i64 denoting an elapsed time in nanoseconds since 2000.01.01D00:00:00.
| q | Rust |
|---|---|
bool |
bool |
GUID |
[u8; 16] |
byte |
u8 |
short |
i16 |
int |
i32 |
long |
i64 |
real |
f32 |
float |
f64 |
char |
char |
symbol |
String |
timestamp |
chrono::DateTime<Utc> |
month |
chrono::NaiveDate |
date |
chrono::NaiveDate |
datetime |
chrono::DateTime<Utc> |
timespan |
chrono::Duration |
minute |
chrono::Duration |
second |
chrono::Duration |
time |
chrono::Duration |
list |
Vec<Item> (Item is a corrsponding type above) |
compound list |
Vec<K> |
table |
Vec<K> |
dictionary |
Vec<K> |
null |
() |
Examples
Client
use *;
async
Listener
use *;
async
Then q client can connect to this acceptor with the acceptor's host, port and the credential configured in KDBPLUS_ACCOUNT_FILE:
q)h:hopen `::7000:reluctant:slowday
Architecture & Design
Cancellation Safety
The core innovation of this library is its use of tokio-util::codec::Framed which provides automatic buffer management:
- Buffer Preservation: Partial reads are stored in the codec's internal buffer
- Resumable Operations: Cancelled reads can be safely retried without data loss
- No Manual State Management: The Framed wrapper handles all buffer lifecycle
This is critical for production systems using patterns like:
tokio::select!for timeouts or concurrent operations- Graceful shutdown with cancellation
- Request racing or fallback logic
Synchronous Deserialization
Unlike the original kdbplus crate, we use synchronous deserialization without async-recursion:
- Simpler: No async recursion complexity
- Faster: Eliminates async overhead for CPU-bound deserialization
- Smaller: No
async-recursionproc-macro dependency - Safer: Avoids potential stack overflow from deep async recursion
The deserialization happens in deserialize_sync.rs and is called from the codec's decode() method after the complete message is buffered.
Why Not Add split() to QStream?
While we show how to split the underlying Framed stream in examples, we don't recommend adding a split() method directly to QStream because:
-
Protocol Semantics: KDB+ IPC is request-response oriented. Splitting would allow sending multiple requests before receiving responses, which can confuse message correlation.
-
Complexity: Users would need to manually track which response corresponds to which request.
-
Better Alternatives: For concurrent operations, use multiple
QStreaminstances or the lower-levelFramedAPI directly when you need full control.
If you need independent send/receive channels, access the underlying stream:
let stream = connect.await?;
let framed = new;
let = framed.split;
// Now you have full control
Installation
Add kdb_codec to your Cargo.toml:
[]
= "0.4"
The IPC feature is enabled by default.
Testing
Unit Tests
Run the standard unit tests (no kdb+ server required):
Integration Tests
Some tests require a running kdb+ server and are marked as #[ignore] by default. To run these tests:
-
Start a kdb+ server on
localhost:5001with credentialskdbuser:pass: -
Run the ignored tests:
The integration tests include:
functional_message_test: Tests various message types and operationscompression_test: Tests compression functionality with large data
Note: These tests are automatically skipped in CI/CD unless a kdb+ server is explicitly configured.
Documentation
The full API documentation is available on docs.rs/kdb_codec.
For details of the kdb+ IPC protocol, see:
License
This library is licensed under Apache-2.0.
See LICENSE.