RocksDB-Sharp

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 errptr out parameter (a char** 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.

© 2026 RocksDB-Sharp. All rights reserved.