|
| | DB (std::string_view path) |
| | Opens or creates a database at the given path.
|
| | ~DB ()=default |
| | Closes the database and releases all file locks.
|
| void | put (std::string_view key, std::string_view value) |
| | Stores a string value under the given key.
|
template<typename T>
requires (std::is_trivially_copyable_v<T> && !std::is_convertible_v<T, std::string_view>) |
| void | put (std::string_view key, const T &value) |
| | Stores a trivially copyable value under the given key.
|
| template<typename T = std::string> |
| auto | get (std::string_view key) const -> std::optional< T > |
| | Retrieves the value stored under the given key.
|
| void | remove (std::string_view key) |
| | Deletes the value stored under the given key.
|
| auto | has (std::string_view key) const -> bool |
| | Returns true if the given key exists in the database.
|
| void | each (const std::function< void(std::string_view, Bytes)> &fn) const |
| | Iterates over all live key-value pairs.
|
| void | prefix (std::string_view pfx, const std::function< void(std::string_view, Bytes)> &fn) const |
| | Iterates over all keys that begin with the given prefix.
|
| void | transaction (const std::function< TxResult(Tx &)> &fn) |
| | Executes a batch of operations atomically.
|
| auto | compact () -> bool |
| | Rewrites the database file retaining only live entries.
|
| auto | key_count () const -> size_t |
| | Returns the number of live keys currently stored.
|
| auto | file_size () const -> size_t |
| | Returns the current size of the database file in bytes.
|
A persistent key-value database backed by a single file.
Keys are UTF-8 strings up to 255 bytes. Values can be std::string, string literals, or any trivially copyable type (int, float, struct, ...).
The database is safe to use from multiple threads concurrently. Reads proceed in parallel, and writes acquire an exclusive lock and are given priority over waiting readers to prevent starvation. See the Thread safety section in the main page for full details.
- Poisoned state
- If a write operation fails at the OS level and the subsequent attempt to truncate the partial write also fails, the DB enters a permanent poisoned state. Every subsequent call to any public method will throw std::runtime_error. The only recovery is to destroy the DB object. Normal I/O errors that are successfully rolled back do not poison the database.
- Note
DB is non-copyable. Create one instance per file.
- Example
db.put("hits", int64_t{0});
if (auto val = db.get<int64_t>("hits")) {
std::cout << *val << std::endl;
}
A persistent key-value database backed by a single file.
Definition fluxen.hpp:665
template<typename T>
requires (std::is_trivially_copyable_v<T> && !std::is_convertible_v<T, std::string_view>)
| void fluxen::DB::put |
( |
std::string_view | key, |
|
|
const T & | value ) |
|
inline |
Stores a trivially copyable value under the given key.
The value is stored as its raw bytes via memcpy. Overwriting a key with a value of a different size is valid. A subsequent get<T>() call will return nullopt if the stored size does not match sizeof(T).
Acquires an exclusive lock, blocking until all active readers have finished.
- Template Parameters
-
| T | Any trivially copyable type: int, float, bool, struct, etc. Must not be implicitly convertible to std::string_view. |
- Parameters
-
| key | Key to write. Must be between 1 and 255 bytes. |
| value | Value to store. |
- Exceptions
-
| std::runtime_error | If the database is poisoned. |
| std::runtime_error | If the append fails. The partial write is rolled back via truncation before throwing. If truncation also fails, the database is poisoned and the error message will say so. |
| std::runtime_error | If the key is empty or longer than 255 bytes. |
- Example
struct Config { int port; bool debug; };
db.put("cfg", Config{8080, false});
db.put("score", int32_t{100});
template<typename T = std::string>
| auto fluxen::DB::get |
( |
std::string_view | key | ) |
const->std::optional< T > |
|
inline |
Retrieves the value stored under the given key.
Returns std::nullopt in two cases:
- The key does not exist.
- For non-string
T: the stored byte size differs from sizeof(T).
The default template parameter is std::string, so db.get("key") and db.get<std::string>("key") are equivalent.
Acquires a shared lock, allowing concurrent calls from other readers. If the memory-mapped file is stale from a recent write, this thread will remap it before reading (see ensure_mapped()).
- Template Parameters
-
| T | The type to deserialise into. Defaults to std::string. Must be std::string or a trivially copyable type. |
- Parameters
-
- Returns
- std::optional<T> containing the value, or std::nullopt.
- Exceptions
-
| std::runtime_error | If the database is poisoned. |
- Example
auto name = db.get("username");
auto score = db.get<int32_t>("score");
auto cfg = db.get<Config>("cfg");
if (auto v = db.get<int32_t>("score")) {
process(*v);
}
| void fluxen::DB::remove |
( |
std::string_view | key | ) |
|
|
inline |
Deletes the value stored under the given key.
Appends a tombstone entry to the log and removes the key from the index. If the key does not exist this is a no-op. Space used by the old value is reclaimed the next time compact() is called.
Acquires an exclusive lock, blocking until all active readers have finished. Waiting writers take priority over new readers.
- Parameters
-
- Exceptions
-
| std::runtime_error | If the database is poisoned. |
| std::runtime_error | If the append fails. The partial write is rolled back via truncation before throwing. If truncation also fails, the database is poisoned and the error message will say so. |
| std::runtime_error | If the key is empty or longer than 255 bytes. |
| void fluxen::DB::prefix |
( |
std::string_view | pfx, |
|
|
const std::function< void(std::string_view, Bytes)> & | fn ) const |
|
inline |
Iterates over all keys that begin with the given prefix.
A convenient way to implement namespaced key sets. For example, all keys stored as "user:1001", "user:1002", etc. can be iterated with db.prefix("user:", fn).
The callback and locking behaviour are identical to each(). The same warnings about write methods and Bytes span lifetime apply.
- Parameters
-
| pfx | The prefix string to filter by. |
| fn | Callback of the form void(std::string_view key, Bytes val). |
- Note
- Iteration is O(total keys), not O(matching keys), because the underlying index is a hash map with no sorted order.
- Exceptions
-
| std::runtime_error | If the database is poisoned. |
- Example
db.put("user:james", "admin");
db.put("user:bryce", "viewer");
db.put("config:x", "value");
});
| void fluxen::DB::transaction |
( |
const std::function< TxResult(Tx &)> & | fn | ) |
|
|
inline |
Executes a batch of operations atomically.
The callback receives a Tx object on which any number of put() and remove() calls can be staged. Returning fluxen::commit applies all operations atomically and flushes them to disk. Returning fluxen::rollback discards all staged operations and the database is left completely unchanged.
If the callback throws, the exception propagates to the caller and all staged operations are discarded (equivalent to rollback).
- Parameters
-
- Exceptions
-
| std::runtime_error | If the database is poisoned. |
| std::runtime_error | If the batch append fails, or if the append succeeds but the subsequent fsync fails. In both cases the partial write is truncated before throwing, leaving the database unchanged. If truncation itself fails after an fsync error, the database is poisoned and every subsequent call will throw. |
- Note
- All staged operations are serialized into a single buffer and written with one syscall and one fsync. This makes transaction() significantly faster than the equivalent number of individual put() calls when writing many keys at once. The callback runs without holding the database mutex, so other threads may write between callback return and commit; the transaction's own operations are always applied atomically.
- Example
tx.
put(
"balance", int32_t{500});
tx.
put(
"currency", std::string(
"USD"));
return fluxen::commit;
});
A staged batch of write operations, used inside DB::transaction().
Definition fluxen.hpp:553
void put(std::string_view key, std::string_view value)
Stage a string value to be written.
Definition fluxen.hpp:572
void remove(std::string_view key)
Stage a key deletion.
Definition fluxen.hpp:623
- Rolling back
if (!validate(new_value)) {
return fluxen::rollback;
}
return fluxen::commit;
});
| auto fluxen::DB::compact |
( |
| ) |
->bool |
|
inlinenodiscard |
Rewrites the database file retaining only live entries.
The append-only log accumulates dead entries over time as keys are overwritten or deleted. compact() rewrites the file with only the current live values, reclaiming that space.
The rewrite is crash-safe. Compacted data is first written to a temporary file ( <path>.tmp), fsynced, and then atomically renamed over the original. A crash at any point leaves either the original or the fully written new file intact.
Acquires an exclusive lock, blocking all reads and writes for the duration of the rewrite. The database remains fully consistent. If the process is interrupted mid-compact the old file is still intact.
There is no automatic compaction. Call this periodically if your workload involves many overwrites or deletions.
- Returns
- true if compaction succeeded. false if the atomic rename failed. In that case the database is fully intact and usable, just not compacted. The caller may retry later.
- Exceptions
-
| std::runtime_error | If the database is poisoned. |
| std::runtime_error | If compaction failed and the original file could not be reopened. The DB object must be destroyed; any further use is undefined. |
| std::runtime_error | If the file mapping could not be refreshed before or after the rewrite. The DB object must be destroyed due to any further use being undefined. |
- Warning
- On success, invalidates all previously returned Bytes spans and pointers into the mapped region. Reacquire any needed data after a successful compaction. Spans remain valid if false is returned.