Connector & Engine adjustments for asset scale precision loss leftovers - #199
Conversation
4927f5d to
d227963
Compare
09c89e6 to
0b8ee3f
Compare
|
Adding a type_length_limit check at the main.rs file does not seem to allow tests to build: Error is pretty opaque, does not indicate where we need to adjust things |
Implement the trait for the eth engine
05dce10 to
055d365
Compare
This allows the trait implementer to choose the data type of the leftovers instead of forcing them to use BigUint
|
Maybe related: rust-lang/rust#54540 (comment) Turns out boxing the futures instead of returning Either's does the trick. Not sure how I feel about it, but I cannot find another workaround. In linkmauve/tokio-socks5@a532d63, they allude how they use Boxes to avoid long compilation times because the nested futures really make the compiler tired. |
Note: https://github.com/interledgerjs/settlement-xrp is not updated yet to return data for checking leftovers
488677e to
88dc75d
Compare
|
@emschwartz this should be good for review again. Curious what you think on Box'ing the futures. Seems like there's no good alternative. |
emschwartz
left a comment
There was a problem hiding this comment.
I think I still prefer load_uncredited_settlement_amount and save_uncredited_settlement_amount over load/pop leftovers
| ) -> Box<dyn Future<Item = (), Error = ()> + Send> { | ||
| trace!("Saving leftovers {:?} {:?}", account_id, leftovers); | ||
| let mut pipe = redis::pipe(); | ||
| pipe.set(format!("leftovers:{}", account_id), leftovers.to_string()) |
There was a problem hiding this comment.
This should be adding to that amount rather than overwriting what's there
There was a problem hiding this comment.
Yeah that's correct since there may be a race condition. However, the amount being written is a BigNumber, we cannot do arithmetic with these in the store, unless we store them as numbers in redis.
What should we do if the connector replies with leftovers greater than u64? Alternatively, we should explore some way to perform atomic threadsafe operations on Redis where we pull some value, operate on it and write back to it.
There was a problem hiding this comment.
Kind of a janky solution but we could use a list of strings in Redis. When you want to save an amount, you would add another number to the list. When you want to get the leftovers, you would load the whole list (and empty it out -- although this creates a possibility that information would get lost if the SE crashes right then) and sum up the numbers parsed from the strings using the big integer library.
There was a problem hiding this comment.
I like the list of strings idea. When we fetch the leftovers, I also think we can mitigate any risk of the SE crashing:
- Generate a new idempotency key locally
- Run a pipeline or Lua script that:
a. Gets the uncredited list (and returns right away if it's empty)
b. Sets new key(s) with the idempotency key, its expiration, and the list of amounts it corresponds to
b. Empties the uncredited list
d. Returns the list of amounts corresponding to the idempotency key - Then, the BigNumber logic locally can sum up the list of amounts for the incoming settlement request. If it crashes and comes back online, it can safely lookup all unexpired idempotency keys, and add up their list of amounts locally to compute the total amount that should be sent in each of the incoming settlement requests.
- If that idempotency key expires/the request fails after a few retries, we can atomically append its list of amounts to the list of uncredited amounts and delete the idempotency key.
There was a problem hiding this comment.
The list of strings logic has been added, thanks for the suggestion @emschwartz.
| Ok(()) | ||
| }) | ||
| }) | ||
| let amount = BigUint::from(100u64); |
There was a problem hiding this comment.
This test should use numbers that are larger than u64, just to ensure it does exactly what we expect
| uncredited_settlement_amount | ||
| ); | ||
| let mut pipe = redis::pipe(); | ||
| pipe.lpush( |
There was a problem hiding this comment.
Might be worth including a comment recording why these amounts are stored as a list
| let mut pipe = redis::pipe(); | ||
| // Loads the value and resets it to 0 | ||
| pipe.lrange( | ||
| format!("uncredited_settlement_amount:{}", account_id), |
There was a problem hiding this comment.
Should this key be prefixed with the SE type like the other keys?
db28493 to
0d5a477
Compare
Closes #185