HHH-18947 UUID v7 - Use randomized counter instead of counter + random
This will add approximately ten bits of entropy to generated UUID, while preserving uniqueness and monotonicity
This commit is contained in:
parent
57c0b26d86
commit
2215d2b9bf
|
@ -13,7 +13,6 @@ import org.hibernate.Internal;
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
import org.hibernate.id.UUIDGenerationStrategy;
|
import org.hibernate.id.UUIDGenerationStrategy;
|
||||||
|
|
||||||
import static java.time.temporal.ChronoUnit.MILLIS;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements UUID Version 7 generation strategy as defined by the <a href="https://datatracker.ietf.org/doc/html/rfc9562#name-uuid-version-7">RFC 9562</a>.
|
* Implements UUID Version 7 generation strategy as defined by the <a href="https://datatracker.ietf.org/doc/html/rfc9562#name-uuid-version-7">RFC 9562</a>.
|
||||||
|
@ -26,8 +25,7 @@ import static java.time.temporal.ChronoUnit.MILLIS;
|
||||||
* to guarantee additional monotonicity.
|
* to guarantee additional monotonicity.
|
||||||
* </li>
|
* </li>
|
||||||
* <li>2 bits - variant field, set to 0b10.</li>
|
* <li>2 bits - variant field, set to 0b10.</li>
|
||||||
* <li>14 bits - counter to guarantee additional monotonicity, resets to 0 when timestamp changes. </li>
|
* <li>62 bits - pseudorandom counter to guarantee additional monotonicity, uniqueness, and good entropy.</li>
|
||||||
* <li>48 bits - pseudorandom data to provide uniqueness.</li>
|
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @author Cedomir Igaly
|
* @author Cedomir Igaly
|
||||||
|
@ -37,6 +35,8 @@ import static java.time.temporal.ChronoUnit.MILLIS;
|
||||||
*/
|
*/
|
||||||
public class UuidVersion7Strategy implements UUIDGenerationStrategy, UuidValueGenerator {
|
public class UuidVersion7Strategy implements UUIDGenerationStrategy, UuidValueGenerator {
|
||||||
|
|
||||||
|
private static final long MAX_RANDOM_SEQUENCE = 0x3FFF_FFFF_FFFF_FFFFL;
|
||||||
|
|
||||||
public static final UuidVersion7Strategy INSTANCE = new UuidVersion7Strategy();
|
public static final UuidVersion7Strategy INSTANCE = new UuidVersion7Strategy();
|
||||||
|
|
||||||
private static class Holder {
|
private static class Holder {
|
||||||
|
@ -44,36 +44,37 @@ public class UuidVersion7Strategy implements UUIDGenerationStrategy, UuidValueGe
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public record State(Instant lastTimestamp, int lastSequence) {
|
public record State(Instant lastTimestamp, long lastSequence, long nanos) {
|
||||||
|
|
||||||
|
State(Instant lastTimestamp, long lastSequence) {
|
||||||
|
this( lastTimestamp, lastSequence, nanos( lastTimestamp ) );
|
||||||
|
}
|
||||||
|
|
||||||
public long millis() {
|
public long millis() {
|
||||||
return lastTimestamp.toEpochMilli();
|
return lastTimestamp.toEpochMilli();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long nanos() {
|
private static long nanos(Instant timestamp) {
|
||||||
return (long) ( ( lastTimestamp.getNano() % 1_000_000L ) * 0.004096 );
|
return (long) ((timestamp.getNano() % 1_000_000L) * 0.004096);
|
||||||
}
|
}
|
||||||
|
|
||||||
public State getNextState() {
|
public State getNextState() {
|
||||||
final Instant now = Instant.now();
|
final Instant now = Instant.now();
|
||||||
if ( lastTimestamp.toEpochMilli() < now.toEpochMilli() ) {
|
if ( lastTimestamp.toEpochMilli() < now.toEpochMilli() ||
|
||||||
return new State(
|
lastTimestamp.toEpochMilli() == now.toEpochMilli() && nanos < nanos( now ) ) {
|
||||||
now.truncatedTo( MILLIS ),
|
return new State( now, randomSequence() );
|
||||||
randomSequence()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
else if ( lastSequence == 0x3FFF ) {
|
final long nextSequence = lastSequence + Holder.numberGenerator.nextLong( 0xFFFF_FFFFL );
|
||||||
return new State(
|
if ( nextSequence > MAX_RANDOM_SEQUENCE ) {
|
||||||
lastTimestamp.plusMillis( 1 ),
|
return new State( lastTimestamp.plusNanos( 250 ), randomSequence() );
|
||||||
Holder.numberGenerator.nextInt( 1 << 14 )
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return new State( lastTimestamp, lastSequence + 1 );
|
return new State( lastTimestamp, nextSequence );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int randomSequence() {
|
private static long randomSequence() {
|
||||||
return Holder.numberGenerator.nextInt( 1 << 14 );
|
return Holder.numberGenerator.nextLong( MAX_RANDOM_SEQUENCE );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,11 +82,11 @@ public class UuidVersion7Strategy implements UUIDGenerationStrategy, UuidValueGe
|
||||||
|
|
||||||
@Internal
|
@Internal
|
||||||
public UuidVersion7Strategy() {
|
public UuidVersion7Strategy() {
|
||||||
this( Instant.EPOCH, Integer.MIN_VALUE );
|
this( Instant.EPOCH, Holder.numberGenerator.nextLong( MAX_RANDOM_SEQUENCE ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Internal
|
@Internal
|
||||||
public UuidVersion7Strategy(final Instant initialTimestamp, final int initialSequence) {
|
public UuidVersion7Strategy(final Instant initialTimestamp, final long initialSequence) {
|
||||||
this.lastState = new AtomicReference<>( new State( initialTimestamp, initialSequence ) );
|
this.lastState = new AtomicReference<>( new State( initialTimestamp, initialSequence ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,14 +116,8 @@ public class UuidVersion7Strategy implements UUIDGenerationStrategy, UuidValueGe
|
||||||
| state.nanos() & 0xFFFL,
|
| state.nanos() & 0xFFFL,
|
||||||
// LSB bits 0-1 - variant = 4
|
// LSB bits 0-1 - variant = 4
|
||||||
0x8000_0000_0000_0000L
|
0x8000_0000_0000_0000L
|
||||||
// LSB bits 2-15 - counter
|
// LSB bits 2-15 - pseudorandom counter
|
||||||
| (long) state.lastSequence << 48
|
| state.lastSequence
|
||||||
// LSB bits 16-63 - pseudorandom data
|
|
||||||
| randomNode()
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long randomNode() {
|
|
||||||
return Holder.numberGenerator.nextLong( 0x1_0000_0000_0000L ) | 0x1000_0000_0000L;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue