diff --git a/heed/Cargo.toml b/heed/Cargo.toml index 80aa6336..5dd0f87c 100644 --- a/heed/Cargo.toml +++ b/heed/Cargo.toml @@ -32,6 +32,7 @@ serde = { version = "1.0.217", features = ["derive"], optional = true } synchronoise = "1.0.1" [dev-dependencies] +memchr = "2.7.4" serde = { version = "1.0.217", features = ["derive"] } tempfile = "3.15.0" diff --git a/heed/src/envs/encrypted_env.rs b/heed/src/envs/encrypted_env.rs index 742280c8..4de94431 100644 --- a/heed/src/envs/encrypted_env.rs +++ b/heed/src/envs/encrypted_env.rs @@ -253,8 +253,50 @@ impl EncryptedEnv { /// /// This function may be used to make a backup of an existing environment. /// No lockfile is created, since it gets recreated at need. - pub fn copy_to_file>(&self, path: P, option: CompactionOption) -> Result { - self.inner.copy_to_file(path, option) + /// + /// Note that the file must be seek to the beginning after the copy is complete. + /// + /// ``` + /// use std::fs; + /// use std::io::{Read, Seek, SeekFrom}; + /// use std::path::Path; + /// use heed3::{EnvOpenOptions, Database, EnvFlags, FlagSetMode, CompactionOption}; + /// use heed3::types::*; + /// use memchr::memmem::find_iter; + /// + /// # fn main() -> Result<(), Box> { + /// # let dir = tempfile::tempdir()?; + /// # let env = unsafe { EnvOpenOptions::new() + /// # .map_size(10 * 1024 * 1024) // 10MB + /// # .max_dbs(3000) + /// # .open(dir.path())? + /// # }; + /// + /// let mut wtxn = env.write_txn()?; + /// let db: Database = env.create_database(&mut wtxn, None)?; + /// + /// db.put(&mut wtxn, &"hello0", &"world0")?; + /// db.put(&mut wtxn, &"hello1", &"world1")?; + /// db.put(&mut wtxn, &"hello2", &"world2")?; + /// db.put(&mut wtxn, &"hello3", &"world3")?; + /// + /// wtxn.commit()?; + /// + /// let mut tmp_file = tempfile::tempfile()?; + /// env.copy_to_file(&mut tmp_file, CompactionOption::Enabled)?; + /// let offset = tmp_file.seek(SeekFrom::Current(0))?; + /// assert_ne!(offset, 0); + /// + /// let offset = tmp_file.seek(SeekFrom::Start(0))?; + /// assert_eq!(offset, 0); + /// + /// let mut content = Vec::new(); + /// tmp_file.read_to_end(&mut content)?; + /// assert!(content.len() > 8 * 6); // more than 8 times hellox + worldx + /// # Ok(()) } + /// ``` + pub fn copy_to_file(&self, file: &mut File, option: CompactionOption) -> Result<()> { + self.inner.copy_to_file(file, option) } /// Copy an LMDB environment to the specified file descriptor, with compaction option. diff --git a/heed/src/envs/env.rs b/heed/src/envs/env.rs index f7c00ccb..d7ad5600 100644 --- a/heed/src/envs/env.rs +++ b/heed/src/envs/env.rs @@ -403,17 +403,52 @@ impl Env { /// /// This function may be used to make a backup of an existing environment. /// No lockfile is created, since it gets recreated at need. - pub fn copy_to_file>(&self, path: P, option: CompactionOption) -> Result { - let file = File::options().create_new(true).write(true).open(&path)?; - let fd = get_file_fd(&file); - - unsafe { self.copy_to_fd(fd, option)? }; - - // We reopen the file to make sure the cursor is at the start, - // even a seek to start doesn't work properly. - let file = File::open(path)?; - - Ok(file) + /// + /// Note that the file must be seek to the beginning after the copy is complete. + /// + /// ``` + /// use std::fs; + /// use std::io::{Read, Seek, SeekFrom}; + /// use std::path::Path; + /// use heed::{EnvOpenOptions, Database, EnvFlags, FlagSetMode, CompactionOption}; + /// use heed::types::*; + /// use memchr::memmem::find_iter; + /// + /// # fn main() -> Result<(), Box> { + /// # let dir = tempfile::tempdir()?; + /// # let env = unsafe { EnvOpenOptions::new() + /// # .map_size(10 * 1024 * 1024) // 10MB + /// # .max_dbs(3000) + /// # .open(dir.path())? + /// # }; + /// + /// let mut wtxn = env.write_txn()?; + /// let db: Database = env.create_database(&mut wtxn, None)?; + /// + /// db.put(&mut wtxn, &"hello0", &"world0")?; + /// db.put(&mut wtxn, &"hello1", &"world1")?; + /// db.put(&mut wtxn, &"hello2", &"world2")?; + /// db.put(&mut wtxn, &"hello3", &"world3")?; + /// + /// wtxn.commit()?; + /// + /// let mut tmp_file = tempfile::tempfile()?; + /// env.copy_to_file(&mut tmp_file, CompactionOption::Enabled)?; + /// let offset = tmp_file.seek(SeekFrom::Current(0))?; + /// assert_ne!(offset, 0); + /// + /// let offset = tmp_file.seek(SeekFrom::Start(0))?; + /// assert_eq!(offset, 0); + /// + /// let mut content = Vec::new(); + /// tmp_file.read_to_end(&mut content)?; + /// assert_eq!(find_iter(&content, b"hello").count(), 4); + /// assert_eq!(find_iter(&content, b"world").count(), 4); + /// # Ok(()) } + /// ``` + pub fn copy_to_file(&self, file: &mut File, option: CompactionOption) -> Result<()> { + let fd = get_file_fd(file); + unsafe { self.copy_to_fd(fd, option) } } /// Copy an LMDB environment to the specified file descriptor, with compaction option. diff --git a/heed3/Cargo.toml b/heed3/Cargo.toml index eabc5926..e0b1b3e2 100644 --- a/heed3/Cargo.toml +++ b/heed3/Cargo.toml @@ -30,6 +30,7 @@ synchronoise = "1.0.1" [dev-dependencies] argon2 = { version = "0.5.3", features = ["std"] } +memchr = "2.7.4" serde = { version = "1.0.217", features = ["derive"] } chacha20poly1305 = "0.10.1" tempfile = "3.15.0"