1
+ package com .scriptql .scriptqlapi .utils ;
2
+
3
+ import java .net .NetworkInterface ;
4
+ import java .security .SecureRandom ;
5
+ import java .time .Instant ;
6
+ import java .util .Enumeration ;
7
+
8
+ /**
9
+ * Distributed Sequence Generator.
10
+ * Inspired by Twitter snowflake: https://github.com/twitter/snowflake/tree/snowflake-2010
11
+ * <p>
12
+ * Modified from https://github.com/callicoder/java-snowflake
13
+ * <p>
14
+ * This class should be used as a Singleton.
15
+ * Make sure that you create and reuse a Single instance of Snowflake per node in your distributed system cluster.
16
+ */
17
+ public class Snowflake {
18
+
19
+ // (2^42)-1 = 4398046511103 + EPOCH = Will die on Thu May 15 2160 07:35:11Z
20
+ private static final int EPOCH_BITS = 42 ;
21
+ // Max 1024 Nodes
22
+ private static final int NODE_ID_BITS = 10 ;
23
+ // Max 4096 Increments per Millis
24
+ private static final int SEQUENCE_BITS = 12 ;
25
+
26
+ private static final long maxNodeId = (1L << NODE_ID_BITS ) - 1 ;
27
+ private static final long maxSequence = (1L << SEQUENCE_BITS ) - 1 ;
28
+
29
+ // Custom Epoch (January 1, 2021 Midnight UTC = 2021-01-01T00:00:00Z)
30
+ private static final long DEFAULT_CUSTOM_EPOCH = 1609459200000L ;
31
+
32
+ private final long nodeId ;
33
+ private final long customEpoch ;
34
+
35
+ private volatile long lastTimestamp = -1L ;
36
+ private volatile long sequence = 0L ;
37
+
38
+ // Let Snowflake generate a nodeId
39
+ public Snowflake () {
40
+ this .nodeId = createNodeId ();
41
+ this .customEpoch = DEFAULT_CUSTOM_EPOCH ;
42
+ }
43
+
44
+ public synchronized long next () {
45
+ long currentTimestamp = timestamp ();
46
+ if (currentTimestamp < lastTimestamp ) {
47
+ throw new IllegalStateException ("Invalid System Clock!" );
48
+ }
49
+ if (currentTimestamp == lastTimestamp ) {
50
+ sequence = (sequence + 1 ) & maxSequence ;
51
+ if (sequence == 0 ) {
52
+ // Sequence Exhausted, wait till next millisecond.
53
+ currentTimestamp = waitNextMillis (currentTimestamp );
54
+ }
55
+ } else {
56
+ // reset sequence to start with zero for the next millisecond
57
+ sequence = 0 ;
58
+ }
59
+ lastTimestamp = currentTimestamp ;
60
+ return currentTimestamp << (NODE_ID_BITS + SEQUENCE_BITS )
61
+ | (nodeId << SEQUENCE_BITS )
62
+ | sequence ;
63
+ }
64
+
65
+ // Get current timestamp in milliseconds, adjust for the custom epoch.
66
+ private long timestamp () {
67
+ return Instant .now ().toEpochMilli () - customEpoch ;
68
+ }
69
+
70
+ // Block and wait till next millisecond
71
+ private long waitNextMillis (long currentTimestamp ) {
72
+ while (currentTimestamp == lastTimestamp ) {
73
+ currentTimestamp = timestamp ();
74
+ }
75
+ return currentTimestamp ;
76
+ }
77
+
78
+ private long createNodeId () {
79
+ long nodeId ;
80
+ try {
81
+ StringBuilder sb = new StringBuilder ();
82
+ Enumeration <NetworkInterface > networkInterfaces = NetworkInterface .getNetworkInterfaces ();
83
+ while (networkInterfaces .hasMoreElements ()) {
84
+ NetworkInterface networkInterface = networkInterfaces .nextElement ();
85
+ byte [] mac = networkInterface .getHardwareAddress ();
86
+ if (mac != null ) {
87
+ for (byte macPort : mac ) {
88
+ sb .append (String .format ("%02X" , macPort ));
89
+ }
90
+ }
91
+ }
92
+ nodeId = sb .toString ().hashCode ();
93
+ } catch (Exception ex ) {
94
+ nodeId = (new SecureRandom ().nextInt ());
95
+ }
96
+ nodeId = nodeId & maxNodeId ;
97
+ return nodeId ;
98
+ }
99
+
100
+ public long [] parse (long id ) {
101
+ long maskNodeId = ((1L << NODE_ID_BITS ) - 1 ) << SEQUENCE_BITS ;
102
+ long maskSequence = (1L << SEQUENCE_BITS ) - 1 ;
103
+
104
+ long timestamp = (id >> (NODE_ID_BITS + SEQUENCE_BITS )) + customEpoch ;
105
+ long nodeId = (id & maskNodeId ) >> SEQUENCE_BITS ;
106
+ long sequence = id & maskSequence ;
107
+
108
+ return new long []{timestamp , nodeId , sequence };
109
+ }
110
+
111
+ @ Override
112
+ public String toString () {
113
+ return "Snowflake Settings [EPOCH_BITS=" + EPOCH_BITS + ", NODE_ID_BITS=" + NODE_ID_BITS
114
+ + ", SEQUENCE_BITS=" + SEQUENCE_BITS + ", CUSTOM_EPOCH=" + customEpoch
115
+ + ", NodeId=" + nodeId + "]" ;
116
+ }
117
+
118
+ }
0 commit comments