RocksDB-Sharp

Iterators

An iterator is an ordered cursor over a database (or a column family within one). RocksDB stores keys in sorted byte order, which makes iterators the natural primitive for range scans, prefix scans, and reverse scans.

This page covers the C# API. For the engine-level guarantees (snapshot semantics, prefix_extractor, total-order seek, tailing) read the upstream Iterator wiki and the Prefix Seek page.


Lifecycle

using var it = db.NewIterator();
it.SeekToFirst();
while (it.Valid())
{
    var k = it.StringKey();
    var v = it.StringValue();
    // …
    it.Next();
}

An iterator holds a native handle and a snapshot of the database state at construction time. Always Dispose it (or wrap it in using) — never leak iterators or you'll keep obsolete SST files alive on disk.


Seeking

Method Behaviour
SeekToFirst() Position at the smallest key.
SeekToLast() Position at the largest key.
Seek(key) First key >= key.
SeekForPrev(key) Last key <= key.
Next() Move forward one key.
Prev() Move backward one key.
Valid() Iterator is positioned on a valid entry.
using var it = db.NewIterator();
it.Seek("user:");          // first user
while (it.Valid() && it.StringKey().StartsWith("user:"))
{
    Console.WriteLine(it.StringValue());
    it.Next();
}

Reverse iteration

SeekToLast + Prev walks keys in descending order.

using var it = db.NewIterator();
for (it.SeekToLast(); it.Valid(); it.Prev())
{
    Console.WriteLine($"{it.StringKey()} = {it.StringValue()}");
}

Range scans with bounds

The most efficient way to scope an iterator is to set iterate_lower_bound and/or iterate_upper_bound on ReadOptions. RocksDB uses them to skip whole SST files at seek time.

var ro = new ReadOptions()
    .SetIterateLowerBound("user:")
    .SetIterateUpperBound("user;");   // ';' is one byte past ':'

using var it = db.NewIterator(readOptions: ro);
for (it.SeekToFirst(); it.Valid(); it.Next())
{
    // … only user:* keys
}

ReadOptions reference


Span access (zero allocation)

On net6+, GetKeySpan() / GetValueSpan() give you a ReadOnlySpan<byte> over the native buffer, with no allocation. The span is only valid until the next Seek / Next / Prev.

using var it = db.NewIterator();
for (it.SeekToFirst(); it.Valid(); it.Next())
{
    ReadOnlySpan<byte> key   = it.GetKeySpan();
    ReadOnlySpan<byte> value = it.GetValueSpan();

    if (key.SequenceEqual("counter"u8))
    {
        long counter = BitConverter.ToInt64(value);
        // …
    }
}

You can also pass an ISpanDeserializer<T> to it.Key<T>(...) and it.Value<T>(...).


Pinning the snapshot

By default an iterator sees the database state at the moment it was created — concurrent writes are invisible. To pin a specific older state, pass a ReadOptions with a Snapshot set:

using var snap = db.CreateSnapshot();
var ro = new ReadOptions().SetSnapshot(snap);
using var it = db.NewIterator(readOptions: ro);
// …

See the Snapshots guide for the full picture.


Iterating a column family

Pass the ColumnFamilyHandle:

using var it = db.NewIterator(cf: users);
for (it.SeekToFirst(); it.Valid(); it.Next()) { /* … */ }

Common pitfalls

Long-lived iterators block compaction

RocksDB cannot delete SST files that an open iterator might still need. Keep iterators short-lived in long-running systems, or you'll see disk usage and rocksdb.estimate-pending-compaction-bytes grow.

Total-order seek vs. prefix seek

If you've set a prefix_extractor on the column family, plain Seek only scans within a prefix bucket. To iterate across prefixes set ReadOptions.SetTotalOrderSeek(true). Read the Prefix Seek wiki page for the trade-offs.

Tailing iterators

ReadOptions.SetTailing(true) gives you an iterator that follows newly-written keys as long as it's valid. Useful for fan-out / log-consumer patterns. See the Iterator wiki.


Worked example: paged scan

public static IEnumerable<(string Key, string Value)> PagedScan(
    RocksDb db,
    string startKey,
    int pageSize)
{
    using var it = db.NewIterator();
    it.Seek(startKey);
    int n = 0;
    while (it.Valid() && n < pageSize)
    {
        yield return (it.StringKey(), it.StringValue());
        it.Next();
        n++;
    }
}

Referenced by

© 2026 RocksDB-Sharp. All rights reserved.