Skip to content

Conversation

Copy link

Copilot AI commented Nov 9, 2025

What changed

Added UUIDGenerator utility class with two UUID generation strategies:

  • UUIDv7 (default): Backported from Java 26 (JDK-8334015) with 48-bit millisecond timestamp, version/variant bits, and cryptographically strong random data
  • Unique-Then-Time: Custom format with 64-bit unique MSB + 44-bit time component + 20-bit random LSB

Configuration via system property jdk.sandbox.uuid.generator.mode with values v7 or unique-then-time. Static initialization ensures thread-safety and single configuration point for both Android and server environments.

// Default (V7)
UUID uuid = UUIDGenerator.generateUUID();

// Explicit UUIDv7 with timestamp
UUID uuid = UUIDGenerator.ofEpochMillis(System.currentTimeMillis());

// Unique-then-time mode
UUID uuid = UUIDGenerator.uniqueThenTime(uniqueMsb);

// Configuration
java -Djdk.sandbox.uuid.generator.mode=unique-then-time -jar app.jar

Why this change is needed

Provides time-ordered UUID generation (UUIDv7) for database efficiency and distributed systems, with fallback to alternative generation strategy when needed. System property configuration supports both server startup flags and Android Application.onCreate() initialization patterns.

How were these changes tested

  • 19 unit tests covering UUIDv7 generation, timestamp validation, monotonicity, thread safety (10 threads × 100 UUIDs), and unique-then-time generation
  • 2 configuration tests verifying mode detection and consistency
  • Demo application validated in both V7 and unique-then-time modes via system property
  • CodeQL security scan: 0 vulnerabilities

Checklist

  • Code builds / passes tests (107 tests passing)
  • New tests added if needed
  • Update to use CODING_STYLE_LLM.md conventions (JEP 467 Markdown docs, exhaustive switch expressions, final vars, lambda logging)
  • Documentation updated if needed (Demo + usage examples)
  • AGENTS.md updated if appropriate (N/A - utility addition)
Original prompt

upgrade the current UUIDGenerator to use this logic backported from java26 ```
/// Creates a type 7 UUID (UUIDv7) {@code UUID} from the given Unix Epoch timestamp.
///
/// The returned {@code UUID} will have the given {@code timestamp} in
/// the first 6 bytes, followed by the version and variant bits representing {@code UUIDv7},
/// and the remaining bytes will contain random data from a cryptographically strong
/// pseudo-random number generator.
///
/// @APinote {@code UUIDv7} values are created by allocating a Unix timestamp in milliseconds
/// in the most significant 48 bits, allocating the required version (4 bits) and variant (2-bits)
/// and filling the remaining 74 bits with random bits. As such, this method rejects {@code timestamp}
/// values that do not fit into 48 bits.
///


/// Monotonicity (each subsequent value being greater than the last) is a primary characteristic
/// of {@code UUIDv7} values. This is due to the {@code timestamp} value being part of the {@code UUID}.
/// Callers of this method that wish to generate monotonic {@code UUIDv7} values are expected to
/// ensure that the given {@code timestamp} value is monotonic.
///
/// @param timestamp the number of milliseconds since midnight 1 Jan 1970 UTC,
/// leap seconds excluded.
///
/// @return a {@code UUID} constructed using the given {@code timestamp}
///
/// @throws IllegalArgumentException if the timestamp is negative or greater than {@code (1L << 48) - 1}
///
/// @SInCE Backport from Java 26 (JDK-8334015)
public static UUID ofEpochMillis(long timestamp) {
if ((timestamp >> 48) != 0) {
throw new IllegalArgumentException("Supplied timestamp: " + timestamp + " does not fit within 48 bits");
}

    byte[] randomBytes = new byte[16];
    LazyRandom.RANDOM.nextBytes(randomBytes);

    // Embed the timestamp into the first 6 bytes
    randomBytes[0] = (byte)(timestamp >> 40);
    randomBytes[1] = (byte)(timestamp >> 32);
    randomBytes[2] = (byte)(timestamp >> 24);
    randomBytes[3] = (byte)(timestamp >> 16);
    randomBytes[4] = (byte)(timestamp >> 8);
    randomBytes[5] = (byte)(timestamp);

    // Set version to 7
    randomBytes[6] &= 0x0f;
    randomBytes[6] |= 0x70;

    // Set variant to IETF
    randomBytes[8] &= 0x3f;
    randomBytes[8] |= (byte) 0x80;

    // Convert byte array to UUID using ByteBuffer
    ByteBuffer buffer = ByteBuffer.wrap(randomBytes);
    long msb = buffer.getLong();
    long lsb = buffer.getLong();
    return new UUID(msb, lsb);
}
```

then also add in this alternative optional way:

    /// ┌──────────────────────────────────────────────────────────────────────────────┐
    /// │  unique  (64 bits)  │  time+counter  (44 bits)  │  random  (20 bits)        │
    /// └──────────────────────────────────────────────────────────────────────────────┘
    public static UUID uniqueThenTime(long uniqueMsb) {
        final int timeBits = 44;
        final int randomBits = 20;
        final int randomMask = (1 << randomBits) - 1;
        long timeCounter = timeCounterBits();
        long msb = uniqueMsb;
        // Take the most significant 44 bits of timeCounter to preserve time ordering
        long timeComponent = timeCounter >> (64 - timeBits); // timeBits is 44
        long lsb = (timeComponent << randomBits) | (getRandom().nextInt() & randomMask);
        return new UUID(msb, lsb);
    }

it would be best current public static UUID generateUUID() can default to using the UUIDv7 approach yet have options to set it up be configurable at startup such as providing a system property to be able to flip it to the other method. have a think about how that is best done on both android and servers.


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI changed the title [WIP] Upgrade UUIDGenerator to implement UUIDv7 logic Add UUIDGenerator with UUIDv7 and configurable generation modes Nov 9, 2025
Copilot AI requested a review from simbo1905 November 9, 2025 07:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants