-
Notifications
You must be signed in to change notification settings - Fork 785
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
Add RPC send ID for idempotency #610
Add RPC send ID for idempotency #610
Conversation
rai/node/wallet.cpp
Outdated
ids_path << path_a << "_send_action_ids"; | ||
auto error (0); | ||
error |= mdb_dbi_open (transaction_a, path_a.c_str (), MDB_CREATE, &handle); | ||
error |= mdb_dbi_open (transaction_a, ids_path.str ().c_str (), MDB_CREATE, &send_action_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.
I realize this pattern is used elsewhere, but this will try to open the second db even if the first one failed. Maybe we should change the pattern to error = error || mdb_dbi_open ...
?
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 don't think there's much harm to opening the second DB, and it's very unlikely to fail. I'd like to stick with precedent here.
rai/node/wallet.cpp
Outdated
block->serialize (stream); | ||
} | ||
auto status (mdb_put (transaction, store.send_action_ids, rai::mdb_val (id->size (), const_cast<char*> (id->data ())), rai::mdb_val (vector.size (), vector.data ()), 0)); | ||
assert (status == 0); |
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.
Maybe send_action
should propagate errors to the caller? In this case, in release mode, mdb_put errors are swallowed.
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.
Fixed
rai/node/wallet.hpp
Outdated
@@ -5,6 +5,7 @@ | |||
#include <rai/secure.hpp> | |||
|
|||
#include <mutex> | |||
#include <optional> |
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.
boost/optional.hpp is used elsewhere
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.
Fixed
c68aa50
to
dec7fe5
Compare
Load tester failed on the last Travis run. This happened before on another recent commit, but it never failed before the hash2 changes. I'm running it locally for some debugging. |
dec7fe5
to
34d2a9b
Compare
rai/node/wallet.cpp
Outdated
{ | ||
rai::transaction transaction (store.environment, nullptr, false); | ||
if (store.valid_password (transaction)) | ||
rai::transaction transaction (store.environment, nullptr, (bool)id_a); |
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.
Why does this need to be a write transaction if id_a is set?
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.
It updates the send_action_ids
store. Should I add a comment?
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.
It's better to only hold a write transaction for the exact amount of time you need it, since it blocks all other readers. You're not even sure you need it at this point, I would move it the block where you do the mdb_put.
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 send queue is serialized, so there's no race)
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 assuming it's better to release the old transaction before creating the write transaction, instead of using a nested transaction?
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.
Yes, annoyingly enough.
I'm also planning on returning an |
rai/node/wallet.cpp
Outdated
assert (!error2); | ||
block.reset (new rai::send_block (info.head, account_a, balance - amount_a, prv, source_a, generate_work_a ? work_fetch (transaction, source_a, info.head) : 0)); | ||
rai::bufferstream stream (reinterpret_cast<uint8_t const *> (i->second.data ()), i->second.size ()); | ||
block = rai::deserialize_block (stream, rai::block_type::send); |
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.
Do we need to store the whole block, or will the just the hash suffice?
If you can't lookup the block by hash to republish it, there's a good chance republishing it won't work anyways since it's probably a fork at that point (the send queue will have moved on, and another block will have taken it's place as the descendant of the parent block at the time).
34d2a9b
to
7e54479
Compare
Fixed @lukealonso's comments, and changed the |
7e54479
to
e20e55b
Compare
LGTM - @clemahieu @androm3da This should be merged and released ASAP. |
I'd support backporting this change until we're certain the hash2 changes are stable. |
@PlasmaPower Agreed, this is a critical fix. |
Hold off for a bit - this fails to restart. |
False alarm - it's some other problem, probably related to the testnet. |
What do we think about having just one id database that crosses all wallets, would that be simpler? The only difference would be handling on collisions which could be avoided if the caller uses a random number. |
The test does prove the idempotency with the same id (it asserts the balance and the block returned), and that it'll repeat the action with a different id. From a code architecture standpoint, I think it makes sense for the database to be per-wallet. |
It might be better to do it cross-wallet, if only because the number of dbs is limited and doesn't scale well. |
That's a good point. One question is concurrency. Are we doing actions for multiple wallets in parallel? |
It's possible for that to happen, yes, though write transactions are exclusive so we have some concurrency protection there. We could do both per-wallet and also in a single database by making the key {wallet_id, tx_id} -> hash. I'm not sure if variable keysizes make a performance difference. |
If you resend the same id to a different wallet it should fail, right? (from a user perspective) |
Honestly I think so. It's very easy to pick a unique, maybe random ID and it seems like it might be more confusing if multiple wallets can have the same id. |
So maybe the mapping should be |
In which structure should the |
I'm thinking the rai::wallets class. The reason is at some point i want to separate the ledger from the wallet data and since TX id tracking is related to a wallet operation it should be associated with that. |
e20e55b
to
ee20c84
Compare
I think we should either keep the send ids for multiple wallets independent, or return a block for a different wallet if you pass it the same send id. My latest commit does the latter. In my opinion, it doesn't make much sense to store the wallet id just to give the user an error when it doesn't match. |
LGTM |
LGTM as well |
Probably needs some more discussion.
Note that currently, send IDs are per wallet. If we decide to keep that behavior, it should be documented.