RocksDB¶
The RocksDB layer provides the shared storage backend used by the
root-local .dftindex and provenance stores introduced by the
RocksDB migration.
It includes:
database wrappers and lifecycle management
column-family and merge-operator registration for the
.dftindexschemakey encoding helpers for typed prefix/range scans
manager utilities for sharing open database handles across readers, indexers, and higher-level composites
bulk-ingest helpers (
SstFileWriter+IngestExternalFile) used by the distributed indexing pipeline
Architecture¶
graph TD
Readers["TraceReader / utilities"] --> Manager["RocksDBManager"]
Indexers["Indexer / provenance writers"] --> Manager
Manager --> Database["RocksDatabase"]
Database --> CFs["Column families"]
Database --> Codec["KeyCodec"]
Database --> Merge["MergeOperators (AGGREGATION, SYSTEM_METRICS)"]
CFs --> Store[".dftindex / provenance store"]
Codec --> Store
All types live in the dftracer::utils::rocksdb namespace; the column
family constants live in dftracer::utils::rocksdb::cf.
RocksDatabase¶
What: a thin RAII wrapper around a rocksdb::DB opened with the
.dftindex column families and DFTracer-specific Env / FileSystem.
When: perform typed point reads/writes, range deletes, merges, batched
writes, iteration, and bulk SST ingest against one store. Move-only (copy
deleted). Every key/value/column-family argument is a std::string_view;
the column family defaults to cf::DEFAULT. Operations return a
rocksdb::Status - check ok().
Prefer opening through RocksDBManager so a single handle is shared process-wide rather than reopening the same path.
Key signatures:
explicit RocksDatabase(const std::string& db_path,
OpenMode open_mode = OpenMode::ReadWrite);
bool open(const std::string& db_path, OpenMode open_mode = OpenMode::ReadWrite);
void close();
bool is_open() const noexcept;
::rocksdb::Status put(std::string_view key, std::string_view value,
std::string_view column_family = cf::DEFAULT);
::rocksdb::Status get(std::string_view key, std::string* value,
std::string_view column_family = cf::DEFAULT) const;
::rocksdb::Status del(std::string_view key,
std::string_view column_family = cf::DEFAULT);
::rocksdb::Status merge(std::string_view key, std::string_view value,
std::string_view column_family = cf::DEFAULT);
Batch begin_batch() const; // Batch = rocksdb::WriteBatch
::rocksdb::Status commit_batch(Batch& batch);
std::unique_ptr<::rocksdb::Iterator> new_iterator(
std::string_view column_family = cf::DEFAULT) const;
::rocksdb::Status ingest_external_files(
std::string_view column_family,
const std::vector<std::string>& external_files, bool ingest_behind = false);
Point read/write:
#include <dftracer/utils/core/rocksdb/database.h>
using namespace dftracer::utils::rocksdb;
RocksDatabase db("/path/to/.dftindex");
db.put("hello", "world", cf::METADATA);
std::string value;
if (db.get("hello", &value, cf::METADATA).ok()) {
use(value);
}
Batched writes (atomic commit):
RocksDatabase::Batch batch = db.begin_batch();
db.put(batch, cf::METADATA, "a", "1");
db.put(batch, cf::METADATA, "b", "2");
db.commit_batch(batch);
Prefix scan with an iterator:
auto it = db.new_iterator(cf::CHUNK_STATS);
for (it->Seek("prefix"); it->Valid(); it->Next()) {
if (!it->key().starts_with("prefix")) break;
consume(it->key(), it->value());
}
RocksDBManager¶
What: a process-wide singleton registry of open databases keyed by their
normalized .dftindex root path. Why: RocksDB allows only one writer
per path, and reopening is expensive; the manager owns one live instance per
path so short-lived wrappers (index reader, provenance writer, Python
bindings) share it. When: any time you need a handle - always go through
get_or_open() instead of constructing RocksDatabase directly.
Key signatures:
static RocksDBManager& instance();
std::shared_ptr<RocksDatabase> get_or_open(
const std::string& db_path,
RocksDatabase::OpenMode open_mode = RocksDatabase::OpenMode::ReadWrite,
RocksDatabase::CfOptionsOverride cf_override = nullptr);
void reset(const std::string& db_path);
void shutdown();
Example:
#include <dftracer/utils/core/rocksdb/db_manager.h>
using namespace dftracer::utils::rocksdb;
std::shared_ptr<RocksDatabase> db =
RocksDBManager::instance().get_or_open("/path/to/.dftindex");
db->put("k", "v", cf::METADATA);
The optional cf_override is a
std::function<void(const std::string&, rocksdb::ColumnFamilyOptions&)>
used to attach per-column-family merge operators (for example the
AGGREGATION and SYSTEM_METRICS families) at open time.
Column families¶
What: cf holds the fixed set of column-family names for the
.dftindex schema as constexpr std::string_view constants, plus
cf::ALL (a std::array of every name, used to open the full set).
When: pass one of these constants as the column_family argument to any
RocksDatabase call. Names include cf::DEFAULT, cf::METADATA,
cf::CHUNK_STATS, cf::CHUNK_BLOOM / cf::FILE_BLOOM,
cf::MANIFEST, cf::PROVENANCE, cf::AGGREGATION, and
cf::SYSTEM_METRICS (see the header for the complete list).
#include <dftracer/utils/core/rocksdb/column_families.h>
using namespace dftracer::utils::rocksdb;
db.merge("stats-key", encoded, cf::AGGREGATION);
KeyCodec and KeyBuilder¶
What: helpers for building the byte keys used across the schema.
KeyCodec encodes/decodes fixed-width big-endian integers (BE ordering
keeps numeric keys lexicographically sortable, so typed prefix/range scans
work). KeyBuilder assembles a composite key from a tag, separators,
strings, and BE integers via a fluent API. When: construct or parse keys
for typed prefix/range scans instead of hand-formatting strings.
Key signatures:
// KeyCodec (all static)
static std::string encode_be32(std::uint32_t value);
static std::string encode_be64(std::uint64_t value);
static std::uint32_t decode_be32(std::string_view bytes);
static std::uint64_t decode_be64(std::string_view bytes);
static void append_be32(std::string& out, std::uint32_t value);
static void append_be64(std::string& out, std::uint64_t value);
// KeyBuilder (fluent; returns *this)
KeyBuilder& append_tag(std::string_view tag);
KeyBuilder& append_separator();
KeyBuilder& append_string(std::string_view value);
KeyBuilder& append_be32(std::uint32_t value);
KeyBuilder& append_be64(std::uint64_t value);
std::string build() const;
void clear();
Example (composite key chunk:<file_id BE32>:<offset BE64>):
#include <dftracer/utils/core/rocksdb/key_codec.h>
using namespace dftracer::utils::rocksdb;
std::string key = KeyBuilder()
.append_tag("chunk")
.append_separator()
.append_be32(file_id)
.append_separator()
.append_be64(offset)
.build();
db.put(key, value, cf::CHUNK_STATS);
// Decode a raw BE64 key back to an integer:
std::uint64_t n = KeyCodec::decode_be64(raw_bytes);
filesystem¶
What: factory helpers make_dftracer_file_system() and
make_dftracer_env() that build the RocksDB FileSystem / Env used
by RocksDatabase. When: rarely called directly - RocksDatabase
constructs these internally. Reach for them only to customize the I/O
backend before opening a database.
std::shared_ptr<::rocksdb::FileSystem> make_dftracer_file_system();
std::unique_ptr<::rocksdb::Env> make_dftracer_env(
const std::shared_ptr<::rocksdb::FileSystem>& file_system);
See also: