-
Notifications
You must be signed in to change notification settings - Fork 322
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
Appending changesets to Store
after opening causes overwriting
#1517
Comments
Store
After opening causes overwritingStore
after opening causes overwriting
@evanlinjin could this happen with the changes in #1514? |
Thanks for writing up this ticket. The intended usecase of Maybe we can structure things to be more fool-proof. Need to think more on this. |
Thanks @KnowWhoami. Maybe |
bdk/crates/file_store/src/store.rs Line 135 in d99b3ef
bdk/crates/file_store/src/store.rs Line 238 in d99b3ef
|
|
I think it would help if we combine opening and loading into one |
I would like to work on this, but I've got some questions:
I'm thinking in implementing something like the |
Perhaps an easy fix is to change the file mode to bdk/crates/file_store/src/store.rs Lines 73 to 77 in 2cf46a2
After quick testing this appears to fix the issue in the original post, however it caused other test failures, in particular |
In particular, |
This is the idea. I have tested it against current file store tests and a modification of the test above to check the expected behavior and it works. Pros:
Cons:
diff --git a/crates/file_store/src/lib.rs b/crates/file_store/src/lib.rs
index 7c943ca2..236995e9 100644
--- a/crates/file_store/src/lib.rs
+++ b/crates/file_store/src/lib.rs
@@ -18,6 +18,8 @@ pub enum FileError {
Io(io::Error),
/// Magic bytes do not match what is expected.
InvalidMagicBytes { got: Vec<u8>, expected: Vec<u8> },
+ /// Something went wrong while decoding file content
+ WrongEncoding(entry_iter::IterError)
}
impl core::fmt::Display for FileError {
@@ -29,6 +31,7 @@ impl core::fmt::Display for FileError {
"file has invalid magic bytes: expected={:?} got={:?}",
expected, got,
),
+ Self::WrongEncoding(e) => e.fmt(f)
}
}
}
diff --git a/crates/file_store/src/store.rs b/crates/file_store/src/store.rs
index 62c3d91b..fdd7523c 100644
--- a/crates/file_store/src/store.rs
+++ b/crates/file_store/src/store.rs
@@ -92,6 +92,31 @@ where
})
}
+ /// Open [`Store`] and recover state after last write.
+ ///
+ /// Use [`open`] to open `Store` and manually handle the previous state if any.
+ /// Use [`create_new`] to create a new `Store`.
+ ///
+ /// # Errors
+ ///
+ /// If the prefixed bytes of the opened file does not match the provided `magic`, the
+ /// [`FileError::InvalidMagicBytes`] error variant will be returned.
+ ///
+ /// [`create_new`]: Store::create_new
+ pub fn rewind<P>(magic: &[u8], file_path: P) -> Result<Self, FileError>
+ where
+ P: AsRef<Path>,
+ {
+ let mut store = Self::open(magic, file_path)?;
+ for next_changeset in store.iter_changesets() {
+ if let Err(iter_error) = next_changeset {
+ return Err(FileError::WrongEncoding(iter_error));
+ }
+ }
+
+ Ok(store)
+ }
+
/// Attempt to open existing [`Store`] file; create it if the file is non-existent.
///
/// Internally, this calls either [`open`] or [`create_new`]. |
@nymius I do think it's reasonable to expect that the "open-append-close" flow should work without overwriting as you mentioned in #1517 (comment).
^ To be honest I'm a little confused what is meant by this. edit: I may have been lacking context in which this is actually true; it makes sense over the lifetime of one Store instance. If we assume the tests are correct, i.e. that On the other hand if we change the file mode to |
@nymius Revisiting the open-append-close idea: Isn't it the case that we will always want to load the backend upon opening the file? For example in the context of a bdk wallet we have to load everything into memory in order to initialize the wallet. Without this you would have nothing to "append". |
I agree. Unless there was a precondition forcing a re scan on each open (which wasn't documented), I think is the most logic thing to do. |
I did a summary of possible solutions I thought about:
I've tried all of the above. The majority of them make the following test fail,
The solutions I prefer is 5 and the second one is 7. Why 5 over 7? |
It should be relatively painless if we keep all the same semantics of /// Reopen an existing [`Store`]. Errors if...
pub fn reopen<P>(magic: &[u8], file_path: P) -> Result<Self, OpenError> where P: AsRef<Path> { ... } We can have a new error /// An error while opening or creating the file store
#[derive(Debug)]
pub enum OpenError {
/// entry iter error
EntryIter {
/// index that caused the error
index: usize,
/// iter error
iter: IterError,
},
/// file error
File(FileError),
} We should be able to keep the existing tests and add coverage for the new functionality fixing the issue. If there's no interest in going the |
Describe the bug
let's say a
Store
instance initially containschangeset1
stored in its file.bdk/crates/file_store/src/store.rs
Line 14 in d99b3ef
Then after opening the store instance using
Store::open
and appending a new changeset (changeset2
) usingappend_changeset
, the store will only containchangeset2
, notchangeset1 + changeset2
.bdk/crates/file_store/src/store.rs
Line 73 in d99b3ef
bdk/crates/file_store/src/store.rs
Line 162 in d99b3ef
To Reproduce
Expected behavior
store
-> its file pointer must be set to end.Build environment
Fedora 37
rustc 1.81.0-nightly (f21554f7f 2024-06-08)
nightly-2024-06-09-x86_64-unknown-linux-gnu
The text was updated successfully, but these errors were encountered: