Skip to content

Commit 63095f0

Browse files
committed
[KYUUBI #7277] Add UUID v7 generator
### Why are the changes needed? https://datatracker.ietf.org/doc/html/rfc9562#name-uuid-version-7 https://uuidv7.org/ > The Key Benefits of UUIDv7 UUIDv7 is rapidly gaining traction for various use cases across many industries. Here are some of the primary benefits: > > 1. Time-Ordered and Sortable: UUIDv7 encodes the time of creation as part of the identifier, making it naturally sortable. This is especially valuable for systems where data needs to be ordered by time without needing an additional timestamp field. > ... I think it's very useful - e.g., use UUIDv7 as session ID, then we can infer the session creation time from the UUID itself, or use UUIDv7 as a staging dir name, then we can easily clean dangling staging folders (for example, created 3 months ago) with a prefix. Implementation is inspired by apache/iceberg#14700 ### How was this patch tested? New UTs are added. ### Was this patch authored or co-authored using generative AI tooling? No. Closes #7277 from pan3793/uuidv7. Closes #7277 1dc1801 [Cheng Pan] style 3c7d3c5 [Cheng Pan] Add UUID v7 generator Authored-by: Cheng Pan <[email protected]> Signed-off-by: Cheng Pan <[email protected]>
1 parent 6d844ea commit 63095f0

File tree

2 files changed

+121
-0
lines changed

2 files changed

+121
-0
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.kyuubi.util;
21+
22+
import java.nio.ByteBuffer;
23+
import java.nio.ByteOrder;
24+
import java.security.SecureRandom;
25+
import java.util.UUID;
26+
27+
// Inspired by Apache Iceberg, see https://github.com/apache/iceberg/pull/14700 for details.
28+
public class UuidUtils {
29+
30+
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
31+
32+
public static UUID generateUUIDv7() {
33+
long epochMs = System.currentTimeMillis();
34+
return generateUUIDv7(epochMs);
35+
}
36+
37+
/**
38+
* Generate a RFC 9562 UUIDv7.
39+
*
40+
* <p>Layout: - 48-bit Unix epoch milliseconds - 4-bit version (0b0111) - 12-bit random (rand_a) -
41+
* 2-bit variant (RFC 4122, 0b10) - 62-bit random (rand_b)
42+
*
43+
* @param epochMs the number of milliseconds since midnight 1 Jan 1970 UTC, leap seconds excluded.
44+
* @return a {@code UUID} constructed using the given {@code epochMs}
45+
* @throws IllegalArgumentException if epochMs is negative or greater than {@code (1L << 48) - 1}
46+
*/
47+
public static UUID generateUUIDv7(long epochMs) {
48+
if ((epochMs >> 48) != 0) {
49+
throw new IllegalArgumentException(
50+
"Invalid timestamp: does not fit within 48 bits: " + epochMs);
51+
}
52+
// Draw 10 random bytes once: 2 bytes for rand_a (12 bits) and 8 bytes for rand_b (62 bits)
53+
byte[] randomBytes = new byte[10];
54+
SECURE_RANDOM.nextBytes(randomBytes);
55+
ByteBuffer rb = ByteBuffer.wrap(randomBytes).order(ByteOrder.BIG_ENDIAN);
56+
long randMSB = ((long) rb.getShort()) & 0x0FFFL; // 12 bits
57+
long randLSB = rb.getLong() & 0x3FFFFFFFFFFFFFFFL; // 62 bits
58+
59+
long msb = (epochMs << 16); // place timestamp in the top 48 bits
60+
msb |= 0x7000L; // version 7 (UUID bits 48..51)
61+
msb |= randMSB; // low 12 bits of MSB
62+
63+
long lsb = 0x8000000000000000L; // RFC 4122 variant '10'
64+
lsb |= randLSB;
65+
66+
return new UUID(msb, lsb);
67+
}
68+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.kyuubi.util;
21+
22+
import static org.junit.Assert.assertEquals;
23+
24+
import java.util.UUID;
25+
import org.junit.Test;
26+
27+
public class UuidUtilsTest {
28+
29+
@Test
30+
public void generateUUIDv7() {
31+
UUID uuid = UuidUtils.generateUUIDv7();
32+
assertEquals(7, uuid.version());
33+
assertEquals(2, uuid.variant());
34+
35+
// 48-bit long
36+
long value = 0xFEDCBA987654L;
37+
uuid = UuidUtils.generateUUIDv7(value);
38+
assertEquals(7, uuid.version());
39+
assertEquals(2, uuid.variant());
40+
}
41+
42+
@Test(expected = IllegalArgumentException.class)
43+
public void generateUUIDv7NegativeTimestamp() {
44+
long value = -0xFEDCBA987654L;
45+
UuidUtils.generateUUIDv7(value);
46+
}
47+
48+
@Test(expected = IllegalArgumentException.class)
49+
public void generateUUIDv7GreaterThan48BitsTimestamp() {
50+
long value = 1L << 48;
51+
UuidUtils.generateUUIDv7(value);
52+
}
53+
}

0 commit comments

Comments
 (0)