leveldb clib Storage / Key-Value

LevelDB embedded key-value store from Google. LSM-tree, sorted keys, snapshots, atomic write batches, Snappy compression.

ppm install leveldb

Overview

leveldb wraps Google's LevelDB — the original LSM-tree key-value store that later inspired RocksDB. It stores key-value pairs on disk, keeps keys in sorted order, and is optimized for write-heavy workloads. No SQL, no server, no configuration file — just open a path and start reading and writing.

LevelDB compresses data with Snappy by default, supports consistent read snapshots, and can apply many operations atomically through write batches. It is a foundational building block for databases, caches, and append-heavy storage systems.

CLib package

Requires libleveldb-dev. Ubuntu/Debian: sudo apt install libleveldb-dev

Key Characteristics

FeatureDetails
LSM-treeLog-Structured Merge-tree — writes go to a memory buffer and are flushed to disk in sorted runs. Optimized for write throughput over random reads.
Sorted keysIterator always traverses keys in lexicographic order. Prefix scans and range queries are efficient.
SnapshotsObtain a consistent point-in-time view of the database. Reads against a snapshot are isolated from concurrent writes.
Write batchesGroup multiple puts and deletes into one atomic operation. Either all changes appear or none do.
Snappy compressionEnabled by default. Reduces disk usage significantly for text-like values with minimal CPU overhead.
Bloom filterOptional per-key bloom filter. Set BloomFilterBits = 10 to dramatically reduce disk reads for keys that do not exist.
Single-process limitOnly one process may open a database at a time. LevelDB acquires an exclusive file lock on open.

Comparison

LibraryCompared to LevelDB
RocksDBMeta-maintained fork with column families, transactions, more tuning options. LevelDB is simpler, smaller codebase, sufficient for most use cases.
LMDBMemory-mapped B-tree. Much faster for random reads and concurrent readers. LevelDB wins for write throughput and smaller RAM footprint.
SQLiteFull SQL engine with transactions and indexes. LevelDB has no SQL, but is significantly faster for raw key-value operations and streaming writes.
Key and value size limits

Key size: recommended < 1 KB. Value size: recommended < 1 MB (larger values work but degrade compaction performance). Write batch: recommended < 4 MB per flush.

Quick Start

Open, write, read, close

uses leveldb;

{ Open (or create) a database at the given path }
var db := Open('/var/data/mydb');

Put(db, 'user:1001:name', 'Alice');
Put(db, 'user:1001:email', 'alice@example.com');

var name := Get(db, 'user:1001:name');
WriteLn('Name: ' + name);

Delete(db, 'user:1001:email');

Close(db);

Write batch (atomic multi-key update)

uses leveldb;

var db := Open('/var/data/mydb');
var b  := NewBatch;

BatchPut(b, 'account:42:balance', '1500');
BatchPut(b, 'account:42:updated', '2026-03-13');
BatchDelete(b, 'account:42:pending');

WriteBatch(db, b);  { all three ops are applied atomically }
FreeBatch(b);
Close(db);

Prefix-range iteration with snapshot

uses leveldb;

var db   := Open('/var/data/mydb');
var snap := NewSnapshot(db);

var ropts: TLevelReadOptions;
ropts.VerifyChecksums := False;
ropts.FillCache       := False;
ropts.Snapshot        := snap;  { read against frozen state }

var it := NewIteratorEx(db, ropts);
IterSeek(it, 'user:');  { jump to first key >= 'user:' }

while IterValid(it) do
begin
  var k := IterKey(it);
  if Copy(k, 1, 5) <> 'user:' then Break;
  WriteLn(k + ' = ' + IterValue(it));
  IterNext(it);
end;

FreeIterator(it);
FreeSnapshot(db, snap);
Close(db);

Types

type
  TLevelDB    = Integer;  { database handle returned by Open/OpenEx }
  TLevelIter  = Integer;  { iterator handle returned by NewIterator/NewIteratorEx }
  TLevelSnap  = Integer;  { snapshot handle returned by NewSnapshot }
  TLevelBatch = Integer;  { write batch handle returned by NewBatch }

  TLevelOptions = record
    CreateIfMissing   : Boolean;  { create DB if it doesn't exist }
    ErrorIfExists     : Boolean;  { fail if DB already exists }
    ParanoidChecks    : Boolean;  { verify checksums on every read }
    WriteBufferSize   : Integer;  { bytes, default 4 MB }
    MaxOpenFiles      : Integer;  { default 1000 }
    BlockSize         : Integer;  { bytes per block, default 4 KB }
    BlockCacheSize    : Integer;  { bytes, 0 = no LRU block cache }
    BloomFilterBits   : Integer;  { bits per key, 0 = disabled, 10 recommended }
    CompressionSnappy : Boolean;  { enable Snappy compression, default True }
  end;

  TLevelWriteOptions = record
    Sync : Boolean;  { fsync after write — slower but durable across crashes }
  end;

  TLevelReadOptions = record
    VerifyChecksums : Boolean;     { check block checksums on reads }
    FillCache       : Boolean;     { populate block cache from reads }
    Snapshot        : TLevelSnap;  { 0 = read current state }
  end;

Open / Close API

Open creates or opens a LevelDB database at the given filesystem path. Only one process may hold the database open at a time.

FunctionDescription
Open(Path)Open (or create) a database at Path. Uses default options: CreateIfMissing=True, Snappy on.
OpenEx(Path, Opts)Open with a TLevelOptions record for full control over cache, bloom filter, write buffer, etc.
Close(DB)Flush pending writes and release the file lock. Always call before the process exits.
Destroy(Path)Delete all database files at Path. The database must not be open.
Repair(Path)Attempt to recover a corrupted database. May lose recently written data.

Basic Read / Write API

All keys and values are strings. Binary data is supported — use any encoding you like; LevelDB treats keys and values as opaque byte sequences.

FunctionDescription
Put(DB, Key, Value)Write a key-value pair. Overwrites any existing value for that key.
PutEx(DB, Key, Value, Opts)Write with TLevelWriteOptions. Set Sync=True for durable writes that survive power loss.
Get(DB, Key)Read a value by key. Returns '' (empty string) if the key does not exist.
GetEx(DB, Key, Opts)Read with TLevelReadOptions. Pass a snapshot to read a frozen view of the database.
Exists(DB, Key)Returns True if the key exists. Faster than Get when you only need presence.
Delete(DB, Key)Remove a key. Does nothing if the key does not exist.

Write Batch API

A write batch groups multiple puts and deletes into one atomic operation applied in a single disk write. This is both safer (all-or-nothing) and faster (one fsync) than issuing individual writes.

FunctionDescription
NewBatchAllocate a new empty write batch. Returns a TLevelBatch handle.
FreeBatch(B)Release the batch and its memory. Call after WriteBatch.
BatchPut(B, Key, Value)Stage a put operation in the batch.
BatchDelete(B, Key)Stage a delete operation in the batch.
BatchClear(B)Discard all staged operations without freeing the batch handle (allows reuse).
WriteBatch(DB, B)Apply all staged operations atomically.
WriteBatchEx(DB, B, Opts)Apply with TLevelWriteOptions (e.g. Sync=True).

Iterator API

Iterators traverse keys in sorted lexicographic order. Because LevelDB stores keys sorted, a seek to a prefix followed by sequential IterNext calls is the canonical way to scan a range.

FunctionDescription
NewIterator(DB)Create an iterator over the current database state.
NewIteratorEx(DB, Opts)Create an iterator with read options. Pass a snapshot to freeze the view.
FreeIterator(It)Release the iterator. Must be called before FreeSnapshot if a snapshot was used.
IterSeekFirst(It)Move to the first (smallest) key in the database.
IterSeekLast(It)Move to the last (largest) key in the database.
IterSeek(It, Target)Move to the first key ≥ Target. Use for prefix scans.
IterNext(It)Advance to the next key in sorted order.
IterPrev(It)Move to the previous key (reverse scan).
IterValid(It)Returns True when the iterator points to a valid key. Check before calling IterKey/IterValue.
IterKey(It)Return the key at the current iterator position.
IterValue(It)Return the value at the current iterator position.

Snapshots API

A snapshot captures the state of the database at the moment it is created. All reads performed against a snapshot return consistent results regardless of concurrent writes, making snapshots ideal for backups and long-running scans.

FunctionDescription
NewSnapshot(DB)Create a consistent snapshot of the current database state. Returns a TLevelSnap handle.
FreeSnapshot(DB, Snap)Release the snapshot. LevelDB cannot compact data covered by live snapshots, so always free them when done.
Iterator + Snapshot pattern

Create the snapshot first, then create an iterator with NewIteratorEx passing the snapshot in TLevelReadOptions.Snapshot. Free the iterator before freeing the snapshot.

Maintenance API

These functions expose LevelDB's internal maintenance and diagnostic operations.

FunctionDescription
CompactRange(DB, StartKey, EndKey)Manually trigger compaction of the given key range. Pass '' for both arguments to compact the entire database. Useful after bulk deletes to reclaim disk space.
GetProperty(DB, Property)Read an internal diagnostic property. Useful values: 'leveldb.stats' (compaction stats), 'leveldb.num-files-at-level0', 'leveldb.approximate-memory-usage'.
ApproximateSize(DB, StartKey, EndKey)Return the approximate number of bytes of disk space used by keys in the range [StartKey, EndKey].
LevelDBVersionReturn the LevelDB library version string (e.g. '1.23').
uses leveldb;

var db := Open('/var/data/mydb');

{ Print internal stats }
WriteLn(GetProperty(db, 'leveldb.stats'));

{ Compact entire DB after a bulk delete pass }
CompactRange(db, '', '');

var sz := ApproximateSize(db, 'user:', 'user;');
WriteLn('User prefix ~' + IntToStr(sz) + ' bytes on disk');

Close(db);

Key Concepts

LSM-tree write path

Writes land first in an in-memory buffer (the MemTable). When full, the MemTable is flushed to a sorted on-disk file (an SSTable) at level 0. Background compaction threads merge and re-sort SSTables across levels, reclaiming space from deleted or overwritten keys. This design makes sequential writes very fast at the cost of read amplification on older data.

Key design for prefix scans

Because LevelDB keeps keys sorted, you can model relational-style access with structured key prefixes such as entity:id:field. A seek to 'user:1001:' followed by sequential IterNext calls will visit all fields of that user in one pass, reading from a contiguous range of the sorted file.

Sync writes

By default, Put and WriteBatch write to the OS page cache and may be lost if the process crashes before the OS flushes to disk. Set TLevelWriteOptions.Sync = True to call fsync after each write. This is roughly 100× slower but guarantees durability across power failures.

Package Info

Version1.0.0
Typeclib
C Librarylibleveldb-dev
Authorgustavo

System Requirements

libleveldb-dev

Ubuntu/Debian:
sudo apt install libleveldb-dev

TLevelOptions Fields

.CreateIfMissing — create if absent
.ErrorIfExists — fail if already exists
.ParanoidChecks — checksum all reads
.WriteBufferSize — MemTable size (bytes)
.MaxOpenFiles — open SSTable limit
.BlockSize — SSTable block size
.BlockCacheSize — LRU cache (0=off)
.BloomFilterBits — 0=off, 10=recommended
.CompressionSnappy — Snappy on/off

Functions

  • Open / OpenEx
  • Close
  • Destroy / Repair
  • Put / PutEx
  • Get / GetEx
  • Exists / Delete
  • NewBatch / FreeBatch
  • BatchPut / BatchDelete
  • BatchClear
  • WriteBatch / WriteBatchEx
  • NewIterator / NewIteratorEx
  • FreeIterator
  • IterSeekFirst / IterSeekLast
  • IterSeek
  • IterNext / IterPrev
  • IterValid
  • IterKey / IterValue
  • NewSnapshot / FreeSnapshot
  • CompactRange
  • GetProperty
  • ApproximateSize
  • LevelDBVersion

See Also

  • rocksdb (LevelDB fork, more features)
  • lmdb (memory-mapped, fast reads)
  • sqlite (SQL, full transactions)
  • duckdb (analytical SQL)