API Layers
RocksDB-Sharp is a multi-level binding. You can pick the abstraction that fits the call site and freely mix levels — most application code lives at the high level, with the occasional drop down to the mid or low level when you need something the high-level API doesn't surface.
| Layer | Types | Use it when |
|---|---|---|
| High level | RocksDb, WriteBatch, Iterator, Snapshot, Checkpoint, SstFileWriter, ColumnFamilies, … |
Default. Idiomatic, safe, handle-managed. |
| Mid level | Native.Instance.rocksdb_* wrappers in Native.Wrap.cs / Native.Marshaled.cs |
When you want marshalling and errptr translation but need a function the high level doesn't expose. |
| Low level | Native.cs P/Invoke declarations |
Maximum control — direct calls to the RocksDB C API. |
High level
The idiomatic surface. Handles are wrapped in IDisposable classes, options are fluent builders, and everything you'd expect from a modern .NET API is available.
using RocksDbSharp;
var options = new DbOptions().SetCreateIfMissing(true);
using var db = RocksDb.Open(options, path);
db.Put("k", "v");
string v = db.Get("k");
using var it = db.NewIterator();
for (it.SeekToFirst(); it.Valid(); it.Next()) { /* … */ }
This is the layer the rest of this documentation is written against.
Mid level
The mid-level lives on Native.Instance and provides typed C# methods that take care of marshalling and error reporting. Use it when the high-level class doesn't yet wrap the C function you need.
using RocksDbSharp;
// Mid-level call — handles errptr, returns string
string stats = Native.Instance.rocksdb_options_statistics_get_string_marshaled(options.Handle);
// Marshalled multi-get
var result = Native.Instance.rocksdb_multi_get(
db.Handle,
readOptionsHandle,
keys,
keyLengths,
cfHandles);
The replication test bench (see Replication) is a real-world example — it uses high-level RocksDb plus a few mid-level calls (rocksdb_flush, rocksdb_flushoptions_*) to flush on demand.
Low level
The raw P/Invoke surface. Every function in the C API is declared on the Native class (see Native.cs). At this layer you manage:
- Lifetime of every native handle (
rocksdb_*_create/rocksdb_*_destroy). - The
errptrout parameter (achar**for error strings). - Marshalling of byte buffers, lengths, and column family handles.
IntPtr errptr;
IntPtr handle = Native.Instance.rocksdb_open(options.Handle, path, out errptr);
if (errptr != IntPtr.Zero) { /* read + free */ }
You almost never need this layer directly — Native.Wrap.cs already wraps every C function in a friendlier signature. It exists so you can call any RocksDB function the high-level API hasn't caught up to yet.
Inspecting raw handles
Every high-level class exposes its underlying IntPtr Handle. You can pass these into mid- or low-level calls when mixing layers:
using var db = RocksDb.Open(options, path);
// Mix layers: use a low-level function with the high-level handle
ulong seq = Native.Instance.rocksdb_get_latest_sequence_number(db.Handle);
Don't double-dispose handles
If you take ownership of a handle at the low level you become responsible for rocksdb_*_destroy. The high-level classes destroy their handles in Dispose() — don't pass those handles to native destruction.
When to drop down a level
Common reasons:
- A brand-new RocksDB feature isn't yet wrapped at the high level.
- You need to set an option not exposed on
DbOptions/ColumnFamilyOptions. - You want zero-copy access via
Span<byte>on a code path that's missing a span overload. - You're profiling and want to skip a marshal step.
If you find yourself reaching for the low level often, consider filing an issue so the high-level API can be extended.