-
-
Notifications
You must be signed in to change notification settings - Fork 2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Core] Add faster UUID generator selectable through SPI (#2703)
The cucumber event bus UUID generator can now be configured through a SPI using a property from: - command-line - environment variables - system properties - `cucumber.properties` - `junit-platform.properties` - `@CucumberOptions` Two UUID generators are provided: An event has a UUID. The UUID generator can be configured using the `cucumber.uuid-generator` property: - name: io.cucumber.core.eventbus.RandomUuidGenerator* features: Thread-safe, collision-free, multi-jvm performance (Millions UUID/second): ~1 Typical usage example: Reports may be generated on different JVMs at the same time. A typical example would be one suite that tests against Firefox and another against Safari. The exact browser is configured through a property. These are then executed concurrently on different Gitlab runners. - name: io.cucumber.core.eventbus.IncrementingUuidGenerator features: Thread-safe, collision-free, single-jvm performance (Millions UUID/second): ~130 Typical usage example: Reports are generated on a single JVM | Performance on real projects depends on the size (e.g. on a project with 3095 Given, 445 When, 1177 Then, 445 Scenarios, 48 Rules, the `IncrementingUuidGenerator` improves the global performance by about 3-5% and the feature file parsing performance of about 37%). The `IncrementingUuidGenerator` is a very simple UUID generator based on `AtomicLong` counters. It is thread-safe and collision-free within a single JVM. The `RandomUuidGenerator` is the usual `UUID.randomUUID()` generator. Fixes: #2698
- Loading branch information
Showing
43 changed files
with
1,468 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
99 changes: 99 additions & 0 deletions
99
cucumber-core/src/main/java/io/cucumber/core/eventbus/IncrementingUuidGenerator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
package io.cucumber.core.eventbus; | ||
|
||
import io.cucumber.core.exception.CucumberException; | ||
|
||
import java.util.Random; | ||
import java.util.UUID; | ||
import java.util.concurrent.atomic.AtomicLong; | ||
|
||
/** | ||
* Thread-safe and collision-free UUID generator for single JVM. This is a | ||
* sequence generator and each instance has its own counter. This generator is | ||
* about 100 times faster than #RandomUuidGenerator. | ||
* | ||
* Properties: | ||
* - thread-safe | ||
* - collision-free in the same classloader | ||
* - almost collision-free in different classloaders / JVMs | ||
* - UUIDs generated using the instances from the same classloader are sortable | ||
* | ||
* UUID version 8 (custom) / variant 2 <a href= | ||
* "https://www.ietf.org/archive/id/draft-peabody-dispatch-new-uuid-format-04.html#name-uuid-version-8">...</a> | ||
* <!-- @formatter:off --> | ||
* | 40 bits | 8 bits | 4 bits | 12 bits | 2 bits | 62 bits | | ||
* | -------------------| -------------- | ------- | ------------- | ------- | ------- | | ||
* | LSBs of epoch-time | sessionCounter | version | classloaderId | variant | counter | | ||
* <!-- @formatter:on --> | ||
*/ | ||
public class IncrementingUuidGenerator implements UuidGenerator { | ||
/** | ||
* 40 bits mask for the epoch-time part (MSB). | ||
*/ | ||
private static final long MAX_EPOCH_TIME = 0x0ffffffffffL; | ||
|
||
/** | ||
* 8 bits mask for the session identifier (MSB). Package-private for testing | ||
* purposes. | ||
*/ | ||
static final long MAX_SESSION_ID = 0xffL; | ||
|
||
/** | ||
* 62 bits mask for the counter value (LSB) | ||
*/ | ||
static final long MAX_COUNTER_VALUE = 0x3fffffffffffffffL; | ||
|
||
/** | ||
* Classloader identifier (MSB). The identifier is a pseudo-random number on | ||
* 12 bits. Note: there is no need to save the Random because it's static. | ||
*/ | ||
@SuppressWarnings("java:S2119") | ||
static final long CLASSLOADER_ID = new Random().nextInt() & 0x0fff; | ||
|
||
/** | ||
* Session counter to differentiate instances created within a given | ||
* classloader (MSB). | ||
*/ | ||
static final AtomicLong sessionCounter = new AtomicLong(-1); | ||
|
||
/** | ||
* Computed UUID MSB value. | ||
*/ | ||
final long msb; | ||
|
||
/** | ||
* Counter for the UUID LSB. | ||
*/ | ||
final AtomicLong counter = new AtomicLong(-1); | ||
|
||
public IncrementingUuidGenerator() { | ||
long sessionId = sessionCounter.incrementAndGet(); | ||
if (sessionId == MAX_SESSION_ID) { | ||
throw new CucumberException( | ||
"Out of " + IncrementingUuidGenerator.class.getSimpleName() + | ||
" capacity. Please reuse existing instances or use another " + | ||
UuidGenerator.class.getSimpleName() + " implementation instead."); | ||
} | ||
long epochTime = System.currentTimeMillis(); | ||
// msb = epochTime | sessionId | version | classloaderId | ||
msb = ((epochTime & MAX_EPOCH_TIME) << 24) | (sessionId << 16) | (8 << 12) | CLASSLOADER_ID; | ||
} | ||
|
||
/** | ||
* Generate a new UUID. Will throw an exception when out of capacity. | ||
* | ||
* @return a non-null UUID | ||
* @throws CucumberException when out of capacity | ||
*/ | ||
@Override | ||
public UUID generateId() { | ||
long counterValue = counter.incrementAndGet(); | ||
if (counterValue == MAX_COUNTER_VALUE) { | ||
throw new CucumberException( | ||
"Out of " + IncrementingUuidGenerator.class.getSimpleName() + | ||
" capacity. Please generate using a new instance or use another " + | ||
UuidGenerator.class.getSimpleName() + "implementation."); | ||
} | ||
long leastSigBits = counterValue | 0x8000000000000000L; // set variant | ||
return new UUID(msb, leastSigBits); | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
cucumber-core/src/main/java/io/cucumber/core/eventbus/Options.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package io.cucumber.core.eventbus; | ||
|
||
public interface Options { | ||
|
||
Class<? extends UuidGenerator> getUuidGeneratorClass(); | ||
|
||
} |
14 changes: 14 additions & 0 deletions
14
cucumber-core/src/main/java/io/cucumber/core/eventbus/RandomUuidGenerator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package io.cucumber.core.eventbus; | ||
|
||
import java.util.UUID; | ||
|
||
/** | ||
* UUID generator based on random numbers. The generator is thread-safe and | ||
* supports multi-jvm usage of Cucumber. | ||
*/ | ||
public class RandomUuidGenerator implements UuidGenerator { | ||
@Override | ||
public UUID generateId() { | ||
return UUID.randomUUID(); | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
cucumber-core/src/main/java/io/cucumber/core/eventbus/UuidGenerator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package io.cucumber.core.eventbus; | ||
|
||
import org.apiguardian.api.API; | ||
|
||
import java.util.UUID; | ||
import java.util.function.Supplier; | ||
|
||
/** | ||
* SPI (Service Provider Interface) to generate UUIDs. | ||
*/ | ||
@API(status = API.Status.EXPERIMENTAL) | ||
public interface UuidGenerator extends Supplier<UUID> { | ||
UUID generateId(); | ||
|
||
default UUID get() { | ||
return generateId(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.