This is an automated email from the ASF dual-hosted git repository.
chengpan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/kyuubi.git
The following commit(s) were added to refs/heads/master by this push:
new 63095f0537 [KYUUBI #7277] Add UUID v7 generator
63095f0537 is described below
commit 63095f05372dd532e3d3c3b3841bf74fac7ad756
Author: Cheng Pan <[email protected]>
AuthorDate: Thu Dec 18 13:27:35 2025 +0800
[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 https://github.com/apache/iceberg/pull/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
1dc180141 [Cheng Pan] style
3c7d3c5e3 [Cheng Pan] Add UUID v7 generator
Authored-by: Cheng Pan <[email protected]>
Signed-off-by: Cheng Pan <[email protected]>
---
.../java/org/apache/kyuubi/util/UuidUtils.java | 68 ++++++++++++++++++++++
.../java/org/apache/kyuubi/util/UuidUtilsTest.java | 53 +++++++++++++++++
2 files changed, 121 insertions(+)
diff --git a/kyuubi-util/src/main/java/org/apache/kyuubi/util/UuidUtils.java
b/kyuubi-util/src/main/java/org/apache/kyuubi/util/UuidUtils.java
new file mode 100644
index 0000000000..6bd72d90da
--- /dev/null
+++ b/kyuubi-util/src/main/java/org/apache/kyuubi/util/UuidUtils.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.kyuubi.util;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.security.SecureRandom;
+import java.util.UUID;
+
+// Inspired by Apache Iceberg, see
https://github.com/apache/iceberg/pull/14700 for details.
+public class UuidUtils {
+
+ private static final SecureRandom SECURE_RANDOM = new SecureRandom();
+
+ public static UUID generateUUIDv7() {
+ long epochMs = System.currentTimeMillis();
+ return generateUUIDv7(epochMs);
+ }
+
+ /**
+ * Generate a RFC 9562 UUIDv7.
+ *
+ * <p>Layout: - 48-bit Unix epoch milliseconds - 4-bit version (0b0111) -
12-bit random (rand_a) -
+ * 2-bit variant (RFC 4122, 0b10) - 62-bit random (rand_b)
+ *
+ * @param epochMs the number of milliseconds since midnight 1 Jan 1970 UTC,
leap seconds excluded.
+ * @return a {@code UUID} constructed using the given {@code epochMs}
+ * @throws IllegalArgumentException if epochMs is negative or greater than
{@code (1L << 48) - 1}
+ */
+ public static UUID generateUUIDv7(long epochMs) {
+ if ((epochMs >> 48) != 0) {
+ throw new IllegalArgumentException(
+ "Invalid timestamp: does not fit within 48 bits: " + epochMs);
+ }
+ // Draw 10 random bytes once: 2 bytes for rand_a (12 bits) and 8 bytes for
rand_b (62 bits)
+ byte[] randomBytes = new byte[10];
+ SECURE_RANDOM.nextBytes(randomBytes);
+ ByteBuffer rb = ByteBuffer.wrap(randomBytes).order(ByteOrder.BIG_ENDIAN);
+ long randMSB = ((long) rb.getShort()) & 0x0FFFL; // 12 bits
+ long randLSB = rb.getLong() & 0x3FFFFFFFFFFFFFFFL; // 62 bits
+
+ long msb = (epochMs << 16); // place timestamp in the top 48 bits
+ msb |= 0x7000L; // version 7 (UUID bits 48..51)
+ msb |= randMSB; // low 12 bits of MSB
+
+ long lsb = 0x8000000000000000L; // RFC 4122 variant '10'
+ lsb |= randLSB;
+
+ return new UUID(msb, lsb);
+ }
+}
diff --git
a/kyuubi-util/src/test/java/org/apache/kyuubi/util/UuidUtilsTest.java
b/kyuubi-util/src/test/java/org/apache/kyuubi/util/UuidUtilsTest.java
new file mode 100644
index 0000000000..f97cf0e5c3
--- /dev/null
+++ b/kyuubi-util/src/test/java/org/apache/kyuubi/util/UuidUtilsTest.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.kyuubi.util;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.UUID;
+import org.junit.Test;
+
+public class UuidUtilsTest {
+
+ @Test
+ public void generateUUIDv7() {
+ UUID uuid = UuidUtils.generateUUIDv7();
+ assertEquals(7, uuid.version());
+ assertEquals(2, uuid.variant());
+
+ // 48-bit long
+ long value = 0xFEDCBA987654L;
+ uuid = UuidUtils.generateUUIDv7(value);
+ assertEquals(7, uuid.version());
+ assertEquals(2, uuid.variant());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void generateUUIDv7NegativeTimestamp() {
+ long value = -0xFEDCBA987654L;
+ UuidUtils.generateUUIDv7(value);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void generateUUIDv7GreaterThan48BitsTimestamp() {
+ long value = 1L << 48;
+ UuidUtils.generateUUIDv7(value);
+ }
+}