NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
crates.io · #2995 most downloaded on crates.io
Rust Embedded DataBase
Last release 5 days ago
02 Oct 2026
Release timing varies
gaps range from 8 days to 5 months
Nearly every release is documented
notes for 60 of the last 60 stable releases
2 versions withdrawn
withdrawn after publishing
8 years old
77 releases · first in 2018
Add optional locking methods to StorageBackend . Backends may implement these methods to support locking. Custom backends that wrap FileBackend should
StorageBackend. Backends may implement these methods to supportFileBackend should delegate these methods to the FileBackendKey::separator(), which returns a short byte string that separates two keys.&[u8], &str, String keys now store minimal prefixes. Option, array keys, and tuple keysKey::min_encoded_key(), the encoding of a key type's smallest value. Implementing it isexperimental-multiprocess feature flag.check_integrity() had previously repaired the database.ReadOnlyUntypedTable and ReadOnlyUntypedMultimapTable potentially returning incorrect resultsErr(Corrupted) could yield the rest of the table on later calls, skipping therestore_savepoint(), rename_table(), and delete_table() leaving the transactioncommit() returns an error instead ofcatch_unwind. The leak survived closing and reopening the database and grewWriteZero I/O error.rename_table() and rename_multimap_table() returning TableExists when the new namepersistent_savepoint() and delete_persistent_savepoint() making the transactionInvalidSavepoint. Only opening, renaming, or deleting a data table, or restoring aOne column per quarter.
Add an experimental cursor API, behind the experimental_cursor feature flag: Table::lower_bound_mut() and Table::upper_bound_mut() return a CursorMut
experimental_cursor feature flag:Table::lower_bound_mut() and Table::upper_bound_mut() return a CursorMut pointing at a gapBTreeMap cursors. Inserting sorted datainsert_before() method can be around 3x faster than calling insert() with theinsert_after() inserts through the gap in descending order at the same speed.ReadableTable::lower_bound() and ReadableTable::upper_bound() return a read-only Cursor.ReadOnlyTable::get_owned(), ReadOnlyTable::range_owned(),ReadOnlyMultimapTable::get_owned(), and ReadOnlyMultimapTable::range_owned(), whichOwnedAccessGuard, OwnedRange, OwnedMultimapValue, andOwnedMultimapRange types. These keep the read transaction alive until they are dropped,Table::entry() and the associated Entry, OccupiedEntry, and VacantEntrystd::collections::BTreeMap::entry. Supports or_insert,or_insert_with, or_insert_with_key, and_modify, and the usual OccupiedEntryVacantEntry accessors.ExtractIf::close() to explicitly finalize an extract iterator without removing unreadDurability::None commits are about 2x faster, and writes that do notDurability::None commits and about 6% fasterWriteTransaction are modified concurrentlyTable::pop_first() and Table::pop_last() to be about 2x faster.Table::retain(), Table::retain_in(), Table::extract_if(), andTable::extract_from_if(). Benchmarks on large tables show a 30-100x speedup for retaining andTable::retain(), Table::retain_in(), Table::extract_if(), and Table::extract_from_if()WriteTransaction::commit() to returnCommitError::TransactionPoisoned. After an extract iterator returns an error, later calls keepcompact() now returns CompactionError::PersistentSavepointExists orCompactionError::EphemeralSavepointExists instead of the misleadingCompactionError::TransactionInProgress when a savepoint blocks compaction.StorageBackend::close() is now called when opening a database fails and when an I/O error occursDatabase, allowing backends to release their resources on both paths.StorageError::Corrupted instead of panicking when opening or repairing a database with aStorageError::Corrupted instead of aborting the process when branch pages form a cycle orWriteTransaction::stats() returning garbage statistics, or panicking when debug assertionsDatabase was dropped while a WriteTransaction was live. A liveWriteTransaction now keeps the database open: the transaction remains usable after theDatabase is dropped, and the database closes when the transaction commits, aborts, or isDatabase, after check_integrity() returnedStorageError::Corrupted, and is no longer recorded as cleanly shut down.WriteTransaction::commit(): thecheck_integrity() failed to repair the database when the table length wasDatabase::drop.compact() could grow the file or stop before fully shrinking it.Table::get_mut() and Entry::and_modify() to enforce the maximum value size limit.Table::insert() and the entry() accessorsinsert() when a single leaf page accumulated 65536 entries via in-placecheck_integrity() incorrectly reporting a healthy database as corrupted (and panickingSavepoint was dropped from another thread while the same write transaction was committing.Savepoint was created from one thread while theStorageError::Corrupted.check_integrity() so that it now returns DatabaseError::TransactionInProgress when anSavepoint is still alive. Previously the check could invalidate the pages such aDatabase::check_integrity() silently discarding transactions committed withDurability::None that had not yet been made durable by a later commit; a passing check nowOption, Vec, tuples, and arrays) of a user-defined type sharing aTableError::TableTypeMismatch.This release contains a large number of bug fixes discovered by AI coding agents
This release contains a large number of bug fixes discovered by AI coding agents
MultimapValue::len() and is_empty() returned stale counts afternext_back().restore_savepoint() used in a non-Immediate durability transaction and whenSavepointError::InvalidSavepoint, but the savepoint would actually be partially applied.SavepointError::ImmediateDurabilityRequired.restore_savepoint() where modifications made earlier in the transaction mightrestore_savepoint() after modifying a table in the samedelete_table() was called on a table that had been modified in the samerestore_savepoint() when passed a Savepoint from a different Database.SavepointError::InvalidSavepoint is now returned instead.Database was dropped.check_integrity() when called while another transaction is still alive.DatabaseError::TransactionInProgress is now returned instead.restore_savepoint() with a savepointrestore_savepoint() would leave moreImplement Drop on AccessGuardMut and AccessGuardMutInPlace , which requires that these be dropped before the Table they borrow from. This fixes a crit
Drop on AccessGuardMut and AccessGuardMutInPlace, which requires that these be droppedTable they borrow from.Table, and be dropped after theLegacy type. To migrate off the Legacy type, use the Legacy type in the 3.x releaseFix a data loss bug which can occur when the guard returned from Table::get_mut() is dropped after the transaction has been committed.
Table::get_mut() is droppedTable::insert_reserve() indicating that it can cause data loss and recommendingReduce memory usage of open databases
Fix panic which could occur when inserting into a table with fixed size keys when debug_assertions are enabled
debug_assertions are enabledcache_stats()Implement std::Error for SetDurabilityError
std::error::Error for SetDurabilityErrorFix performance issue where a transaction with a large number of writes would cause WriteTransaction::abort() and committing non-durable transactions
WriteTransaction::abort() and committing non-durable transactions to become slowFix correctness issue with range() , extract_from_if() , and retain_in() . If a RangeBounds with start > end was passed as an argument and start and e
range(), extract_from_if(), and retain_in(). If a RangeBoundsstart > end was passed as an argument and start and end keys were stored in differentstart..Durability::None commits could become linearlyUse Database::upgrade(), in redb 2.6, to migrate to the v3 file format.
Use Database::upgrade(), in redb 2.6, to migrate to the v3 file format.
The v3 file format has been further optimized to reduce the size of the database. Databases with only
a few small keys will see the largest benefit, and the minimum size of a database file has decreased
from ~2.5MiB to ~50KiB. To achieve the smallest file size call Database::compact() before
dropping the Database.
Additionally, performance is ~15% better in bulk load benchmarks. This was achieved by implementing
a custom hash function for various in-memory HashSets and HashMaps, and by optimizing the usage
of buffers held in Arcs to reduce the number of atomic instructions executed.
Storage of variable width tuple types with arity greater than 1 is more efficient. The new format elides the length of any fixed width fields and uses varint encoding for the lengths of all variable width fields.
Note that this encoding is not compatible with the serialization of variable width tuples used in prior versions.
To load tuple data created prior to version 3.0, wrap them in the Legacy type.
For example, TableDefinition<u64, (&str, u32)> becomes TableDefinition<u64, Legacy<(&str, u32)>>.
Fixed width tuples, such as (u32, u64) are backwards compatible.
Key and Value can be derived using the redb-derive crate. Note that it does not support
schema migration. The recommended pattern to migrate schema is to create a new table, and then
perform a migration from the old table to the new table.
Multiple processes may open the same database file for reading by using the new ReadOnlyDatabase
type. On platforms which support file locks, this acquires a shared lock on the database file.
Non-durable transactions will now free pages when possible (pages allocated in a preceding non-durable transaction which are no longer referenced). This resolves an issue where a long sequence of non-durable transactions led to significant growth in the size of the database file. This change increases the RAM required for a sequence of non-durable transactions, such that RAM proportional to the net change in the database is now used. However, it will never use more than about 0.2% of the database file size.
ReadOnlyDatabaseBuilder::open_read_only()StorageBackend::close()Table::get_mut()chrono_v0_4 feature flag which enables serialization of the NaiveDate, NaiveTime,
NaiveDatetime, DateTime<FixedOffset>, and FixedOffset types in the chrono crateuuid feature flag which enables serialization of the Uuid type in the uuid crateStorageBackend::read() to accept a &mut [u8] output argument instead of returning
a Vec<u8>Table::insert_reserve() to take usize instead of u32 as the argument typeTypeName::name() to be publicReadTransactionStillInUse to contain a Boxset_durability() to return a ResultDatabase::cache_stats() and Database::begin_read() to ReadableDatabase traitAccessGuardMut to AccessGuardMutInPlace. Note that a new AccessGuardMut struct has
been added; it serves a different purposeDurability::Paranoidcheck_integrity() returned Ok(false) even though no repair was required,
when called on a database that was not shutdown cleanly and was automatically repaired when openedDatabase as been
dropped. Access will now return DatabaseClosedFix unbounded memory growth in the page cache under write workloads, when the cache never reached its configured size limit.
Fix correctness issue with range() , extract_from_if() , and retain_in() . If a RangeBounds with start > end was passed as an argument and start and e
range(), extract_from_if(), and retain_in(). If a RangeBoundsstart > end was passed as an argument and start and end keys were stored in differentstart..Forward compatibility improvement which makes the file format more flexible to support a potential future optimization
Fix a forward compatibility issue which caused a crash when opening databases created with redb 3.x. Note that opening 3.x databases with redb 2.x is
This file format improves savepoints. Savepoints in the v3 format have constant, and small, overhead. Creating and restoring them is also much faster.
This file format improves savepoints.
Savepoints in the v3 format have constant, and small, overhead. Creating
and restoring them is also much faster. The v3 file format also supports
savepoints on large databases (v2 has a limit around 32TB). This release
creates v2 databases by default. Use Builder::create_with_file_format_v3()
and Database::upgrade(), respectively, to enable and migrate to v3.
The upcoming 3.0 release will only support the v3 file format.
Builder::create_with_file_format_v3()Database::upgrade()Add rename_table() and rename_multimap_table()
rename_table() and rename_multimap_table()Key and Value implementations for the unary tuple type (i.e. (T,))debug_assertions were enabledretain() and retain_in() to use less space in the database fileLockPoisoned instead of panickingFix open() and create() to return InvalidData when they are called on a database file that is not a valid redb database
Database::cache_stats()open() and create() to return InvalidData when they are called on a database file
that is not a valid redb databaserestore_savepoint(). The time is takes now scales with the change delta
since the savepoint was captured, rather than the size of the database fileDatabaseStats::fragmented_bytes() is now more accurateDurability::Paranoid is now deprecated. Use set_two_phase_commit(true) instead
WriteTransaction::set_two_phase_commit()WriteTransaction::set_quick_repair() which enables a faster repair mechanism at the cost of
slower transaction commitsDurability::Paranoid is now deprecated. Use set_two_phase_commit(true) insteadImplement TableHandle for ReadOnlyTable
TableHandle for ReadOnlyTableOptimize first() and last() to be almost 2x faster
first() and last() to be almost 2x fasterSignificant performance optimizations to compact()
compact()compact() did not fully compact the databasecommit() or abort() after an IO error. StorageError::PreviousIo is now returnedFix leak of database space that could occur when calling restore_savepoint()
restore_savepoint()delete_multimap_table()Database was dropped, could cause database corruptionImportant: If your application has called restore_savepoint(), delete_multimap_table(),
or you suspect it may have experienced a transient I/O error during transaction commit.
It is recommended that you run check_integrity() after upgrading to this version.
This will both detect corruption and clean up any leaked space.
check_integrity() would return Ok(false) instead of Ok(true)compact() did not fully compact the databaseWriteTransaction::stats() more accurateStorageError::ValueTooLarge when a key-value pair exceeds a total of 3.75GiB.
Previously, a panic would occur for key-value pairs that were approximately 4GiB.info! log messages to debug!Fix panic that occurred when calling compact() when a read transaction was in progress
compact() when a read transaction was in progressReadTransaction::close() to return Ok when it succeedsImplement Key and Value for String
Key and Value for StringReadableTableMetadata, ReadableTable, and ReadableMultimapTable2.0.0 uses a new file format that optimizes len() to be constant time. This means that it is not backwards compatible with 1.x. To upgrade, consider u
2.0.0 uses a new file format that optimizes len() to be constant time. This means that it is not
backwards compatible with 1.x. To upgrade, consider using a pattern like that shown in the
upgrade_v1_to_v2 test.
check_integrity() now returns a DatabaseError instead of a StorageErrorReadableTableMetadata traitRedbKey to KeyRedbValue to ValueWriteTransaction and ReadTransactiondrain() and drain_filter() from Table. Use retain, retain_in, extract_if or extract_from_if insteadClone for Range[T;N] as a Value or Key as appropriate for the type Tlen() and is_empty() to MultimapValueretain() and retain_in() to Tableextract_if() and extract_from_if() to Tablerange() returning a Range with the 'static lifetime to read-only tablesget() returning a range with the 'static lifetime to read-only tablesclose() method to ReadTransactionNothing published for this version
Fix correctness issue with range(), drain(), and drain_filter(). If a RangeBounds with start > end was passed as an argument and start and end keys we
range(), drain(), and drain_filter(). If a RangeBounds
with start > end was passed as an argument and start and end keys were stored in different
internal pages in the database (i.e. a sufficient condition is that more than 4KiB of key-value
pairs were between the two keys) then these methods would perform as if the argument had been
start..Fix check_integrity() so that it returns Ok(true) when no repairs were preformed. Previously, it returned Ok(false)
check_integrity() so that it returns Ok(true) when no repairs were preformed. Previously,
it returned Ok(false)Export MutInPlaceValue which allows custom types to support insert_reserve()
TableStats typeMutInPlaceValue which allows custom types to support insert_reserve()Add Builder::set_repair_callback() which can be used to set a callback function that will be invoked if the database needs repair while opening it.
Builder::set_repair_callback() which can be used to set a callback function that will be invoked if the database needs repair while opening it.StorageBackend trait and
using the Builder::create_with_backend function. This allows the database to be stored in a location other
than the filesystemRedbKey and RedbValue for charRedbKey and RedbValue for boolTableHandle for TableMultimapTableHandle for MultimapTabledelete_table() on a table that is already openAdd ReadableTable::first() and last() which retrieve the first and last key-value pairs, respectively`
RedbKey for Option<T>RedbValue for Vec<T>Debug for tablesReadableTable::first() and last() which retrieve the first and last key-value pairs, respectively`Add Builder::create_file() which does the same thing as create() but takes a File instead of a path
Builder::create_file() which does the same thing as create() but
takes a File instead of a pathstats() to tables which provides informational statistics on the table's storageWriteTransaction::stats() to correctly count the storage used by multi-map tablesWriteTransactionSend for WriteTransactionFix panic when calling compact() on certain databases
compact() on certain databasescompact() when an ephemeral Savepoint existedcompact()insert()Fix a rare panic when recovering a database file after a crash
Fix serious data corruption issue when calling drain() or drain_filter() on a Table that had uncommitted data
drain() or drain_filter() on a Table that had
uncommitted dataFix panic when re-opening databases of certain, small, sizes
Fix panic when recovering some databases after a forceful shutdown
Fix panic that could occur after an IO error when reopening a database
redb has reached its first stable release! The file format is now gauranteed to be backward compatible, and the API is stable. I've run pretty extensi
redb has reached its first stable release! The file format is now gauranteed to be backward compatible, and the API is stable. I've run pretty extensive fuzz testing, but please report any bugs you encounter.
The following features are complete:
WriteTransaction and multiple ReadTransactionsrestore_savepoint()Fix panic while repairing a database file after crash
restore_savepoint()Improve cache heuristic. This asymptotically improves performance on large databases. Benchmarks show 30% to 5x+
Export TransactionError and CommitError. These were unintentionally private
TransactionError and CommitError. These were unintentionally privatestd::error::Error for all error enumsRemove Clone bound from range argument type on drain() and drain_filter()
Clone bound from range argument type on drain() and drain_filter()Improve errors to be more granular. Error has been split into multiple different enums, which can all be implicitly converted back to Error for conven
Error has been split into multiple different enums, which
can all be implicitly converted back to Error for conveniencesavepoint() to ephemeral_savepoint()restore_savepoint() to be ~30x fasterRedbKey for ()Enforce a limit of 3GiB on keys & values
Durability::None commit was made,
followed by a durable commit and the durable commit crashed or encountered an I/O error during commit()commit() could cause a panic instead of returning an Errlength argument to insert_reserve() to u32Table::len() to return u64DatabaseStats to u64K type parameter from AccessGuardMutDatabase::compact() which compacts the database fileCombine Builder::set_read_cache_size() and Builder::set_write_cache_size() into a single, Builder::set_cache_size() setting
Builder::set_read_cache_size() and Builder::set_write_cache_size() into a single,
Builder::set_cache_size() settingSavepointAdd Database::check_integrity() to explicitly run repair process (it is still always run if needed on db open)
Database::check_integrity() to explicitly run repair process (it is still always run if needed on db open)list_tables() to return a TableHandledelete_table() to take a TableHandleinsert_reserve() API signature type safeResult and propagate I/O errorsWriteStrategy with Durability::ParanoidBuilder::set_initial_size()Remove Builder::create_mmapped() and Builder::open_mmapped(). The mmap backend has been removed because it was infeasible to prove that it was sound.
Builder::create_mmapped() and Builder::open_mmapped(). The mmap backend has been removed
because it was infeasible to prove that it was sound. This makes the redb API entirely safe,
and the remaining File based backed is within a factor of ~2x on all workloads that I've benchmarkedTable implement Send. It is now possible to insert into multiple Tables concurrentlyAccessGuardMut, Drain and DrainFilter in the public APIRangeIter to RangeMultimapRangeIter to MultimapRangeMultimapValueIter to MultimapValueFix a major data corruption issue that was introduced in version 0.12.0. It caused databases greater than ~4GB to become irrecoverably corrupted due t
PageNumber::address_range
that was introduced by commit b2c44a824d1ba69f526a1a75c56ae8484bae7248drain_filter() to TableFix open() on platforms with OS page size != 4KiB
open() on platforms with OS page size != 4KiBrange() and drain()Add pop_first() and pop_last() to Table
pop_first() and pop_last() to Tabledrain() to TableOption<T> as a value typeRedbKey and/or RedbValueget(), insert(), remove()...etc to take arguments of type impl Borrow<SelfType>Error::UpgradeRequired when opening a file with an outdated file formatRemove [u8] and str type support. Use &[u8] and &str instead.
[u8] and str type support. Use &[u8] and &str instead.get(), range() and several other methods to return AccessGuard.AccessGuard::to_value() to value()Database::create() and
Database::open() safe, but has worse performance in some cases. The mmap backend is available
via create_mmapped()/open_mmapped(). There is no difference in the file format, so applications
can switch from one backend to the other.Remove maximum database size argument from create(). Databases are now unbounded in size
create(). Databases are now unbounded in sizeset_dynamic_growth()set_initial_size() to Builderset_page_size(). 4kB pages are always used nowiter() method to Table and MultimapTable&str and (&[u8], u64)Add support for dynamic file growth on Windows
Builder::set_region_sizeSavepointcreate() to open an existing database created with WriteStrategy::TwoPhasePerformance improvements for database files that are too large to fit in RAM
savepoint() and restore_savepoint()restore_savepoint() failedsavepoint() and restore_savepoint() methods to WriteTransactionIterator for the types returned from range() and remove_all()Add Database::set_write_strategy which allows the WriteStrategy of the database to be changed after creation
Database::set_write_strategy which allows the WriteStrategy of the database to be changed after creationDatabase::begin_write block, instead of panic'ing, if there is another write already in progressDatabase::savepoint and Database::restore_savepoint which can be used to snapshot and rollback the databaseDatabaseBuilder to BuilderFix crash when Database::open() was called on a database that had been created with WriteStrategy::TwoPhase
Database::open() was called on a database that had been created with WriteStrategy::TwoPhaseReturn Err instead of panic'ing when opening a database file with an incompatible file format version
Err instead of panic'ing when opening a database file with an incompatible file format versionWriteStrategysWriteStrategy::Checksum (which is the default)insert_reserve() in certain casesOptimize MultimapTable storage format to use O(k * log(n_k) + v * log(n_v / n_k)) space instead of O(k * log(n_k + n_v) + v * log(n_k + n_v)) space, w
MultimapTable storage format to use O(k * log(n_k) + v * log(n_v / n_k)) space instead of O(k * log(n_k + n_v) + v * log(n_k + n_v)) space,
where k is the size of the stored keys, v is the size of the stored values, n_k is the number of stored keys,
n_v is the number of stored values(), as a valueMultimapTable::range to return an iterator of key-value-collection pairs, instead of key-value pairsWriteTransaction on dropYour coding agent can read these notes before it upgrades. Set up the MCP server →