Do not lock when generating time based uuid (#52436)

Currently we lock when generating time based uuids. The lock is
implemented to prevent concurrent writes to the last timestamp. The uuid
generation is an area of contention when indexing. This commit modifies
the code to use atomic compare and set operations to update the last
timestamp.
This commit is contained in:
Tim Brooks 2020-02-18 08:09:07 -07:00
parent 123b3c6f55
commit 8038f9bba6
No known key found for this signature in database
GPG Key ID: C2AA3BB91A889E77

View File

@ -21,6 +21,7 @@ package org.elasticsearch.common;
import java.util.Base64; import java.util.Base64;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
/** /**
* These are essentially flake ids but we use 6 (not 8) bytes for timestamp, and use 3 (not 2) bytes for sequence number. We also reorder * These are essentially flake ids but we use 6 (not 8) bytes for timestamp, and use 3 (not 2) bytes for sequence number. We also reorder
@ -37,7 +38,7 @@ class TimeBasedUUIDGenerator implements UUIDGenerator {
private final AtomicInteger sequenceNumber = new AtomicInteger(SecureRandomHolder.INSTANCE.nextInt()); private final AtomicInteger sequenceNumber = new AtomicInteger(SecureRandomHolder.INSTANCE.nextInt());
// Used to ensure clock moves forward: // Used to ensure clock moves forward:
private long lastTimestamp; private final AtomicLong lastTimestamp = new AtomicLong(0);
private static final byte[] SECURE_MUNGED_ADDRESS = MacAddressProvider.getSecureMungedAddress(); private static final byte[] SECURE_MUNGED_ADDRESS = MacAddressProvider.getSecureMungedAddress();
@ -58,21 +59,22 @@ class TimeBasedUUIDGenerator implements UUIDGenerator {
@Override @Override
public String getBase64UUID() { public String getBase64UUID() {
final int sequenceId = sequenceNumber.incrementAndGet() & 0xffffff; final int sequenceId = sequenceNumber.incrementAndGet() & 0xffffff;
long timestamp = currentTimeMillis(); long currentTimeMillis = currentTimeMillis();
synchronized (this) { long timestamp = this.lastTimestamp.updateAndGet(lastTimestamp -> {
// Don't let timestamp go backwards, at least "on our watch" (while this JVM is running). We are still vulnerable if we are // Don't let timestamp go backwards, at least "on our watch" (while this JVM is running). We are
// shut down, clock goes backwards, and we restart... for this we randomize the sequenceNumber on init to decrease chance of // still vulnerable if we are shut down, clock goes backwards, and we restart... for this we
// collision: // randomize the sequenceNumber on init to decrease chance of collision:
timestamp = Math.max(lastTimestamp, timestamp); long nonBackwardsTimestamp = Math.max(lastTimestamp, currentTimeMillis);
if (sequenceId == 0) { if (sequenceId == 0) {
// Always force the clock to increment whenever sequence number is 0, in case we have a long time-slip backwards: // Always force the clock to increment whenever sequence number is 0, in case we have a long
timestamp++; // time-slip backwards:
nonBackwardsTimestamp++;
} }
lastTimestamp = timestamp; return nonBackwardsTimestamp;
} });
final byte[] uuidBytes = new byte[15]; final byte[] uuidBytes = new byte[15];
int i = 0; int i = 0;