Skip to content

Singleton state and only forward lapse support #5

@JensRantil

Description

@JensRantil

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:

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions