Reputation: 1532
I have the following code to generate unique eventId. This is not a pure GUID generator but a way to generate unique id across different machines/processes.
import java.util.concurrent.atomic.AtomicInteger
object EventIdGenerator extends Serializable {
val totalBits = 64
private val epochBits = 42
private val partitionIdBits = 10
private val sequenceBits = 12
private val sequenceInt = new AtomicInteger(0)
private val maxPartitionId = (Math.pow(2, partitionIdBits) - 1).toInt
private val maxSequenceId = (Math.pow(2, sequenceBits) - 1).toInt
def nextEventId(partitionId: Int): Long = {
assert(partitionId <= maxPartitionId)
val nextSequence = sequenceInt.incrementAndGet() % maxSequenceId
val timestamp = if (nextSequence == 0) {
Thread.sleep(1)
System.currentTimeMillis()
} else System.currentTimeMillis()
timestamp << (totalBits - epochBits) |
partitionId << (totalBits - epochBits - partitionIdBits) |
nextSequence
}
}
This is supposed to be called from a distributed program running over several JVMs in different machines. Here partition id is going to be unique across different JVMs and machine.
When I run this program in a single thread, it works fine. But when I run it in multiple threads where each thread calls nextEventId with a unique partition id, sometimes I get duplicate event Ids. I am not able to figure out what the problem is with the code below.
Upvotes: 1
Views: 166
Reputation: 4017
You have only 1024 different values for nextSequence
. So if you have request rate more than 1024 per millisecond (for 1 partition) you have ~100% chance of collision. In fact I believe that collision will appear with even much smaller rates because of clock precision.
I see that you tried to sidestep it with sleep(1)
if you detected an overflow of nextSequence
. But it doesn't work in a multithreaded environment. A particular partition may even never see nextSequence
equal to 0.
Upvotes: 1