Single-header embedded key-value store for C++20.
Drop this file into any project and get a persistent key-value database with zero dependencies, no build system changes, and no linking required.
- Quick start
db.put("username", "rick");
if (auto name = db.get("username")) {
std::cout << *name << std::endl;
}
A persistent key-value database backed by a single file.
Definition fluxen.hpp:665
- How does it work?
- The database is an append-only log on disk backed by a memory-mapped file. An in-memory hash index maps each key to the offset and length of its value in the file. Iteration via each() and prefix() is zero-copy. Callbacks receive a Bytes span pointing directly into the mapped region. get() copies into the return type. Writes append a 6-byte header + key + value to the file. Deletes append a tombstone. The index is rebuilt by scanning the log once on open (last write wins). transaction() stages any number of operations and flushes them as a single write + fsync.
- File & data layout
[magic: 8 bytes "FLUXEN01"]
[entry] [entry] ...
entry layout:
flags : 1 byte — 0x00 is live and 0x01 is tombstone
key_len : 1 byte — length of key in bytes (max 255)
val_len : 4 bytes — length of value in bytes (little-endian)
key : key_len bytes
value : val_len bytes (omitted in tombstone entries)
- Thread safety
- All public methods are thread-safe. Concurrent reads are permitted via a shared mutex: multiple threads may call
get(), has(), each(), prefix(), key_count(), and file_size() simultaneously. Write operations (put(), remove(), transaction(), compact()) acquire an exclusive lock and block until all active readers have finished.
The memory-mapped file is remapped lazily: writes leave the mapping stale and the first reader to observe a dirty mapping performs the remap under a secondary sync_mutex_ before proceeding. Subsequent concurrent readers that also observed the dirty flag wait for the remap to complete and then proceed without remapping again.
- Important limitations
- Maximum key length: 255 bytes.
- Maximum value length: ~4 GB (uint32_t).
- No range queries (keys are stored in hash order).
- The full key set lives in RAM (std::unordered_map).
- Windows-specific: The database file is locked while a DB object exists. You must destroy the DB object before attempting to delete or move the file.
- Compiler requirements
- C++20. Tested with GCC 12+, Clang 15+, MSVC 19.34+. Runs on Linux, macOS, and Windows.
- Version
- 1.1.1
- License
- MIT License