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

HHH-18377 Support for uuid v6 and v7 generated ids #8719

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

cigaly
Copy link
Contributor

@cigaly cigaly commented Jul 27, 2024

Jira issue HHH-18377

Implementation of UUID Versioin 6 and UUID Version 7


By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license
and can be relicensed under the terms of the LGPL v2.1 license in the future at the maintainers' discretion.
For more information on licensing, please check here.


@cigaly cigaly force-pushed the HHH-18377 branch 3 times, most recently from 5b0017a to 066a676 Compare July 29, 2024 15:28
@cigaly cigaly marked this pull request as ready for review July 29, 2024 16:21
@CaptainGlac1er
Copy link

How will it handle if a separate server generates the same id into the same database? Will it recreate a new ID automatically and try again?

@sebersole
Copy link
Member

How will it handle if a separate server generates the same id into the same database? Will it recreate a new ID automatically and try again?

No.

Copy link
Member

@sebersole sebersole left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a few comments about odd references to UUID#randomUUID(). Let's clean those up.

Additionally, you write great detail about what each value generator does. But you do it on #generateUuid. I'd prefer to see those on the class Javadoc, because...

Another thing we should do here is to add values for these into UuidGenerator.Style. Its probably best to have these link to the class Javadoc for the corresponding value-generator.

@cigaly cigaly force-pushed the HHH-18377 branch 2 times, most recently from 5134d12 to e44c59b Compare September 19, 2024 05:43
@sebersole
Copy link
Member

A few naming related points here...

  1. Not a fan of the enum names (UUIDV6, UUIDV7). How about VERSION6 and VERSION7 (or VERSION_6...)? Or since they are RFC compliant, RFC_6 and RFC_7?
  2. The value generator names should use {Type}Strategy pattern to match with the others. E.g. Version6Strategy.

I'll need to pull this locally to squash the commits before I apply. I can apply the name changes at that time if you prefer.

Thanks again for the work on this!

@cigaly
Copy link
Contributor Author

cigaly commented Sep 19, 2024

A few naming related points here...

1. Not a fan of the enum names (UUIDV6, UUIDV7).  How about VERSION6 and VERSION7 (or VERSION_6...)?  Or since they are RFC compliant, RFC_6 and RFC_7?

2. The value generator names should use `{Type}Strategy` pattern to match with the others.  E.g. `Version6Strategy`.

Changed both things.

I'll need to pull this locally to squash the commits before I apply. I can apply the name changes at that time if you prefer.

I've squashed everything into two commits, one for implementation, other for test case. Hope that this is OK?

Thanks again for the work on this!

You're welcome. Happy if I can help. Not that I was too busy lately.

@sebersole
Copy link
Member

sebersole commented Sep 19, 2024

In general, we like the test/impl split for the PR as it shows (1) what is addressed and (2) how it was addressed.

When we apply them, though, we generally (not always) squash to one as it makes it easier to cherry-pick to other branches. This we won't be backporting, so its less important. Long story short, 2 commits is fine here.

else if ( config.style() == VERSION_6 ) {
return new UuidVersion6Strategy();
}
else if ( config.style() == VERSION_7 ) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would a switch case make sense?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You certainly could. Personally I don't see the benefit here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if this will look any better than now ...

if ( config.algorithm() != UuidValueGenerator.class ) {
...
}
else {
    switch (config.style()) {
    ...
    }
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You won't need the else since the first if statement returns.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that is true, and was actually my fault from the first :)

It really could/should be:

if ( config != null ) {
    if ( config.algorithm() != UuidValueGenerator.class ) {
        // this block returns or throws an exception...
    }

    if ( config.style() == TIME ) {
        return ...
    }

    if ( config.style() == VERSION_6 ) {
        return UuidVersion6Strategy.INSTANCE;
    }

    if ( config.style() == VERSION_7 ) {
        return UuidVersion7Strategy.INSTANCE;
    }
}

return StandardRandomStrategy.INSTANCE;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or like

if(config == null) {
  return StandardRandomStrategy.INSTANCE;
}
if(config.algorithm() != UuidValueGenerator.class) {
  // throw or return logic
}

switch(config.style()) {
  case TIME:
    return ..
  case VERSION_6:
    return UuidVersion6Strategy.INSTANCE;
  case VERSION_7:
    return UuidVersion7Strategy.INSTANCE;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well no. You are missing what is effectively the AUTO, which also happens to be the config == null case.

I'm not following the desire to add a switch just to add a switch. A switch with so few case options is unuseful imo. In fact, IntelliJ will even suggest this as a refactoring.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well was trying to flatten the if statement and moved it up as a guard clause for the AUTO.
https://blog.codinghorror.com/flattening-arrow-code/

final long nanosPart = Math.round( ( currentTimestamp.getNano() % 1_000_000L ) * 0.004096 );

return new UUID(
millis << 16 & 0xFFFF_FFFF_FFFF_0000L | 0x7000L | nanosPart & 0xFFFL,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feel fine to ignore this comment, but would it make it more self documenting to save this mask to a constant?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wdyt @cigaly ?

Or at least some code comments?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would advise against code comments, as code comments tend to trend to lie as the project ages.
https://dev.to/ruben_alapont/magic-numbers-and-magic-strings-its-time-to-talk-about-it-ci2

Copy link
Member

@sebersole sebersole left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few minor suggestions/requests. Let me know what you think. FWIW I left "suggested changes" in those places to make it easy to apply if you agree.

* <li>14 bits - the clock sequence, resets to 0 when timestamp changes. </li>
* <li>48 bits - pseudorandom data to provide uniqueness.</li>
* </ul>
*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
*
*
* @apiNote Version 6 is field-compatible with Version 1, with the time bits reordered for improved DB locality.
*

*/
@Override
public int getGeneratedVersion() {
// UUIDv6 is a field-compatible version of UUIDv1, reordered for improved DB locality
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// UUIDv6 is a field-compatible version of UUIDv1, reordered for improved DB locality

Comment on lines +88 to +89
* @param session session
*
* @return UUID version 6
* @see UuidValueGenerator#generateUuid(SharedSessionContractImplementor)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @param session session
*
* @return UUID version 6
* @see UuidValueGenerator#generateUuid(SharedSessionContractImplementor)
* Generates a Version 6 compliant {@linkplain UUID} value.
*
* @return A Version 6 compliant {@linkplain UUID}.
*
* @see UuidValueGenerator#generateUuid

* <li>14 bits - counter to guarantee additional monotonicity, resets to 0 when timestamp changes. </li>
* <li>48 bits - pseudorandom data to provide uniqueness.</li>
* </ul>
*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
*
*
* @apiNote A Version 7 UUID features a time-ordered value field derived from the widely
* implemented and well known Unix Epoch timestamp source, the number of milliseconds
* since midnight 1 Jan 1970 UTC, leap seconds excluded.
*

Comment on lines +69 to +71
/*
* UUIDv7 features a time-ordered value field derived from the widely implemented and well-
* known Unix Epoch timestamp source, the number of milliseconds since midnight 1 Jan 1970 UTC,
* leap seconds excluded.
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/*
* UUIDv7 features a time-ordered value field derived from the widely implemented and well-
* known Unix Epoch timestamp source, the number of milliseconds since midnight 1 Jan 1970 UTC,
* leap seconds excluded.
*/

Comment on lines +86 to +87
* @param session session
*
* @return UUID version 7
* @see UuidValueGenerator#generateUuid(SharedSessionContractImplementor)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @param session session
*
* @return UUID version 7
* @see UuidValueGenerator#generateUuid(SharedSessionContractImplementor)
* Generates a Version 7 {@linkplain UUID}.
*
* @return A Version version 7 {@linkplain UUID}.
* @see UuidValueGenerator#generateUuid(SharedSessionContractImplementor)

@sebersole
Copy link
Member

Also, clearly we need to address the failures :)

Execution failed for task ':hibernate-core:compileTestJava'.
> Compilation failed; see the compiler error output for details.

@cigaly
Copy link
Contributor Author

cigaly commented Sep 19, 2024

Also, clearly we need to address the failures :)

Execution failed for task ':hibernate-core:compileTestJava'.
> Compilation failed; see the compiler error output for details.

One will expect that IDE should know to make basic refactoring things like renaming classes :-)

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

Successfully merging this pull request may close these issues.

3 participants