Technologies · Storage
Context Transfer Engine: a key-value store over every tier.
Underneath every CLIO Core product is the Context Transfer Engine, a key-value store: tags (namespaces) hold blobs (values), and each blob is placed on a tier by its score. Use it directly when your pipeline thinks in objects, not files.
- Keys
- Tag + blob name
- Values
- Arbitrary bytes, partial put/get by offset
- Clients
- C++, Python
- Transport
- Shared memory, TCP, Unix sockets
Key takeaways
- A key-value store for C++ and Python: tags hold blobs, with partial reads and writes by offset.
- Each blob is placed on a tier by its score; changing the score moves it.
- Regex, keyword and time-window queries, and a shared-memory path for clients on the same node.
- The substrate every other technology writes to.
- 42.6kYCSB-B ops/s, against Redis 16.8k
- ~2xRedis on read-heavy YCSB workloads
- 15-16 GB/sasync GPU checkpoint into a RAM tier
- 2.0xthe HDF5 async VOL on that checkpoint
The data model
A tag is a named collection, a blob is a named value inside it. Puts and gets take an offset, so large values can be written and read in pieces. Each put carries a score from 0 to 1 that the placement policy uses to choose a tier, and the organizer can move a blob later with ReorganizeBlob. Queries match tag and blob names with regular expressions, and the default setup indexes what you write for keyword (BM25) search.
import clio_cte_core_ext as cte
cte.clio_init(cte.RuntimeMode.kClient, False)
cte.initialize_cte("", cte.PoolQuery.Dynamic())
tag = cte.Tag("experiments/run-42") # get or create the namespace
tag.PutBlob("params", b'{"dt": 0.01}') # put bytes (zero-copy in runtime mode)
size = tag.GetBlobSize("params")
print(tag.GetBlob("params", size, 0)) # get bytes back
print(tag.GetContainedBlobs()) # list keys in the tagThe same operations exist in C++ (clio::cte::core::Tag::PutBlob, GetBlob), with asynchronous variants such as AsyncPutBlob, and on the client for whole-store queries (TagQuery, BlobQuery, SemanticSearch). The Python API reference lists them all.
Performance against Redis, RocksDB and DynamoDB
The repository's YCSB binding (context-transfer-engine/benchmark/ycsb) runs the standard workloads against CLIO Core and three other stores. Throughput in operations per second, higher is better:
How to read it:
- CLIO Core's shared-memory read path is about twice as fast as Redis on the read-heavy workloads, B and C.
- The update-heavy workloads, A and F, are slower than Redis because the binding implements an update as a full-record read followed by a full-record rewrite, where Redis updates one hash field. A partial put at an offset is the planned fix.
- Embedded RocksDB has no inter-process hop at all and leads every workload. CLIO Core is a separate runtime process that many clients and nodes share.
- DynamoDB Local is the local emulator, not the AWS service. All records sit in one tag, so scale-out across tags is not exercised. These are single-node numbers on a development machine.
GPU checkpoints through the store
The kvhdf5 adapter writes HDF5-style datasets from GPU kernels as CTE blobs. Its scaling campaign checkpoints a Gray-Scott simulation on an RTX 4090 with local NVMe, comparing asynchronous blob puts against HDF5:
Absolute numbers are this machine's; the campaign notes that the ratios carry over but the bandwidths must be re-measured on your hardware.
Put and get bandwidth on a workstation
clio_cte_bench, put then get, 4 client threads with 8 requests in flight each, against a separate runtime with a 16 GB DRAM tier (cte_config_ram.yaml). Release build of dev (f88db54bf) on Windows: Ryzen 7 3800X, 32 GB RAM. The client reaches the runtime over loopback TCP, the transport Windows auto-selects.
One run each. Longer 64 KiB and 4 KiB runs (80,000 operations) stopped partway with put errors on this build, so the small-value rows use shorter runs.