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:

data path
filesystem 560.0 → your operator 600.0 → cache 563.0 → indexer 564.0 → replication 561.0 → core 512.0

Because 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 toWriteOutside the tree?Guide
Observe, transform, filter or route data: auditing, encryption, deduplication, format conversion, custom cachingA chain operatorYesWrite a chain operator
Store data somewhere new: another device type, an object store, a remote serviceA block device module, attached as a tierYesExtend the storage system
Add a block device type selected by bdev_typeA transport in the block device moduleNoExtend the storage system
Change where data is placed or when it movesA placement policy or a data organizerNoExtend the storage system

Anatomy of a module

FileHolds
clio_mod.yamlThe module's name, namespace and method ids. The build reads the name and namespace to name the libraries.
*_methods.hThe method ids as constants, and their names for the dashboard.
*_tasks.hThe configuration struct, parsed from the module's compose entry, and the task types of the module's own methods.
*_runtime.h, *_runtime.ccThe Runtime class: one handler per method, plus CLIO_TASK_CC(...), which exports the module from the shared library.
*_exec.ccDispatch: routes each method id to its handler and handles serialization. Operators copy it from an existing operator.
*_client.h, *_client.ccThe 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.

Write the dispatch file by hand

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:

  1. The directory that holds libclio_run_cxx
  2. Each directory in CLIO_REPO_PATH (separated by :, or ; on Windows)
  3. Each directory in LD_LIBRARY_PATH
  4. ./lib, ../lib and /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:

output
LoadChiMod Loaded ChiMod: clio_demo_bytecount from /home/me/bytecount/build/libclio_demo_bytecount_runtime.so

Talking 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 Monitor method. The dashboard forwards GET /api/pools/<pool>/monitor?query=<text>&routing=local to the module's Monitor handler 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.