Develop
Develop custom data operators for your workload.
Every write in CLIO Core passes through a chain of data operators, and you can add your own. An operator is a module, built outside the CLIO Core tree, that the runtime loads at startup and that you place in the chain with a few lines of clio.yaml.
- Language
- C++20
- Builds against
- An installed CLIO Core
- Loaded
- At runtime start, from a shared library
What a data operator is
The runtime hosts modules, each in its own pool. Some modules are data operators: they receive the storage engine's calls, act on them, and pass them on. The built-in ones are the cache, the indexer, the compressor and replication (see Compose data operators). A module of your own works the same way:
filesystem 560.0 → your operator 600.0 → cache 563.0 → indexer 564.0 → replication 561.0 → core 512.0Because the operator speaks the storage engine's interface, everything above it works unchanged: the FUSE mount, the Python API and other operators. It sees every call that passes through, and handles only the ones it cares about. The rest go down the chain untouched.
Choose an extension point
| You want to | Write | Outside the tree? | Guide |
|---|---|---|---|
| Observe, transform, filter or route data: auditing, encryption, deduplication, format conversion, custom caching | A chain operator | Yes | Write a chain operator |
| Store data somewhere new: another device type, an object store, a remote service | A block device module, attached as a tier | Yes | Extend the storage system |
Add a block device type selected by bdev_type | A transport in the block device module | No | Extend the storage system |
| Change where data is placed or when it moves | A placement policy or a data organizer | No | Extend the storage system |
Anatomy of a module
| File | Holds |
|---|---|
clio_mod.yaml | The module's name, namespace and method ids. The build reads the name and namespace to name the libraries. |
*_methods.h | The method ids as constants, and their names for the dashboard. |
*_tasks.h | The configuration struct, parsed from the module's compose entry, and the task types of the module's own methods. |
*_runtime.h, *_runtime.cc | The Runtime class: one handler per method, plus CLIO_TASK_CC(...), which exports the module from the shared library. |
*_exec.cc | Dispatch: routes each method id to its handler and handles serialization. Operators copy it from an existing operator. |
*_client.h, *_client.cc | The client library that programs use to call the module. |
The build produces lib<namespace>_<module>_runtime.so and a client library. In clio.yaml, mod_name is the name the module reports, its chimod_lib_name.
clio_run refresh can generate dispatch files, but it currently writes include paths that do not match namespaced modules. Copy the dispatch file from an existing operator instead, as the guide does.
How the runtime finds your module
When clio_run start runs, the runtime scans for shared libraries whose names end in _runtime.so and that export the module entry points. It looks in this order:
- The directory that holds
libclio_run_cxx - Each directory in
CLIO_REPO_PATH(separated by:, or;on Windows) - Each directory in
LD_LIBRARY_PATH ./lib,../liband/usr/local/lib
Scanning happens once, at startup. Set CLIO_REPO_PATH before you start the runtime; after that, clio_run compose start can create pools of your module. The log confirms each module it loads:
LoadChiMod Loaded ChiMod: clio_demo_bytecount from /home/me/bytecount/build/libclio_demo_bytecount_runtime.soTalking to your operator
- Through the chain. Put the operator in the filesystem's path, as the guide does, and every write through the mount reaches it. Or bind any CTE client to it with
CLIO_CTE_POOL=600.0. - Its
Monitormethod. The dashboard forwardsGET /api/pools/<pool>/monitor?query=<text>&routing=localto the module'sMonitorhandler and decodes a msgpack reply to JSON. This is the quickest way to expose statistics. - Your own methods. Give them ids of 100 and above, so they never collide with the storage engine's, and call them from a client you write.
Next: write a chain operator, step by step.