-
Notifications
You must be signed in to change notification settings - Fork 10
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
Guarantee uE implementors always get the correct UUID when calling UUIDBuilder::build()
by using internally held singleton
#74
Conversation
c0be6b9
to
39c25e7
Compare
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.
@PLeVasseur I like your idea of using CAS. I left some nitpicks.
@@ -590,17 +593,22 @@ mod tests { | |||
ttl, | |||
..Default::default() | |||
}; | |||
|
|||
if let Some(created_millis_ago) = created_millis_ago { | |||
thread::sleep(std::time::Duration::from_millis(created_millis_ago)); |
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.
This is more intuitive to create a timeout UAttributes, but I'm not sure whether this will need a longer time to do the test. (Maybe it's ok while we don't have too many of this kind of tests).
src/uuid/uuidbuilder.rs
Outdated
UUID::from_u64_pair(new_msb, self.lsb) | ||
.expect("should have been able to create UUID for valid timestamp") | ||
// Try to update the MSB atomically. | ||
match self.msb.compare_exchange( |
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.
Okay, so we use compare_exchange
instead of swap
here to ensure no one modifies the self.msb
, right?
Brilliant idea!
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.
Looks good overall, like what you can do with Rust to support the desired functionality, and that you put in the time to figure it out :)
thread::sleep(std::time::Duration::from_millis(created_millis_ago)); | ||
|
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.
FMPOV it is an anti-pattern to rely on sleeping in test code. Please replace this with the original or a clock-tick based approach.
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 agree that it's bad to sleep in test code. Problem is that we now calculate the timestamp within the build_internal()
function. Hmm. Do you have ideas on how to navigate the situation now that's the case?
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.
OK -- I think I was able to pull this off. Can you take another look @sophokles73?
…llow end-users a simpler, more consistent API when generating UUIDs for their uE.
…on, we wait for another millisecond to pass.
12a72f2
to
9594a1a
Compare
|
||
let creation_time = now_millis_since_epoch_start - created_millis_ago; | ||
UUIDBuilder::new().build_with_instant(creation_time) | ||
fn uuid_n_ms_in_past(n_ms_in_past: u64) -> UUID { |
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 we can add this as pub(crate) fn UUIDBuilder::build_with_instant(timestap) -> UUID
so that it can (potentially) be reused in other test cases of the up-rust crate?
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 could see adding a pub(crate)
function which does what I wrote here -- generates a UUID and then subtracts a certain amount of time from it.
I ran into issues with passing in an absolute timestamp. Read on to hear of my woes 👇
I was running into a fair amount of tricky debugging and headscratching before I moved away from the two-step process that was here of build()
-> build_with_timestamp()
, to calculating the timestamp purely in the loop of the compare-and-swap and not passing in the timestamp.
Imagine on thread 1 we had entered build_internal()
from build_with_timestamp()
with a timestamp of n and on thread 2 we had entered build_internal()
from build_with_timestamp() with a timestamp of n+1, and the previous time in the
msb` was n.
We make it to thread 2 first, see the msb
time of n which differs from the passed in timestamp of n+1 and set the timestamp to n+1.
We hit thread 1 second, see the msb
time of n+1 which differs from the passed in timestamp of n and set the timestamp to n (going back in time!).
If I missed the point though, please feel free to let me know your thoughts 🙂
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 could see adding a pub(crate) function which does what I wrote here -- generates a UUID and then subtracts a certain amount of time from it.
That is what I had in mind :-) Just didn't want to bury it in the test cases ...
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.
😆 Can you tell from my paragraph that I had gone through some trauma...
Sounds good, I'll move this over into UUIDBuilder
so it can be accessed from elsewhere for other tests 👍
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.
Okey dokey -- can you take another look?
src/uuid/uuidbuilder.rs
Outdated
pub(crate) const BITMASK_CLEAR_VERSION: u64 = 0xffff_ffff_ffff_0fff; | ||
const BITMASK_CLEAR_VARIANT: u64 = 0x3fff_ffff_ffff_ffff; |
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.
nice formatting 👍
…DBuilder to allow other tests access
aa99f08
to
ea89473
Compare
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
Thanks for contributing 👍
Addresses #58
What
Updated
UUIDBuilder
to expose onlybuild()
to allow end-users a simpler, more consistent API when generating UUIDs for their uE.Why
End users of the library should be shielded from making mistakes, which is what we do here. They simply call
UUIDBuilder::build()
and they'll always get the correct result.How
We use an internally held instance of
UUIDBuilder
which generates therand_b
portion once, meaning all calls tobuild()
have the samerand_b
in the UUIDs.We use a compare-and-swap (CAS) technique using an AtomicU64 to ensure we correctly handled logic for generating timestamp + counter portion, even in extremely high-contention scenarios when generating UUIDs. To do so we had to refactor so that we regenerate the timestamp at the top of the CAS loop, rather than be passed in the timestamp to use.
Mutex vs Atomic Compare-and-Swap (CAS) Performance
Using the Atomic CAS technique is approximately twice as fast as using a Mutex
With 10 async tasks, each generating 400 UUIDs all released and run at once