-
Notifications
You must be signed in to change notification settings - Fork 224
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
Inject module_instance_id as prefix into database operations #1331
Conversation
Amost there, but IMO, we need to do the prefix adding and removal on the lower level APIs. The higher APIs are just a convenience layer. BTW. We can still pass to the modules the a The way I see it:
then maybe a helper: impl<'a> DatabaseTranstion<'a> {
fn with_module_prefix(self, module_id: Vec<u8>) -> &'a ModuleDatabaseTransaction<'a> {
ModuleDatabaseTransaction::new(self, module_id_to_db_byte_prefix(module_id)))
}
} so that in the code we can quickly turn for (module_id, module) in self.modules {
module.some_method(&dbtx.with_module_prefix(module_id), other_params).await?;
} As you can see There might be something I'm missing here, and async/await might get it the way, so please ping me if anything is unclear or you get stuck with some async/await + lifetimes problems. |
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.
Agree with @dpc and added some of my own nits
cb09498
to
268a8fc
Compare
Box::new(raw_prefix.map(|pair| match pair { | ||
Ok(kv) => { | ||
let key = kv.0; | ||
let stripped_key = &key[(self.prefix.len())..]; |
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.
observation: Assumes prefix-freeness of ModuleInstanceId
encodings.
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.
How come? The self.prefix
is the only thing we appended, and only thing we should remove. If we nest isolated classess each one will take care of its part of the prefix.
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.
Had we change the defintion of PrefixIter
to return &[u8]
we might get rid of the to_vec()
and since we are probably going to decode to the destination soon anyway, the higher level code would not care.
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.
Is the concern with to_vec just that it is an extra copy? I tried changing this to &[u8] but ran into some lifetime issues, since the returned key/value pairs are temporary values with a lifetime of the existing function. I think I would need to use a Box here if we wanted to return &[u8]?
broadcast_pending_tx( | ||
db.begin_transaction() | ||
.await | ||
.with_module_prefix(LEGACY_HARDCODED_INSTANCE_ID_WALLET), |
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.
Needs to be fixed. I'm ok with a follow-up, but outside tests and the old client we should not use legacy ids.
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.
Hmm yes I think this is best as a follow up. I think the issue here is that, in order to not use the hardcoded constant, we'd need to pass the module_instance_id all the way down from ModuleGen::init. We can't create a database transaction during ModuleGen::init because this thread is polling the DB and needs to create a new transaction every time. Passing it down all the way through ModuleGen::init though means individual modules are now aware of their ID, which I think we're trying to avoid. Maybe this would also be a good candidate for the notification work @maan2003
@dpc is there an existing issue for removing the legacy harded instance ids or should I make a new one?
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.
Wait, what? This is a module code already. Whatever this module is holding in that db: Database
should be already isolated.
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.
If we need to pass modules a whole db handle, we might need a similar wrapper for the whole Database
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.
The module must never get it's unprivileged hands on anything remotely resembling unisolated access to the database. :D
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.
Something tells me that it's best to avoid that instance_id in that IServerModule
, even if just to keep the typed and untyped version the same. It might come later and make us have to insert some dummy values where otherwise we wouldn't have to, or be a gateway to breaking the module-id obliviousness of the module implementation.
I think it should be calling code's responsibility to do this. I don't think there will be many places doing it, and it's not something that can be easily overlooked.
@elsirion What do you think?
I'll make the change, I agree it's not too difficult to do and the calling code should be responsible. |
Easier and safer for module writers to not worry about db txs 👍 |
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.
LGTM
@@ -169,10 +175,99 @@ impl Drop for CommitTracker { | |||
} | |||
} | |||
|
|||
/// IsolatedDatabaseTransaction is a wrapper around DatabaseTransaction that is responsible for |
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: using inline code formatting for type names would look nicer in rendered docs
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'm fine with landing already but we need to follow up with doing the same isolation technique for db: Database
that we give that wallet.
@m1sterc001guy feel free to smash the Merge button, just making sure you will not miss my commment :)
…_tx, and remove module_instance_id from ServerModule
Ah yes, I'll address that next. Will rebase then merge |
6f989af
to
977dfaa
Compare
This PR introduces a wrapper struct to wrap the DatabaseTransaction into an IsolatedDatabaseTransaction. IsolatedDatabaseTransaction will now always prepend a 2 byte "module_prefix", which is the ModuleInstanceId, when inserting and reading from the database. The purpose of this is to logically partition the database so modules can use keys that have conflicting prefixes. This also allows us to use the same key for different modules (ex: ModuleDatabaseVersion).
When passing a DatabaseTransaction to a module, it is first wrapped using with_module_prefix, which wraps the existing DatabaseTransaction inside an IsolatedDatabaseTransaction. This means they cannot overwrite keys from other modules. The fedimint server is now responsible for committing the wrapped DatabaseTransactions, because the individual modules cannot commit because they do not own the transaction. This approach also allows the fedimint server to create a DatabaseTransaction that spans over multiple modules and is also atomic.
Original issue