5
5
package org .hibernate .id .uuid ;
6
6
7
7
import java .security .SecureRandom ;
8
- import java .time .Duration ;
9
8
import java .time .Instant ;
10
9
import java .time .LocalDate ;
11
10
import java .time .ZoneId ;
12
11
import java .util .UUID ;
13
- import java .util .concurrent .atomic .AtomicLong ;
14
- import java .util .concurrent .locks .Lock ;
15
- import java .util .concurrent .locks .ReentrantLock ;
12
+ import java .util .concurrent .atomic .AtomicReference ;
16
13
17
14
import org .hibernate .Internal ;
18
15
import org .hibernate .engine .spi .SharedSessionContractImplementor ;
31
28
* <li>48 bits - pseudorandom data to provide uniqueness.</li>
32
29
* </ul>
33
30
*
34
- * @apiNote This strategy is field-compatible with Version 1, with the time bits reordered for improved DB locality.
35
- *
36
31
* @author Cedomir Igaly
32
+ * @apiNote This strategy is field-compatible with Version 1, with the time bits reordered for improved DB locality.
37
33
*/
38
34
public class UuidVersion6Strategy implements UUIDGenerationStrategy , UuidValueGenerator {
39
35
public static final UuidVersion6Strategy INSTANCE = new UuidVersion6Strategy ();
40
36
41
37
private static class Holder {
42
38
static final SecureRandom numberGenerator = new SecureRandom ();
43
- static final Instant EPOCH_1582 = LocalDate .of ( 1582 , 10 , 15 )
39
+ static final long EPOCH_1582_SECONDS = LocalDate .of ( 1582 , 10 , 15 )
44
40
.atStartOfDay ( ZoneId .of ( "UTC" ) )
45
- .toInstant ();
41
+ .toInstant ().getEpochSecond ();
42
+
46
43
}
47
44
48
- private final Lock lock = new ReentrantLock ( true );
49
- private final AtomicLong clockSequence = new AtomicLong ( 0 );
50
- private long currentTimestamp ;
45
+ private record State (long timestamp , int sequence ) {
46
+ public State getNextState () {
47
+ final long now = instantToTimestamp ();
48
+ if ( this .timestamp < now ) {
49
+ return new State (
50
+ now ,
51
+ randomSequence ()
52
+ );
53
+ }
54
+ else if ( sequence == 0x3FFF ) {
55
+ return new State (
56
+ this .timestamp + 1 ,
57
+ randomSequence ()
58
+ );
59
+ }
60
+ else {
61
+ return new State ( timestamp , sequence + 1 );
62
+ }
63
+ }
51
64
65
+ private static int randomSequence () {
66
+ return Holder .numberGenerator .nextInt ( 1 << 14 );
67
+ }
68
+
69
+ private static long instantToTimestamp () {
70
+ final Instant instant = Instant .now ();
71
+ final long seconds = instant .getEpochSecond () - Holder .EPOCH_1582_SECONDS ;
72
+ return seconds * 10_000_000 + instant .getNano () / 100 ;
73
+ }
74
+ }
75
+
76
+ private final AtomicReference <State > lastState ;
52
77
53
78
@ Internal
54
79
public UuidVersion6Strategy () {
55
- this ( getCurrentTimestamp (), 0 );
80
+ this ( Long . MIN_VALUE , Integer . MIN_VALUE );
56
81
}
57
82
58
83
@ Internal
59
- public UuidVersion6Strategy (final long currentTimestamp , final long clockSequence ) {
60
- this .currentTimestamp = currentTimestamp ;
61
- this .clockSequence .set ( clockSequence );
84
+ public UuidVersion6Strategy (final long initialTimestamp , final int initialSequence ) {
85
+ this .lastState = new AtomicReference <>( new State ( initialTimestamp , initialSequence ) );
62
86
}
63
87
64
88
/**
@@ -70,47 +94,31 @@ public int getGeneratedVersion() {
70
94
}
71
95
72
96
@ Override
73
- public UUID generateUUID (SharedSessionContractImplementor session ) {
97
+ public UUID generateUUID (final SharedSessionContractImplementor session ) {
74
98
return generateUuid ( session );
75
99
}
76
100
77
101
@ Override
78
- public UUID generateUuid (SharedSessionContractImplementor session ) {
79
- final long currentTimestamp = getCurrentTimestamp ( );
102
+ public UUID generateUuid (final SharedSessionContractImplementor session ) {
103
+ final State state = lastState . updateAndGet ( State :: getNextState );
80
104
81
105
return new UUID (
82
106
// MSB bits 0-47 - most significant 32 bits of the 60-bit starting timestamp
83
- currentTimestamp << 4 & 0xFFFF_FFFF_FFFF_0000L
107
+ state . timestamp << 4 & 0xFFFF_FFFF_FFFF_0000L
84
108
// MSB bits 48-51 - version = 6
85
109
| 0x6000L
86
110
// MSB bits 52-63 - least significant 12 bits from the 60-bit starting timestamp
87
- | currentTimestamp & 0x0FFFL ,
111
+ | state . timestamp & 0x0FFFL ,
88
112
// LSB bits 0-1 - variant = 4
89
113
0x8000_0000_0000_0000L
90
114
// LSB bits 2-15 - clock sequence
91
- | ( getSequence ( currentTimestamp ) & 0x3FFFL ) << 48
92
- // LSB bits 16-63 - pseudorandom data
93
- | Holder . numberGenerator . nextLong () & 0xFFFF_FFFF_FFFFL
115
+ | ( state . sequence & 0x3FFFL ) << 48
116
+ // LSB bits 16-63 - pseudorandom data, least significant bit of the first octet is set to 1
117
+ | randomNode ()
94
118
);
95
119
}
96
120
97
-
98
- private long getSequence (final long currentTimestamp ) {
99
- lock .lock ();
100
- try {
101
- if ( this .currentTimestamp < currentTimestamp ) {
102
- this .currentTimestamp = currentTimestamp ;
103
- clockSequence .updateAndGet ( l -> l & 0x1FFFL );
104
- }
105
- }
106
- finally {
107
- lock .unlock ();
108
- }
109
- return clockSequence .getAndIncrement ();
110
- }
111
-
112
- private static long getCurrentTimestamp () {
113
- final Duration duration = Duration .between ( Holder .EPOCH_1582 , Instant .now () );
114
- return duration .toSeconds () * 10_000_000 + duration .toNanosPart () / 100 ;
121
+ private static long randomNode () {
122
+ return Holder .numberGenerator .nextLong ( 0x1_0000_0000_0000L ) | 0x1000_0000_0000L ;
115
123
}
116
124
}
0 commit comments