-
Notifications
You must be signed in to change notification settings - Fork 53
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
Refactor into store-centric API #101
Conversation
OK.. @ncloudioj @mykmelez - This should be good to go. When fixing up the tests, I'm pretty happy how the types and lifetimes got cleaned up a little.
Also, check out the Rkv::open* methods, I don't having to pass the extra params.. I guess the other option is to create a StoreConfig to house the flags and create-vs-open option. |
@rrichardson, this branch could use a merge from master as well. Currently it still shows changes that have landed on master in #100. |
@mykmelez - huh.. git tells me that my store-api-refactor branch is up to date with master. |
also says its up to date with rrichardson:2018-edition which merged in your changes.. |
Hmm, strange. The "Files changed" tab of this pull request is still showing the changes from #100. For example, examples/iterator.rs shows replacement of |
Strangely, GitHub displays an up-to-date diff when I compare mozilla/master to rrichardson/store-api-refactor: mozilla:master...rrichardson:store-api-refactor It's just this PR's "Files changed" view that is out-of-date. @rrichardson What happens when you merge the upstream master branch into this branch? When I do so, Git tells me that this branch is "Already up to date!" But then it creates a merge commit anyway:
I wonder if pushing such a commit to this branch would convince GitHub to update "Files changed." |
Thanks, that seems to have worked! The "Files changed" tab now only shows the changes on this branch, which makes it easier to review! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, check out the Rkv::open* methods, I don't having to pass the extra params.. I guess the other option is to create a StoreConfig to house the flags and create-vs-open option.
I don't know that there's a way to eliminate all extra arguments without multiplexing the open* functions, but a StoreConfig struct would halve them from two to one, and its value could be None if the StoreConfig parameter was optional (and the consumer wants the default config):
let store = env.open_single("foo", None);
Reducing configuration to a single argument would also allow us to retain open_or_create()_ variants without ending up with 16 permutations, as we'd only need a single _create_ variant for each open* function, for a total of 8:
pub fn open_single(…) {}
pub fn open_integer(…) {}
pub fn open_multi(…) {}
pub fn open_multi_integer(…) {}
pub fn open_or_create_single(…) {}
pub fn open_or_create_integer(…) {}
pub fn open_or_create_multi(…) {}
pub fn open_or_create_multi_integer(…) {}
And that would be useful, given that create: true
without flags seems to be one of the more popular configurations of the open* calls.
Either way, given a StoreConfig struct that derives Default, Clone, and Copy:
#[derive(Default, Copy, Clone)]
pub struct StoreConfig {
create: bool,
flags: Option<DatabaseFlags>,
}
Consumers could pass StoreConfig::default()
explicitly to indicate they want the default configuration, which is more self-documenting than the true, None
argument pair (although it's also longer):
let store = env.open_single("foo", StoreConfig::default());
Consumers could also create static variants for popular configurations:
static CREATE: StoreConfig = StoreConfig { create: true, flags: None };
…
let store = env.open_single("foo", CREATE);
(I might bikeshed the name of the struct a bit. The leveldb crate calls its equivalent simply Options, which seems simpler and can be disambiguated if necessary via declarations like use Store::Options as StoreOptions
.)
Arg.. I just ran |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, @rrichardson, this looks great. I went through it again and noticed a few more nits, but no blockers; so this can land as-is, with the nits addressed in followups as we see fit. Thanks for all your hard work on this significant enhancement!
}) | ||
} | ||
|
||
/// Provides a cursor to all of the values for the duplicate entries that match this key |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: this should say that the function provides the first value that matches this key.
match self.iter.next() { | ||
None => None, | ||
Some(Ok((key, bytes))) => Some((key, read_transform(Ok(bytes)))), | ||
Some(Err(_)) => None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: I'm going to change what Iter.next() returns in the Some(Err)
case over in #105. But I'll merge this PR first, so that PR doesn't break it, and then update that PR as needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
} | ||
} | ||
|
||
fn read_transform_owned(val: Result<&[u8], lmdb::Error>) -> Result<Option<OwnedValue>, StoreError> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: I don't see any uses of this in the codebase. Is it needed (or will soon be needed)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this was related to the MultiIter impl that I mostly removed because I failed to implement it.
|
||
/// Insert a value at the specified key. | ||
/// This put will allow duplicate entries. If you wish to have duplicate entries | ||
/// rejected, use the `put_with_flags` function and specify NO_DUP_DATA |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: we should add tests for the put_with_flags() functions as well, although we can do that in a followup.
} | ||
|
||
/* | ||
impl<'env> Iterator for MultiIter<'env> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: this comment block (and the next one) references a MultiIter type, but that type isn't defined anywhere (not even in another comment block). It'd be helpful to include a declaration of that type along with this partial implementation, even if it's just a skeletal one. Or, perhaps better, move this code into an issue that tracks the problem and its resolution.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like to support a MultiIter
. I have not been able to make it work re: lifetimes, it would need to copy over the cursor on every iteration (I think?) since it is an iterator of iterators.
I'd say leave it there, but ad a TODO or somesuch to remind us to do that in the future.
|
||
use crate::value::Value; | ||
|
||
fn read_transform(val: Result<&[u8], lmdb::Error>) -> Result<Option<Value>, StoreError> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: the two implementations of read_transform() in single.rs and multi.rs are identical and could be refactored into their super module store.rs.
lazy_static = "1.0.2" | ||
lmdb-rkv = "0.9" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: I'd preserve the alphabetical ordering here.
This depends on the 2018 edition PR #100
The
Store
types are nowThe Reader/Writer types have been completely removed.. this now uses
all
*Store::get
methods take aT: Transaction
instance.1 additional interface change:
open : bool
and aflags : DatabaseFlags
which all do what you'd expect.