Skip to content
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

Correctness of clock sequence handling in uuid v1 generation #23

Closed
jvf opened this issue Nov 14, 2017 · 1 comment
Closed

Correctness of clock sequence handling in uuid v1 generation #23

jvf opened this issue Nov 14, 2017 · 1 comment

Comments

@jvf
Copy link

jvf commented Nov 14, 2017

RFC 4122 states:

If the clock is set backwards, or might have been set backwards
(e.g., while the system was powered off), and the UUID generator can
not be sure that no UUIDs were generated with timestamps larger than
the value to which the clock was set, then the clock sequence has to
be changed. If the previous value of the clock sequence is known, it
can just be incremented; otherwise it should be set to a random or
high-quality pseudo-random value.

RFC 4122 also states in 4.2.1. Basic Algorithm:

  • If the state was available, but the saved timestamp is later than
    the current timestamp, increment the clock sequence value.

However, in get_v1/1, the clock sequence is never altered. Incrementing the clock is implemented in increment/1, but increment/1 is never called during uuid v1 generation.

In case of a new timestamp being older than the last timestamp instead of adjusting the clock sequence (as intended by RFC 4122) the timestamp itself is altered in timestamp/2, at least for erlang_timestamp. It is obviously arguable what the "correct" timestamp is in such cases but in my opinion messing with the timestamp is not the correct answer, thats exactly what the clock sequence is for.

An example where messing with the timestamp is problematic: This library is used by the Erlang Cassandra driver cqerl. Cassandra in turn relies on extracting the time portion of the uuid v1 (timeuuid in Cassandra) as timestamp.

Both of the these behaviours do not seem to be correct in the sense of RFC 4122. Am I missing something in your implementation? Do I misunderstand the standard?

@okeuday
Copy link
Owner

okeuday commented Nov 15, 2017

@jvf Incrementing the clock sequence is something that must be done by source code that is aware of the clock going backwards, using a function call to uuid:increment/1 and that source code would need to be outside of the uuid source code to be aware of the clock going backwards (in a meaningful way).

The v1 UUIDs are created with a state variable that is first created with the uuid:new/2 function. In the Options variable, the uuid:new/2 function takes a timestamp_type value which defaults to erlang. This option represents the default behavior of the deprecated erlang:now/0 function which only provided incrementing time values, so the timestamp_type == erlang code path in the uuid:get_v1/1 function call will ensure the time is incrementing (by single microseconds if the clock moved backwards, using erlang:system_time(microsecond) (as shown here)). This is safe to use with the Multi-Time Warp Mode if you have a ntpd setup to keep the machine's time in-sync, which is a typical requirement for server deployments.

If instead you wanted the time to move backwards/forwards without the uuid:get_v1/1 function doing anything to alter the time value, it is possible to use timestamp_type == warp. You would then need to have an Erlang process that called erlang:monitor/2 with time_offset and use that event to call uuid:increment/1. However, I find it better to have the ordering guarantee with timestamp_type == erlang and avoid the need to call uuid:increment/1 while depending on ntpd to handle minor time changes.

You can also do timestamp_type == os but that basically means you don't care about the time changing (by getting the time directly from the hardware), so you probably wouldn't care about the uuid:increment/1 function if you were using that.

@okeuday okeuday closed this as completed Nov 16, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants