Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow opening a database using a file handle instead of a path #665

Merged
merged 4 commits into from
Sep 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ redb1 = { version = "=1.0.0", package = "redb" }
# Just benchmarking dependencies
[target.'cfg(not(target_os = "wasi"))'.dev-dependencies]
ctrlc = "3.2.3"
fastrand = "1.9.0"
fastrand = "2.0.0"
lmdb-rkv = "0.14.0"
sanakirja = "1.3.3"
sled = "0.34.7"
Expand Down
82 changes: 66 additions & 16 deletions src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ use crate::{
use crate::{ReadTransaction, Result, WriteTransaction};
use std::fmt::{Display, Formatter};
use std::fs::{File, OpenOptions};
use std::io;
use std::io::ErrorKind;
use std::marker::PhantomData;
use std::ops::RangeFull;
Expand Down Expand Up @@ -368,7 +367,7 @@ impl Database {

fn mark_persistent_savepoints(
system_root: Option<(PageNumber, Checksum)>,
mem: &mut TransactionalMemory,
mem: &TransactionalMemory,
oldest_unprocessed_free_transaction: TransactionId,
) -> Result {
let freed_list = Arc::new(Mutex::new(vec![]));
Expand Down Expand Up @@ -769,20 +768,32 @@ impl Builder {

/// Opens an existing redb database.
pub fn open(&self, path: impl AsRef<Path>) -> Result<Database, DatabaseError> {
if !path.as_ref().exists() {
Err(StorageError::Io(ErrorKind::NotFound.into()).into())
} else if File::open(path.as_ref())?.metadata()?.len() > 0 {
let file = OpenOptions::new().read(true).write(true).open(path)?;
Database::new(
file,
self.page_size,
None,
self.read_cache_size_bytes,
self.write_cache_size_bytes,
)
} else {
Err(StorageError::Io(io::Error::from(ErrorKind::InvalidData)).into())
let file = OpenOptions::new().read(true).write(true).open(path)?;

if file.metadata()?.len() == 0 {
return Err(StorageError::Io(ErrorKind::InvalidData.into()).into());
}

Database::new(
file,
self.page_size,
None,
self.read_cache_size_bytes,
self.write_cache_size_bytes,
)
}

/// Open an existing or create a new database in the given `file`.
///
/// The file must be empty or contain a valid database.
pub fn create_file(&self, file: File) -> Result<Database, DatabaseError> {
Database::new(
file,
self.page_size,
self.region_size,
self.read_cache_size_bytes,
self.write_cache_size_bytes,
)
}
}

Expand All @@ -795,7 +806,10 @@ impl std::fmt::Debug for Database {

#[cfg(test)]
mod test {
use crate::{Database, Durability, ReadableTable, TableDefinition};
use crate::{
Database, DatabaseError, Durability, ReadableTable, StorageError, TableDefinition,
};
use std::io::ErrorKind;

#[test]
fn small_pages() {
Expand Down Expand Up @@ -1082,4 +1096,40 @@ mod test {
let final_file_size = tmpfile.as_file().metadata().unwrap().len();
assert!(final_file_size < file_size);
}

#[test]
fn create_new_db_in_empty_file() {
let tmpfile = crate::create_tempfile();

let _db = Database::builder()
.create_file(tmpfile.into_file())
.unwrap();
}

#[test]
fn open_missing_file() {
let tmpfile = crate::create_tempfile();

let err = Database::builder()
.open(tmpfile.path().with_extension("missing"))
.unwrap_err();

match err {
DatabaseError::Storage(StorageError::Io(err)) if err.kind() == ErrorKind::NotFound => {}
err => panic!("Unexpected error for empty file: {}", err),
}
}

#[test]
fn open_empty_file() {
let tmpfile = crate::create_tempfile();

let err = Database::builder().open(tmpfile.path()).unwrap_err();

match err {
DatabaseError::Storage(StorageError::Io(err))
if err.kind() == ErrorKind::InvalidData => {}
err => panic!("Unexpected error for empty file: {}", err),
}
}
}
2 changes: 1 addition & 1 deletion src/multimap_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -948,7 +948,7 @@ impl<'db, 'txn, K: RedbKey + 'static, V: RedbKey + 'static> Drop
{
fn drop(&mut self) {
self.transaction
.close_table(&self.name, self.system, &mut self.tree);
.close_table(&self.name, self.system, &self.tree);
}
}

Expand Down
5 changes: 2 additions & 3 deletions src/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,7 @@ impl<'db, 'txn, K: RedbKey + 'static, V: RedbValue + 'static> Table<'db, 'txn, K
// TODO: optimize this
let last = self
.iter()?
.rev()
.next()
.next_back()
.map(|x| x.map(|(key, _)| K::as_bytes(&key.value()).as_ref().to_vec()));
if let Some(owned_key) = last {
let owned_key = owned_key?;
Expand Down Expand Up @@ -189,7 +188,7 @@ impl<K: RedbKey, V: RedbValue> Sealed for Table<'_, '_, K, V> {}
impl<'db, 'txn, K: RedbKey + 'static, V: RedbValue + 'static> Drop for Table<'db, 'txn, K, V> {
fn drop(&mut self) {
self.transaction
.close_table(&self.name, self.system, &mut self.tree);
.close_table(&self.name, self.system, &self.tree);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/transactions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -644,7 +644,7 @@ impl<'db> WriteTransaction<'db> {
&self,
name: &str,
system: bool,
table: &mut BtreeMut<K, V>,
table: &BtreeMut<K, V>,
) {
if system {
self.open_system_tables
Expand Down
8 changes: 4 additions & 4 deletions src/tree_store/page_store/page_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ impl TransactionalMemory {
let region_size = min(region_size, (MAX_PAGE_INDEX as u64 + 1) * page_size as u64);
assert!(region_size.is_power_of_two());

let mut storage = PagedCachedFile::new(
let storage = PagedCachedFile::new(
file,
page_size as u64,
read_cache_size_bytes,
Expand Down Expand Up @@ -178,7 +178,7 @@ impl TransactionalMemory {
.write(0, DB_HEADER_SIZE, true, |_| CachePriority::High)?
.mem_mut()
.copy_from_slice(&header.to_bytes(false, false));
allocators.flush_to(tracker_page, layout, &mut storage)?;
allocators.flush_to(tracker_page, layout, &storage)?;

storage.flush()?;
// Write the magic number only after the data structure is initialized and written to disk
Expand Down Expand Up @@ -406,7 +406,7 @@ impl TransactionalMemory {

state
.allocators
.flush_to(tracker_page, state.header.layout(), &mut self.storage)?;
.flush_to(tracker_page, state.header.layout(), &self.storage)?;

state.header.recovery_required = false;
self.write_header(&state.header, false)?;
Expand Down Expand Up @@ -1083,7 +1083,7 @@ impl Drop for TransactionalMemory {
.flush_to(
state.header.region_tracker(),
state.header.layout(),
&mut self.storage,
&self.storage,
)
.is_err()
{
Expand Down
2 changes: 1 addition & 1 deletion src/tree_store/page_store/region.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ impl Allocators {
&self,
region_tracker_page: PageNumber,
layout: DatabaseLayout,
storage: &mut PagedCachedFile,
storage: &PagedCachedFile,
) -> Result {
let page_size = layout.full_region_layout().page_size();
let region_header_size =
Expand Down
Loading