# LevelDB User Guide

LevelDB is a fast, embeddable key-value storage library written at Google. It provides an **ordered** mapping from arbitrary string (byte-array) keys to arbitrary string values, persisted to the local filesystem. You link it directly into your C++ or C application — there is no server, no daemon, and no network protocol.

This guide is written for **developers who embed LevelDB in their own programs**. It covers building and installing the library, every public operation with copy-paste-ready examples, performance tuning, the bundled `leveldbutil` command-line tool, troubleshooting, and a reference of every option and status code.

> [!NOTE]
> LevelDB is the storage engine that inspired RocksDB and underpins many systems. It is intentionally small and focused: keys/values in, keys/values out, sorted by key. If you need SQL, relations, or a network server, LevelDB is a building block, not a finished product — see [Limitations](#limitations).

---

## Table of Contents

1. [Quick Start](#1-quick-start)
2. [Installation](#2-installation)
3. [Core Concepts](#3-core-concepts)
4. [Everyday Usage](#4-everyday-usage)
5. [Iteration](#5-iteration)
6. [Atomic Updates with WriteBatch](#6-atomic-updates-with-writebatch)
7. [Snapshots](#7-snapshots)
8. [Advanced Usage](#8-advanced-usage)
9. [Performance Tuning](#9-performance-tuning)
10. [Using the C API](#10-using-the-c-api)
11. [The `leveldbutil` Command-Line Tool](#11-the-leveldbutil-command-line-tool)
12. [Troubleshooting](#12-troubleshooting)
13. [Frequently Asked Questions](#13-frequently-asked-questions)
14. [Reference](#14-reference)

---

## 1. Quick Start

The fastest path from zero to a working key-value store. The following program opens (creating if needed) a database, writes a value, reads it back, and closes cleanly.

```cpp
#include <cassert>
#include <iostream>
#include <string>

#include "leveldb/db.h"

int main() {
  leveldb::DB* db;
  leveldb::Options options;
  options.create_if_missing = true;

  // Open (or create) the database stored in the directory /tmp/testdb.
  leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db);
  assert(status.ok());

  // Write a key/value pair.
  status = db->Put(leveldb::WriteOptions(), "name", "leveldb");
  assert(status.ok());

  // Read it back.
  std::string value;
  status = db->Get(leveldb::ReadOptions(), "name", &value);
  if (status.ok()) {
    std::cout << "name = " << value << std::endl;
  }

  // Close the database by deleting the handle.
  delete db;
  return 0;
}
```

Compile and run (assuming LevelDB is installed — see [Installation](#2-installation)):

```bash
c++ -std=c++17 quickstart.cc -lleveldb -o quickstart
./quickstart
```

```text
name = leveldb
```

> [!IMPORTANT]
> A LevelDB "database" is a **directory** on disk, not a single file. The name you pass to `DB::Open` (`/tmp/testdb` above) is the path to that directory. LevelDB owns every file inside it.

> [!TIP]
> Always check the returned `leveldb::Status`. Almost every operation that can fail returns one. The idiom `if (!status.ok()) { ... }` should appear throughout your code — see [Core Concepts](#3-core-concepts).

---

## 2. Installation

LevelDB is distributed as source and built with [CMake](https://cmake.org/). It has no required third-party dependencies; compression and CRC acceleration libraries are detected and linked automatically if present.

### 2.1 Requirements

| Requirement | Detail |
|---|---|
| C++ compiler | **C++17** required (`CMAKE_CXX_STANDARD 17`) |
| CMake | **3.22 or newer** (`cmake_minimum_required(VERSION 3.22)`) |
| Build threads | POSIX threads (`Threads::Threads`) — found automatically |
| Platforms | POSIX (Linux, macOS) and Windows are the tested, supported platforms |

Optional libraries, auto-detected at configure time, that improve LevelDB if installed:

| Library | Effect when present |
|---|---|
| `snappy` | Enables Snappy block compression (the default compression) |
| `zstd` | Enables Zstandard block compression (`kZstdCompression`) |
| `crc32c` | Hardware-accelerated CRC32C checksums |
| `tcmalloc` | Faster memory allocation |

> [!NOTE]
> If `snappy` is not installed, LevelDB still builds and runs — blocks are simply stored uncompressed. The `Options::compression` default of `kSnappyCompression` becomes a no-op at runtime when Snappy support is absent.

### 2.2 Getting the source

The repository uses git submodules (GoogleTest and Google Benchmark, used only for tests and benchmarks). Clone recursively:

```bash
git clone --recurse-submodules https://github.com/google/leveldb.git
cd leveldb
```

### 2.3 Building

=== "Linux / macOS"

    ```bash
    mkdir -p build && cd build
    cmake -DCMAKE_BUILD_TYPE=Release .. && cmake --build .
    ```

=== "Windows (Visual Studio)"

    ```cmd
    mkdir build
    cd build
    cmake -G "Visual Studio 15" ..
    ```

    For a 64-bit build, generate with the Win64 generator instead:

    ```cmd
    cmake -G "Visual Studio 15 Win64" ..
    ```

    Then build from the command line or open `leveldb.sln` in Visual Studio:

    ```cmd
    devenv /build Debug leveldb.sln
    ```

A successful build produces the static library (`libleveldb.a` / `leveldb.lib`) plus the `leveldbutil` tool. Expected tail of output:

```text
[100%] Built target leveldb
[100%] Built target leveldbutil
```

### 2.4 Build options

These CMake cache options control what gets built. Pass them as `-D<OPTION>=<VALUE>` to `cmake`.

| Option | Default | Purpose |
|---|---|---|
| `LEVELDB_BUILD_TESTS` | `ON` | Build the unit-test suite |
| `LEVELDB_BUILD_BENCHMARKS` | `ON` | Build the `db_bench` benchmark programs |
| `LEVELDB_INSTALL` | `ON` | Generate install rules for the headers + library |
| `BUILD_SHARED_LIBS` | `OFF` | Build a shared library instead of a static one |

Example: build a release shared library, no tests or benchmarks:

```bash
cmake -DCMAKE_BUILD_TYPE=Release \
      -DBUILD_SHARED_LIBS=ON \
      -DLEVELDB_BUILD_TESTS=OFF \
      -DLEVELDB_BUILD_BENCHMARKS=OFF ..
cmake --build .
```

> [!TIP]
> Disabling `LEVELDB_BUILD_TESTS` and `LEVELDB_BUILD_BENCHMARKS` removes the need for the GoogleTest and Benchmark submodules and noticeably speeds up the build when you only need the library itself.

### 2.5 Installing

With `LEVELDB_INSTALL=ON` (the default), install the headers, library, and CMake package config:

```bash
cmake --build . --target install
```

This installs:

- The library to your platform's library directory.
- The public headers to `<prefix>/include/leveldb/`.
- A CMake package (`leveldbConfig.cmake`) so downstream projects can `find_package(leveldb)`.

### 2.6 Using LevelDB from your CMake project

Once installed, link against the imported target `leveldb::leveldb`:

```cmake
find_package(leveldb REQUIRED)

add_executable(myapp main.cc)
target_link_libraries(myapp PRIVATE leveldb::leveldb)
```

> [!CAUTION]
> Include **only** the public headers under `include/leveldb/`. Headers elsewhere in the source tree (e.g. `db/`, `table/`, `util/`) are internal and may change without warning between releases.

---

## 3. Core Concepts

A handful of small types appear throughout the API. Understanding them up front makes every later example obvious.

### 3.1 `DB` — the database handle

`leveldb::DB` is an abstract class. You obtain a concrete instance from the static `DB::Open` factory and you destroy it with `delete`. A `DB` is **safe for concurrent use by multiple threads** with no external locking.

### 3.2 `Slice` — a pointer + length

`leveldb::Slice` is a lightweight, non-owning view over a byte array: just a pointer and a size. It is how LevelDB avoids copying potentially large keys and values. Because LevelDB keys and values are arbitrary byte arrays (they may contain `'\0'`), the API uses `Slice` rather than C strings.

A `Slice` is constructible from both `std::string` and C strings, and convertible back:

```cpp
leveldb::Slice s1 = "hello";          // from a C string
std::string str("world");
leveldb::Slice s2 = str;              // from a std::string
std::string back = s1.ToString();     // back to std::string
```

> [!WARNING]
> A `Slice` does **not** own its data. The caller must keep the backing byte array alive for as long as the `Slice` is used. This is buggy:
>
> ```cpp
> leveldb::Slice slice;
> if (cond) {
>   std::string str = ComputeKey();
>   slice = str;        // slice points into str
> }                     // str is destroyed here
> Use(slice);           // slice now dangles — undefined behavior
> ```

### 3.3 `Status` — success or error

`leveldb::Status` is the universal return type for fallible operations. Check `ok()`, and classify errors with the `Is*` predicates.

```cpp
leveldb::Status s = db->Get(leveldb::ReadOptions(), "missing", &value);
if (s.ok()) {
  // found
} else if (s.IsNotFound()) {
  // key does not exist
} else {
  std::cerr << s.ToString() << std::endl;   // some other error
}
```

The error categories are: `IsNotFound()`, `IsCorruption()`, `IsIOError()`, `IsNotSupportedError()`, and `IsInvalidArgument()`. See the [Status reference](#144-status-codes).

### 3.4 `Options`, `ReadOptions`, `WriteOptions`

Behavior is configured with three plain structs:

- **`Options`** — passed once to `DB::Open`; controls the whole database (cache, compression, comparator, etc.).
- **`ReadOptions`** — passed per read; controls checksum verification, caching, and snapshots.
- **`WriteOptions`** — passed per write; controls durability (the `sync` flag).

All three have sensible defaults; you only set the fields you care about. Full tables are in the [Reference](#141-options-fields).

---

## 4. Everyday Usage

The three basic operations are `Put`, `Get`, and `Delete`. All keys and values are `Slice`s.

### 4.1 Writing a value — `Put`

```cpp
leveldb::Status s = db->Put(leveldb::WriteOptions(), key, value);
```

`Put` inserts or overwrites the entry for `key`.

### 4.2 Reading a value — `Get`

```cpp
std::string value;
leveldb::Status s = db->Get(leveldb::ReadOptions(), key, &value);
if (s.ok()) {
  // value now holds the stored bytes
} else if (s.IsNotFound()) {
  // no such key
}
```

### 4.3 Deleting a value — `Delete`

```cpp
leveldb::Status s = db->Delete(leveldb::WriteOptions(), key);
```

It is **not** an error to delete a key that does not exist; `Delete` still returns OK.

### 4.4 A common pattern: move a value between keys

```cpp
std::string value;
leveldb::Status s = db->Get(leveldb::ReadOptions(), key1, &value);
if (s.ok()) s = db->Put(leveldb::WriteOptions(), key2, value);
if (s.ok()) s = db->Delete(leveldb::WriteOptions(), key1);
```

> [!WARNING]
> The code above is **not atomic**. If the process dies after the `Put` of `key2` but before the `Delete` of `key1`, the value is left under both keys. For atomicity, use a [`WriteBatch`](#6-atomic-updates-with-writebatch).

### 4.5 Closing the database

There is no `Close()` method. You close a database by deleting its handle:

```cpp
delete db;
```

> [!IMPORTANT]
> Delete all iterators, snapshots, and other handles obtained from the `DB` **before** you delete the `DB` itself.

---

## 5. Iteration

A LevelDB iterator yields key/value pairs in sorted key order. Get one with `NewIterator`, and always delete it when done.

### 5.1 Forward iteration over the whole database

```cpp
leveldb::Iterator* it = db->NewIterator(leveldb::ReadOptions());
for (it->SeekToFirst(); it->Valid(); it->Next()) {
  std::cout << it->key().ToString() << ": " << it->value().ToString() << "\n";
}
assert(it->status().ok());   // check for errors found during the scan
delete it;
```

### 5.2 Iterating a key range `[start, limit)`

```cpp
for (it->Seek(start);
     it->Valid() && it->key().ToString() < limit;
     it->Next()) {
  // process it->key(), it->value()
}
```

### 5.3 Reverse iteration

```cpp
for (it->SeekToLast(); it->Valid(); it->Prev()) {
  // process entries from largest key to smallest
}
```

> [!NOTE]
> Reverse iteration may be somewhat slower than forward iteration, by design.

> [!CAUTION]
> The `Slice`s returned by `it->key()` and `it->value()` are only valid until the next mutation of the iterator. Copy them with `ToString()` if you need to keep them.

The full iterator interface — `Valid()`, `SeekToFirst()`, `SeekToLast()`, `Seek()`, `Next()`, `Prev()`, `key()`, `value()`, `status()` — is in the [Reference](#146-iterator-methods).

---

## 6. Atomic Updates with `WriteBatch`

A `WriteBatch` collects a sequence of `Put` and `Delete` operations and applies them to the database **atomically** — all or nothing.

```cpp
#include "leveldb/write_batch.h"

std::string value;
leveldb::Status s = db->Get(leveldb::ReadOptions(), key1, &value);
if (s.ok()) {
  leveldb::WriteBatch batch;
  batch.Delete(key1);
  batch.Put(key2, value);
  s = db->Write(leveldb::WriteOptions(), &batch);
}
```

Operations within a batch are applied **in order**. In the example, `Delete(key1)` is ordered before `Put(key2)` so that even if `key1 == key2`, the value is preserved rather than dropped.

`WriteBatch` has two benefits:

1. **Atomicity** — partial application can never be observed, even across a crash.
2. **Throughput** — bundling many mutations into one batch is faster than issuing them individually, because the cost of the write (and an optional sync) is amortized.

```cpp
leveldb::WriteBatch batch;
for (const auto& [k, v] : pending) {
  batch.Put(k, v);
}
db->Write(leveldb::WriteOptions(), &batch);
```

> [!TIP]
> To make a bulk update both atomic **and** durable, set `write_options.sync = true` on the single `Write` call. The sync cost is paid once for the whole batch.

---

## 7. Snapshots

A **snapshot** is a consistent, read-only view of the entire database at a point in time. Reads against a snapshot ignore any writes made after the snapshot was taken.

```cpp
leveldb::ReadOptions options;
options.snapshot = db->GetSnapshot();

// ... apply some updates to db ...

leveldb::Iterator* iter = db->NewIterator(options);
// ... iter sees the DB exactly as it was when the snapshot was created ...
delete iter;

db->ReleaseSnapshot(options.snapshot);
```

If `ReadOptions::snapshot` is `nullptr` (the default), each read operates on an implicit snapshot of the state at the moment the read begins.

> [!IMPORTANT]
> Always call `DB::ReleaseSnapshot` when you are finished with a snapshot. Holding a snapshot prevents LevelDB from discarding superseded data, so a long-lived snapshot increases disk usage.

---

## 8. Advanced Usage

### 8.1 Custom comparators

By default, keys are ordered lexicographically by byte value (`BytewiseComparator`). To impose a different order, subclass `leveldb::Comparator` and pass it via `Options::comparator`.

```cpp
class TwoPartComparator : public leveldb::Comparator {
 public:
  // Sort by first number, break ties by second number.
  int Compare(const leveldb::Slice& a, const leveldb::Slice& b) const override {
    int a1, a2, b1, b2;
    ParseKey(a, &a1, &a2);
    ParseKey(b, &b1, &b2);
    if (a1 != b1) return a1 < b1 ? -1 : +1;
    if (a2 != b2) return a2 < b2 ? -1 : +1;
    return 0;
  }
  const char* Name() const override { return "TwoPartComparator"; }
  void FindShortestSeparator(std::string*, const leveldb::Slice&) const override {}
  void FindShortSuccessor(std::string*) const override {}
};
```

```cpp
TwoPartComparator cmp;
leveldb::Options options;
options.create_if_missing = true;
options.comparator = &cmp;
leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db);
```

> [!CAUTION]
> The comparator's `Name()` is recorded when the database is created and verified on every open. **Changing the name makes existing databases unopenable.** Change the name only when the new ordering is genuinely incompatible with old data. To evolve a key format safely, keep the name and embed a version byte in your keys.

### 8.2 Bloom filters

A `FilterPolicy` reduces the number of disk reads per `Get`. The built-in Bloom filter is the common choice.

```cpp
#include "leveldb/filter_policy.h"

leveldb::Options options;
options.filter_policy = leveldb::NewBloomFilterPolicy(10);  // 10 bits per key
leveldb::DB* db;
leveldb::DB::Open(options, "/tmp/testdb", &db);
// ... use the database ...
delete db;
delete options.filter_policy;   // delete AFTER the db is closed
```

`NewBloomFilterPolicy(10)` keeps ~10 bits per key in memory and cuts unnecessary disk reads for point lookups by roughly 100×. More bits per key = fewer disk reads, at the cost of more memory.

> [!TIP]
> Set a filter policy if your working set does not fit in memory and you do many random reads. It is one of the highest-impact tuning knobs.

> [!WARNING]
> If you use a custom comparator that ignores parts of keys (e.g. trailing spaces), you must **not** use `NewBloomFilterPolicy` directly — supply a matching custom filter policy that ignores the same parts.

### 8.3 Custom environments (`Env`)

Every filesystem and OS interaction goes through a `leveldb::Env`. The default, `Env::Default()`, is suitable for normal use. Advanced clients can subclass `Env` (or `EnvWrapper`, which forwards all calls to another `Env`) to intercept I/O — for rate limiting, fault injection, or in-memory storage.

```cpp
class SlowEnv : public leveldb::EnvWrapper {
 public:
  explicit SlowEnv(leveldb::Env* base) : leveldb::EnvWrapper(base) {}
  // override only the methods you want to change
};

SlowEnv env(leveldb::Env::Default());
leveldb::Options options;
options.env = &env;
leveldb::DB::Open(options, "/tmp/testdb", &db);
```

> [!NOTE]
> The library ships an in-memory `Env` helper (`helpers/memenv`) that backs a database entirely in RAM — useful for tests. It is not part of the stable public interface.

### 8.4 Estimating storage size

`GetApproximateSizes` returns the approximate on-disk bytes used by one or more key ranges.

```cpp
leveldb::Range ranges[2];
ranges[0] = leveldb::Range("a", "c");
ranges[1] = leveldb::Range("x", "z");
uint64_t sizes[2];
db->GetApproximateSizes(ranges, 2, sizes);
// sizes[0] = approx bytes for [a, c); sizes[1] = approx bytes for [x, z)
```

Because the sizes reflect *compressed* file-system space, they will be smaller than the raw user-data size when compression is effective.

### 8.5 Querying database properties

`GetProperty` exposes internal statistics as human-readable strings.

```cpp
std::string stats;
if (db->GetProperty("leveldb.stats", &stats)) {
  std::cout << stats << std::endl;
}
```

| Property | Returns |
|---|---|
| `leveldb.num-files-at-level<N>` | Number of files at level `<N>` (e.g. `leveldb.num-files-at-level0`) |
| `leveldb.stats` | Multi-line table of per-level statistics |
| `leveldb.sstables` | Multi-line description of all SSTables in the DB |
| `leveldb.approximate-memory-usage` | Approximate bytes of memory in use by the DB |

`GetProperty` returns `false` for an unknown property name.

### 8.6 Manual compaction

LevelDB compacts in the background automatically. You rarely need to trigger it, but `CompactRange` forces compaction of a key range, discarding deleted/overwritten versions and reorganizing data for faster access.

```cpp
db->CompactRange(nullptr, nullptr);   // compact the entire database
```

`begin == nullptr` means "before all keys"; `end == nullptr` means "after all keys."

> [!NOTE]
> Manual compaction is an advanced operation intended for callers who understand the storage layout. Most applications should rely on automatic background compaction.

### 8.7 Destroying and repairing a database

These free functions operate on a closed database directory:

```cpp
#include "leveldb/db.h"

leveldb::Status s = leveldb::DestroyDB("/tmp/testdb", leveldb::Options());
```

```cpp
leveldb::Status s = leveldb::RepairDB("/tmp/testdb", leveldb::Options());
```

`DestroyDB` deletes the database's contents — use with care. `RepairDB` attempts to recover as much data as possible from a database that will not open; **some data may be lost**, so only use it on a database you cannot otherwise open.

---

## 9. Performance Tuning

LevelDB's defaults are reasonable, but several `Options` fields meaningfully affect throughput and latency.

### 9.1 Synchronous vs. asynchronous writes

By default writes are **asynchronous**: `Put`/`Write` return after handing the data to the operating system, before it reaches persistent storage.

```cpp
leveldb::WriteOptions write_options;
write_options.sync = true;       // force the write all the way to disk
db->Put(write_options, key, value);
```

| Mode | Speed | Crash behavior |
|---|---|---|
| `sync = false` (default) | Often >1000× faster | A machine reboot/power loss may lose the last few updates. A *process* crash loses nothing. |
| `sync = true` | Much slower (one disk flush per write) | Data is durable once the call returns. |

> [!TIP]
> Asynchronous writes are safe for many workloads. For bulk loads, use `sync = false` and restart the load from a checkpoint after a crash. Or batch many updates into one `WriteBatch` and issue a single synchronous `Write` to amortize the flush cost.

### 9.2 Block size

```cpp
options.block_size = 4 * 1024;   // default ~4 KB (uncompressed)
```

- Increase for workloads dominated by **bulk scans**.
- Consider decreasing for workloads dominated by **point reads of small values** (measure first).
- There's little benefit below ~1 KB or above a few MB. Larger blocks compress better.

### 9.3 Compression

Block compression is on by default (`kSnappyCompression`), which is fast and self-disables on incompressible data. Disable it only if benchmarks justify it:

```cpp
options.compression = leveldb::kNoCompression;
```

Zstandard is also available where the `zstd` library is present:

```cpp
options.compression = leveldb::kZstdCompression;
options.zstd_compression_level = 1;   // supported range [-5, 22], default 1
```

### 9.4 Block cache

Provide an explicit LRU cache to keep frequently used **uncompressed** blocks in memory.

```cpp
#include "leveldb/cache.h"

leveldb::Options options;
options.block_cache = leveldb::NewLRUCache(100 * 1048576);  // 100 MB
leveldb::DB* db;
leveldb::DB::Open(options, name, &db);
// ... use the db ...
delete db;
delete options.block_cache;   // delete AFTER the db
```

If `block_cache` is null, LevelDB creates an internal **8 MB** cache automatically. Size the cache to your application-level data sizes (it holds uncompressed data).

> [!TIP]
> When performing a one-off bulk read, set `ReadOptions::fill_cache = false` so the scan doesn't evict your hot working set from the cache:
>
> ```cpp
> leveldb::ReadOptions ro;
> ro.fill_cache = false;
> leveldb::Iterator* it = db->NewIterator(ro);
> ```

### 9.5 Key layout

The unit of disk transfer and caching is a block, and adjacent keys (by sort order) usually land in the same block. Place keys accessed together near each other. For example, when building a file system on LevelDB, prefix metadata keys (`/name`) and block-data keys (`0blockid`) with different leading bytes so a metadata scan doesn't drag bulky file contents through the cache.

### 9.6 Other capacity knobs

| Option | Default | Tune when |
|---|---|---|
| `write_buffer_size` | 4 MB | Larger speeds bulk loads but uses more memory and lengthens recovery |
| `max_open_files` | 1000 | Increase for large working sets (budget ~1 open file per 2 MB) |
| `max_file_size` | 2 MB | Increase if your filesystem prefers larger files |

### 9.7 Checksums and integrity

LevelDB stores checksums for all data. Two controls govern verification:

```cpp
leveldb::ReadOptions ro;
ro.verify_checksums = true;       // verify on every read (default: false)
```

```cpp
leveldb::Options options;
options.paranoid_checks = true;   // fail fast on detected corruption (default: false)
```

> [!CAUTION]
> With `paranoid_checks = true`, a single corrupted entry can make a large portion of the database — or the whole database — unreadable, because LevelDB stops at the first detected error. Leave it off if you prefer best-effort availability over fail-fast strictness.

---

## 10. Using the C API

LevelDB ships a complete C binding (`leveldb/c.h`) that exposes opaque struct pointers and functions. It's intended as a stable ABI for shared-library users and for foreign-function interfaces (e.g. JNI).

Conventions of the C API:

- Errors are reported through a trailing `char** errptr`. On success `*errptr` is left unchanged; on failure it is set to a `malloc`-ed, null-terminated message you must free.
- There is no `Slice` type — pass pointer and length as separate arguments.
- Booleans are `uint8_t` (`0` = false, non-zero = true).
- All pointer arguments must be non-NULL.

A minimal open / put / get / close sequence:

```c
#include "leveldb/c.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void) {
  char* err = NULL;

  leveldb_options_t* options = leveldb_options_create();
  leveldb_options_set_create_if_missing(options, 1);

  leveldb_t* db = leveldb_open(options, "/tmp/c_testdb", &err);
  if (err != NULL) { fprintf(stderr, "open: %s\n", err); leveldb_free(err); return 1; }

  leveldb_writeoptions_t* woptions = leveldb_writeoptions_create();
  leveldb_put(db, woptions, "foo", 3, "hello", 5, &err);
  if (err != NULL) { fprintf(stderr, "put: %s\n", err); leveldb_free(err); return 1; }

  leveldb_readoptions_t* roptions = leveldb_readoptions_create();
  size_t vlen;
  char* val = leveldb_get(db, roptions, "foo", 3, &vlen, &err);
  if (err == NULL && val != NULL) {
    printf("foo = %.*s\n", (int)vlen, val);
    leveldb_free(val);
  }

  leveldb_writeoptions_destroy(woptions);
  leveldb_readoptions_destroy(roptions);
  leveldb_options_destroy(options);
  leveldb_close(db);
  return 0;
}
```

```text
foo = hello
```

The C API mirrors the C++ one: write batches (`leveldb_writebatch_*`), iterators (`leveldb_iter_*`), snapshots (`leveldb_create_snapshot`), custom comparators (`leveldb_comparator_create`), filter policies (`leveldb_filterpolicy_create_bloom`), LRU caches (`leveldb_cache_create_lru`), and management calls (`leveldb_destroy_db`, `leveldb_repair_db`, `leveldb_compact_range`).

> [!IMPORTANT]
> Free every buffer the C API returns with `leveldb_free` (not plain `free`). On some platforms — notably Windows — the library and your program may use different allocators, and `leveldb_free` routes the deallocation correctly.

The C compression enum differs from C++: `leveldb_no_compression = 0`, `leveldb_snappy_compression = 1`.

> [!NOTE]
> The C API does **not** support: option *getters*, custom comparators that implement key shortening, or custom iterator/DB/Env/Cache implementations written purely through the C bindings.

---

## 11. The `leveldbutil` Command-Line Tool

Building LevelDB also produces `leveldbutil`, a small command-line utility. It currently supports exactly one command.

### 11.1 Usage

```bash
leveldbutil command...
```

```text
Usage: leveldbutil command...
   dump files...         -- dump contents of specified files
```

### 11.2 `dump` — print the contents of LevelDB files

```bash
leveldbutil dump /path/to/db/000005.ldb /path/to/db/MANIFEST-000002
```

`dump` prints, in text form, the contents of each named LevelDB storage file (SSTables, log files, and MANIFEST/descriptor files) to standard output. It accepts one or more file paths. If a file cannot be read or is not a recognized LevelDB file, an error message is printed to standard error and the tool exits with a non-zero status.

> [!NOTE]
> `dump` operates on the individual files inside a database directory, not on the directory as a whole. Point it at specific `.ldb`, `.log`, or `MANIFEST-*` files. This is a diagnostic and inspection aid, not a backup or export format.

> [!IMPORTANT]
> `dump` is the **only** subcommand `leveldbutil` provides. There are no flags, no `get`/`put`/`repair` subcommands, and no interactive mode. To manipulate data, use the library API.

---

## 12. Troubleshooting

Errors surface as `leveldb::Status` values (or `*errptr` strings in the C API). The patterns below cover the situations you are most likely to hit.

### Error: database open fails with "exists" when `error_if_exists` is set

**Cause:** `Options::error_if_exists = true` and the target directory already contains a database.
**Solution:** Either remove the existing database (`DestroyDB`) or set `error_if_exists = false` to open the existing one.
**Prevention:** Only set `error_if_exists = true` when you specifically intend to fail on a pre-existing database.

### Error: open returns NotFound / "does not exist"

**Cause:** The database directory does not exist and `create_if_missing` is `false` (the default).
**Solution:** Set `options.create_if_missing = true` before calling `DB::Open`.
**Prevention:** Decide explicitly whether your code should create databases on demand.

### Error: `Corruption: ...` on open or read

**Cause:** On-disk data failed a checksum or structural check (bad sectors, a partially written file after a power loss, or a bug).
**Solution:** Try `leveldb::RepairDB(name, options)` to salvage what is recoverable, then reopen. Inspect suspect files with `leveldbutil dump`.
**Prevention:** Use `WriteOptions::sync = true` for writes that must survive power loss; consider `verify_checksums = true` for early detection.

> [!WARNING]
> `RepairDB` can lose data. Take a copy of the database directory before repairing if the data is important.

### Error: comparator name mismatch — open fails

**Cause:** The database was created with one comparator name and is being opened with a different one (`Options::comparator->Name()` differs from the recorded name).
**Solution:** Open with the *same* comparator (and name) used to create the database.
**Prevention:** Never rename a comparator unless you intend to abandon existing databases. Embed a version byte in keys to evolve formats without renaming. See [Custom comparators](#81-custom-comparators).

### Error: "too many open files" / IOError under heavy load

**Cause:** The working set requires more file descriptors than `max_open_files` (or the OS limit) allows.
**Solution:** Increase `options.max_open_files` (budget ~1 open file per 2 MB of working set) and/or raise the OS file-descriptor limit (`ulimit -n`).
**Prevention:** Size `max_open_files` to your dataset; modern LevelDB also caps level-0 files to mitigate this.

### Error: second process cannot open the database (lock held)

**Cause:** A LevelDB database may be opened by **only one process at a time**; LevelDB holds an OS lock on the directory.
**Solution:** Ensure no other process has the database open. Within one process, share the single `DB` object across threads instead of opening twice.
**Prevention:** Architect for a single owning process; wrap your own server around LevelDB if multiple clients need access. See [Limitations](#limitations).

### Error: recent writes lost after a power failure

**Cause:** Writes used the default `sync = false`, so the last updates were still in the OS buffer cache when power was lost.
**Solution:** Re-run from your last checkpoint.
**Prevention:** Use `WriteOptions::sync = true` for durability-critical writes, or checkpoint periodically with a synchronous marker write.

### Symptom: high read latency right after many random writes

**Cause:** Many level-0 files accumulate during bursty writes; reads must merge across them until background compaction catches up.
**Solution:** Allow compaction to complete, or trigger `CompactRange(nullptr, nullptr)`. Add a block cache and a Bloom filter policy.
**Prevention:** Provide a `FilterPolicy` and a generously sized `block_cache`; avoid sustained write bursts far above your disk's compaction throughput.

### Symptom: dangling data / crash when using `Slice`

**Cause:** A `Slice` outlived the byte array it pointed to.
**Solution:** Copy with `Slice::ToString()` when you need to retain key/value bytes beyond the current scope or the next iterator mutation.
**Prevention:** Treat every `Slice` as a borrow, never as an owner. See [the Slice warning](#32-slice--a-pointer--length).

---

## 13. Frequently Asked Questions

**Q: Is LevelDB a database server I can connect to over the network?**
No. LevelDB is an in-process library with no built-in client-server support. If you need remote access, wrap your own server around the library.

**Q: Can multiple processes share one database?**
No. Only a single process (which may be multi-threaded) can have a given database open at a time; LevelDB enforces this with a file lock. Within that process, the `DB` object is safe to share across threads.

**Q: Does LevelDB support SQL, tables, or secondary indexes?**
No. It is a sorted key-value store only — no relational model, no SQL, no indexes. You build higher-level structures on top of it.

**Q: Are my keys and values restricted to text?**
No. Keys and values are arbitrary byte arrays and may contain `'\0'`. That is why the API uses `Slice` (pointer + length) rather than C strings.

**Q: How are keys ordered?**
By default, lexicographically by byte value (`BytewiseComparator`). Supply a custom `Comparator` to change the order.

**Q: Is compression required? What if Snappy isn't installed?**
Compression is optional and on by default. If the Snappy library isn't present at build time, blocks are stored uncompressed and the default compression setting is simply a no-op.

**Q: How do I make a write durable against power loss?**
Set `WriteOptions::sync = true`. By default writes are asynchronous and a reboot may lose the most recent updates (a mere process crash will not).

**Q: How do I take a consistent backup?**
Snapshots give a consistent read-only *view* for live reads/iteration. For a file backup, ensure writes are quiesced or use a snapshot-aware copy of the directory; LevelDB's design (immutable SSTables) makes copying the directory feasible. See the Admin Guide for operational backup guidance.

**Q: How big can a LevelDB database get?**
LevelDB has been tested with very large datasets (on the order of a billion ~100-byte entries, ~100 GB). Practical limits depend on your hardware, `max_open_files`, and workload.

**Q: How do I delete an entire database?**
Call `leveldb::DestroyDB(name, options)` on a closed database. To recover an un-openable one, try `leveldb::RepairDB`.

**Q: Why is there no `Close()` method?**
You close the database by `delete`-ing the `DB*` handle. Delete all iterators and release all snapshots first.

**Q: What is the current version?**
This documentation reflects LevelDB **1.23** (`kMajorVersion = 1`, `kMinorVersion = 23`).

**Q: Which headers am I allowed to include?**
Only those under `include/leveldb/`. Everything else is internal and may change without notice.

**Q: Does `leveldbutil` let me read or edit values from the shell?**
Only indirectly. `leveldbutil dump` prints the textual contents of LevelDB files; there is no command-line `get`/`put`. Use the library API to manipulate data.

---

## 14. Reference

### 14.1 `Options` fields

Defaults as defined in `include/leveldb/options.h`.

| Field | Type | Default | Description |
|---|---|---|---|
| `comparator` | `const Comparator*` | `BytewiseComparator()` | Key ordering. Must match the DB's recorded comparator. |
| `create_if_missing` | `bool` | `false` | Create the DB if it doesn't exist. |
| `error_if_exists` | `bool` | `false` | Fail to open if the DB already exists. |
| `paranoid_checks` | `bool` | `false` | Stop early on any detected corruption. |
| `env` | `Env*` | `Env::Default()` | OS abstraction for file/thread operations. |
| `info_log` | `Logger*` | `nullptr` | Where progress/error logs go (null = a file in the DB dir). |
| `write_buffer_size` | `size_t` | `4 * 1024 * 1024` | In-memory buffer before flushing to an SSTable. |
| `max_open_files` | `int` | `1000` | Max files the DB may keep open. |
| `block_cache` | `Cache*` | `nullptr` | Uncompressed-block cache (null = internal 8 MB cache). |
| `block_size` | `size_t` | `4 * 1024` | Approx uncompressed bytes per block. |
| `block_restart_interval` | `int` | `16` | Keys between restart points for key delta-encoding. |
| `max_file_size` | `size_t` | `2 * 1024 * 1024` | Max bytes per table file. |
| `compression` | `CompressionType` | `kSnappyCompression` | Block compression algorithm. |
| `zstd_compression_level` | `int` | `1` | Zstd level, range `[-5, 22]`. |
| `reuse_logs` | `bool` | `false` | (Experimental) append to existing MANIFEST/log on open for faster startup. |
| `filter_policy` | `const FilterPolicy*` | `nullptr` | Optional filter (e.g. Bloom) to cut disk reads. |

### 14.2 `ReadOptions` fields

| Field | Type | Default | Description |
|---|---|---|---|
| `verify_checksums` | `bool` | `false` | Verify checksums of all data read. |
| `fill_cache` | `bool` | `true` | Cache the data read by this operation. |
| `snapshot` | `const Snapshot*` | `nullptr` | Read as of this snapshot (null = implicit current). |

### 14.3 `WriteOptions` fields

| Field | Type | Default | Description |
|---|---|---|---|
| `sync` | `bool` | `false` | Flush to persistent storage before returning. |

### 14.4 Status codes

| Predicate | Meaning |
|---|---|
| `ok()` | Success |
| `IsNotFound()` | Key not present |
| `IsCorruption()` | Data corruption detected |
| `IsIOError()` | Underlying I/O failure |
| `IsNotSupportedError()` | Operation not supported |
| `IsInvalidArgument()` | Bad argument |

Use `Status::ToString()` for a printable description.

### 14.5 `DB` methods

| Method | Purpose |
|---|---|
| `static Status Open(options, name, &db)` | Open or create a database. |
| `Status Put(wopts, key, value)` | Insert/overwrite an entry. |
| `Status Delete(wopts, key)` | Remove an entry (OK if absent). |
| `Status Write(wopts, batch)` | Apply a `WriteBatch` atomically. |
| `Status Get(ropts, key, &value)` | Look up a key. |
| `Iterator* NewIterator(ropts)` | Create a sorted iterator (caller deletes). |
| `const Snapshot* GetSnapshot()` | Take a consistent read snapshot. |
| `void ReleaseSnapshot(snap)` | Release a snapshot. |
| `bool GetProperty(name, &value)` | Read an internal property. |
| `void GetApproximateSizes(ranges, n, sizes)` | Estimate on-disk size of key ranges. |
| `void CompactRange(begin, end)` | Force compaction of a key range. |

Free functions: `DestroyDB(name, options)`, `RepairDB(name, options)`.

### 14.6 `Iterator` methods

| Method | Purpose |
|---|---|
| `bool Valid()` | Is the iterator positioned at an entry? |
| `void SeekToFirst()` | Position at the first key. |
| `void SeekToLast()` | Position at the last key. |
| `void Seek(target)` | Position at the first key ≥ `target`. |
| `void Next()` | Advance forward (requires `Valid()`). |
| `void Prev()` | Move backward (requires `Valid()`). |
| `Slice key()` | Current key (valid until next mutation). |
| `Slice value()` | Current value (valid until next mutation). |
| `Status status()` | Error status encountered during iteration. |

### 14.7 Public headers

`c.h`, `cache.h`, `comparator.h`, `db.h`, `dumpfile.h`, `env.h`, `export.h`, `filter_policy.h`, `iterator.h`, `options.h`, `slice.h`, `status.h`, `table.h`, `table_builder.h`, `write_batch.h`. Start with `db.h`.

### Limitations

- **Not a SQL database** — no relational model, no SQL queries, no indexes.
- **Single process** — only one (possibly multi-threaded) process may access a database at a time.
- **No client-server** — no built-in network access; wrap your own server if needed.

---

_Generated June 2026 from google/leveldb at 7ee830d. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
