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.
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 descendingcurrentTimeMillis
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 usesAtomicLong
for 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
lastTime
andUUIDGen#createTime(...)
were static, this would not be a problem. In that case, I'd be able to instantiate oneUUIDGen
per migration thread, sorting my stream individually and then generating UUID using strictly monotonical input toUUIDGen#createTime(...)
.Proposed solution:
UUIDGen
, call itReusableUUIDGen
or something, that supports reuse with non-singleton state.UUIDGen
simply hold a singletonReusableUUIDGen
instance and proxy calls to it.UUIDGen#createTime(...)
theif
statement in thewhile
loop 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: