Skip to content

Instantly share code, notes, and snippets.

@AlexMAS
Created July 23, 2025 09:55
Show Gist options
  • Save AlexMAS/165d3ad3706c1806ce4b2eecaff0769c to your computer and use it in GitHub Desktop.
Save AlexMAS/165d3ad3706c1806ce4b2eecaff0769c to your computer and use it in GitHub Desktop.
How to create a unique integer sequence in a distributed system by using the Snowflake ID.
import java.time.Instant;
import static java.lang.System.currentTimeMillis;
public class SnowflakeID {
private static final byte DATACENTER_ID_BITS = 5;
private static final byte MACHINE_ID_BITS = 5;
private static final byte SEQUENCE_BITS = 12;
private static final byte TIMESTAMP_SHIFT = DATACENTER_ID_BITS + MACHINE_ID_BITS + SEQUENCE_BITS;
private static final byte DATACENTER_ID_SHIFT = MACHINE_ID_BITS + SEQUENCE_BITS;
private static final byte MACHINE_ID_SHIFT = SEQUENCE_BITS;
private static final long MAX_DATACENTER_ID = ~(-1L << DATACENTER_ID_BITS);
private static final long MAX_MACHINE_ID = ~(-1L << MACHINE_ID_BITS);
private static final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS);
private final long epochMillis;
private final long datacenterId;
private final long machineId;
private volatile long lastTimestamp;
private volatile long lastSequence;
public SnowflakeID(Instant epoch, short datacenterId, short machineId) {
if (datacenterId < 0 || datacenterId > MAX_DATACENTER_ID) {
throw new IllegalArgumentException(String.format("DatacenterID must be between 0 and %d.", MAX_DATACENTER_ID));
}
if (machineId < 0 || machineId > MAX_MACHINE_ID) {
throw new IllegalArgumentException(String.format("MachineID must be between 0 and %d.", MAX_MACHINE_ID));
}
this.epochMillis = epoch.toEpochMilli();
this.datacenterId = ((long) datacenterId << DATACENTER_ID_SHIFT);
this.machineId = ((long) machineId << MACHINE_ID_SHIFT);
this.lastTimestamp = -1;
this.lastSequence = -1;
}
public long next() {
var timestamp = currentTimeMillis();
if (timestamp < lastTimestamp) {
throw new IllegalStateException(String.format("Clock moved backwards. Refusing to generate ID for %d milliseconds.",
lastTimestamp - timestamp));
}
long sequence;
synchronized (this) {
if (timestamp == lastTimestamp) {
sequence = (lastSequence + 1) & SEQUENCE_MASK;
// Sequence overflow
if (sequence == 0) {
timestamp = waitForNextMillis(timestamp);
}
} else {
sequence = 0;
}
lastTimestamp = timestamp;
lastSequence = sequence;
}
return ((timestamp - epochMillis) << TIMESTAMP_SHIFT)
| datacenterId
| machineId
| sequence;
}
public Instant decodeTimestamp(long snowflakeId) {
return Instant.ofEpochMilli((snowflakeId >>> TIMESTAMP_SHIFT) + epochMillis);
}
public short decodeDatacenterId(long snowflakeId) {
return (short) ((snowflakeId >>> DATACENTER_ID_SHIFT) & MAX_DATACENTER_ID);
}
public short decodeMachineId(long snowflakeId) {
return (short) ((snowflakeId >>> MACHINE_ID_SHIFT) & MAX_MACHINE_ID);
}
private static long waitForNextMillis(long lastTimestamp) {
var timestamp = currentTimeMillis();
while (timestamp <= lastTimestamp) {
timestamp = currentTimeMillis();
}
return timestamp;
}
}
@AlexMAS
Copy link
Author

AlexMAS commented Jul 23, 2025

Usage example:

// The app instance settings
Instant appEpoch = Instant.EPOCH;
short datacenterId = 1;
short machineId = 1;

// At the app start time
var snowflakeID = new SnowflakeID(appEpoch, datacenterId, machineId);

// Generating IDs
var id1 = snowflakeID.next();
var id2 = snowflakeID.next();
var id3 = snowflakeID.next();

// Decoding IDs (if needed)
Instant id1Timestamp = snowflakeID.decodeTimestamp(id1);
short id1DatacenterId = snowflakeID.decodeDatacenterId(id1);
short id1MachineId = snowflakeID.decodeMachineId(id1);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment