First off, eaio-uuid could possibly be the best Java UUID library out there. That said, I found a couple of somewhat related issues with it for my usecase:
Background: I have bunch of event streams that I need to migrate from one database to another. Each stream consists of events with auto increment IDs:
- 1: timestamp_1, data_1
- 2: timestamp_2, data_2
- ...
- n: timestamp_n, data_n
Since the new storage solution is more a distributed one I intend to migrate the auto increment IDs to UUIDs. The end goal will be:
- UUID1(timestamp_1): data_1
- UUID1(timestamp_2): data_2
- ...
- UUID1(timestamp_n): data_n
To be able to generate these UUIDs I had a look at UUIDGen#createTime(...) which at first looked like a perfect fit. However;
Problem: The UUIDGen#createTime(...) implementation only works for correctly for ascending consecutive input of currentTimeMillis. As soon as a descending currentTimeMillis is put in, the next time generated will be increased by 100 nanoseconds. That is a bug.
In my case this means that I need to sort all my events globally by timestamp to correctly generate UUIDs of type 1. Since my data is too big for a single instance to handle, this is impossible. Restarting the JVM for every stream :-) is neither an option.
While UUIDGen#createTime(...) is thread-safe per se (it uses AtomicLong for synchronization), a related problem is that of multiple threads calling UUIDGen#createTime(...). Since they generally will have a different notion of time (especially in my case where time is not Thread.currentTimeMillis()) the implementation is inherently broken. This boils down to singleton state;
If it wasn't for the fact that lastTime and UUIDGen#createTime(...) were static, this would not be a problem. In that case, I'd be able to instantiate one UUIDGen per migration thread, sorting my stream individually and then generating UUID using strictly monotonical input to UUIDGen#createTime(...).
Proposed solution:
- Make a non-static implementation of
UUIDGen, call it ReusableUUIDGen or something, that supports reuse with non-singleton state.
- Make the current
UUIDGen simply hold a singleton ReusableUUIDGen instance and proxy calls to it.
- Patch
UUIDGen#createTime(...) the if statement in the while loop to properly support decreasing input to the method.
- Document the fact that
UUIDGen#createTime(...) only works for consecutive larger input.
The above solution has the benefit of being a backward compatible implementation while still enabling reusing the UUIDGen.
Workaround:
- For the descending issue, the caller could make a
UUIDGen#createTime(0) call before calling the static method with a descending value.
- For the issue of global state, all input currently need to be strictly monotonically increasing.
Related:
First off, eaio-uuid could possibly be the best Java UUID library out there. That said, I found a couple of somewhat related issues with it for my usecase:
Background: I have bunch of event streams that I need to migrate from one database to another. Each stream consists of events with auto increment IDs:
Since the new storage solution is more a distributed one I intend to migrate the auto increment IDs to UUIDs. The end goal will be:
To be able to generate these UUIDs I had a look at
UUIDGen#createTime(...)which at first looked like a perfect fit. However;Problem: The
UUIDGen#createTime(...)implementation only works for correctly for ascending consecutive input ofcurrentTimeMillis. As soon as a descendingcurrentTimeMillisis put in, the next time generated will be increased by 100 nanoseconds. That is a bug.In my case this means that I need to sort all my events globally by timestamp to correctly generate UUIDs of type 1. Since my data is too big for a single instance to handle, this is impossible. Restarting the JVM for every stream :-) is neither an option.
While
UUIDGen#createTime(...)is thread-safe per se (it usesAtomicLongfor synchronization), a related problem is that of multiple threads callingUUIDGen#createTime(...). Since they generally will have a different notion of time (especially in my case where time is notThread.currentTimeMillis()) the implementation is inherently broken. This boils down to singleton state;If it wasn't for the fact that
lastTimeandUUIDGen#createTime(...)were static, this would not be a problem. In that case, I'd be able to instantiate oneUUIDGenper migration thread, sorting my stream individually and then generating UUID using strictly monotonical input toUUIDGen#createTime(...).Proposed solution:
UUIDGen, call itReusableUUIDGenor something, that supports reuse with non-singleton state.UUIDGensimply hold a singletonReusableUUIDGeninstance and proxy calls to it.UUIDGen#createTime(...)theifstatement in thewhileloop to properly support decreasing input to the method.UUIDGen#createTime(...)only works for consecutive larger input.The above solution has the benefit of being a backward compatible implementation while still enabling reusing the
UUIDGen.Workaround:
UUIDGen#createTime(0)call before calling the static method with a descending value.Related: