Skip to content

Commit 0bab0a3

Browse files
committed
KEEP-0382: Describe changes introduced in Kotlin 2.3
- Update section about supported UUID versions - Mention `Uuid.parse*OrNull` functions
1 parent 08e1806 commit 0bab0a3

File tree

1 file changed

+52
-12
lines changed

1 file changed

+52
-12
lines changed

proposals/stdlib/KEEP-0382-uuid.md

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -102,19 +102,44 @@ we may introduce dedicated comparators to accommodate such use cases.
102102

103103
We have examined which UUID versions can be correctly generated within the stdlib and which versions are popular.
104104

105-
Correctly generating time-based UUIDs is not straightforward. Each new UUID must have a timestamp that is not earlier
106-
than the timestamp of the previously generated one. Handling clock rollbacks and system restarts would require storing
107-
the last generated UUID in stable storage, as described in the [RFC](https://www.rfc-editor.org/rfc/rfc9562.html#name-uuid-generator-states).
108-
We believe the Standard Library is not an appropriate place to implement such logic.
109-
Hence, it was decided not to implement the generation of time-based UUIDs.
105+
Some versions require functionality not available in the Standard library, or not available across all supported platforms.
110106

111-
The popularity of each UUID version was also explored. Our findings indicate that in approximately 90 percent of cases
112-
users generate a UUID version 4 (random). Excluding time-based UUIDs, this figure rises to over 97 percent.
107+
For example, UUID versions 2, 3, and 5 require support of `MD5` and `SHA-1` hash functions, which are currently unsupported.
108+
Correctly generating time-based UUIDs (versions 1, 6, 7) comes with its own set of challenges as proper generation have to
109+
guarantee monotonicity. Such a guarantee is hard to support without making different trade-offs.
113110

114-
Considering this, it was decided to initially provide an API only for generating version 4 (random) UUIDs.
115-
These UUIDs are produced using a cryptographically secure pseudorandom number generator (CSPRNG) available
116-
on the platform. For more details about the underlying APIs used to produce the random `Uuid` in each of the
117-
supported targets, refer to the official documentation of the `Uuid.random()` function.
111+
In terms of popularity, our findings indicate that in approximately 90 percent of cases users generate a UUID version 4 (random).
112+
Yet another UUID version gaining popularity, especially in domains relative to databases, is version 7.
113+
114+
Hence, it was decided to implement only UUID versions 4 and 7 generation for now, and consider providing generators
115+
for other versions based on demand and technical limitations we have.
116+
117+
Corresponding generator functions were named `Uuid.generateV4()` and `Uuid.generateV7()`.
118+
For convenience, a `Uuid.random()` function is also provided. `Uuid.random()` generates V4 UUID and is fully replaceable
119+
with `Uuid.generateV4()`, yet its name is more friendly to users who unfamiliar with UUID and only need to generate any
120+
random UUID.
121+
122+
Both UUID version 4 and 7 include a random part that [should be](https://www.rfc-editor.org/rfc/rfc9562.html#name-unguessability)
123+
generated using a cryptographically secure pseudorandom number generator (CSPRNG).
124+
Implementations provided in the Standard library utilize CSPRNGs available on a platform.
125+
For more details about the underlying APIs used to produce the random `Uuid` in each of the supported targets,
126+
refer to the official documentation of UUID generation functions.
127+
128+
UUID V7 is a time-based UUID, and as it was mentioned, correctly generating time-based UUIDs is not straightforward.
129+
Each new UUID must have a timestamp that is not earlier than the timestamp of the previously generated one.
130+
[RFC](https://www.rfc-editor.org/rfc/rfc9562.html#name-uuid-best-practices) describes multiple possible trade-offs in its
131+
"UUID Best Practices" section, and for UUID V7 generation we decided to guarantee a monotonicity of generated UUIDs
132+
within an application lifetime. Providing no monotonicity guarantees would compromise the whole point of UUID V7 existence,
133+
and providing monotonicity for a different scope would be hard or impossible.
134+
Please refer to `Uuid.generateV7()` documentation for additional details on monotonicity guarantee and how it is implemented.
135+
136+
With all that being said about version 7's monotonicity, there are scenarios requiring creation of such UUIDs corresponding
137+
for some fixed moment in time. For example, inserting events corresponding to past moments of time into a database.
138+
139+
To accommodate generation of such UUIDs we provided additional function - `Uuid.generateV7NonMonotonicAt(timestamp: Instant)`.
140+
As the name suggests, this function generates `Uuid` for a given `Instant`,
141+
and it does not provide any monotonicity guarantees (meaning that two `Uuid`s generated
142+
for the same `timestamp` value are not guaranteed to be ordered in any particular way).
118143

119144
#### UUID string formats the Kotlin Standard Library should parse and format to
120145

@@ -502,11 +527,26 @@ ByteArray.toHexString(format: HexFormat = HexFormat.Default): String
502527

503528
It was discovered that some UUID use-cases may require accepting any of the supported string representations.
504529
The `parse` function is aimed to fulfill these scenarios,
505-
and it will throw `IllegalStateException` only if a supplied string's format could not be recognized.
530+
and it will throw `IllegalArgumentException` only if a supplied string's format could not be recognized.
506531
If any new formats are supported in the future, `parse` will start accepting them as well.
507532
For scenarios where UUID string representations in an exact format are expected, specialized functions, such as
508533
`parseHexDash` and `parseHex`, should be used instead.
509534

535+
#### Invalid inputs handling
536+
537+
In some scenarios strings not matching a UUID format are expected (to some extent), and require specific handling.
538+
To aim with invalid inputs processing, all three `parse`-functions have counterparts returning either an
539+
`Uuid`, if the input string conformed a format, or `null` otherwise.
540+
541+
For consistency with other functions in the Standard library, these functions use `OrNull` suffix in their name:
542+
543+
| Function throwing `IllegalArgumentException` on invalid input | Function returning `null` on invalid input |
544+
|---------------------------------------------------------------|------------------------------------------------------|
545+
| `Uuid.parse(uuidString: String): Uuid` | `Uuid.parseOrNull(uuidString: String): Uuid?` |
546+
| `Uuid.parseHex(uuidString: String): Uuid` | `Uuid.parseHexOrNull(uuidString: String): Uuid?` |
547+
| `Uuid.parseHexDash(uuidString: String): Uuid` | `Uuid.parseHexDashOrNull(uuidString: String): Uuid?` |
548+
549+
510550
## Dependencies
511551

512552
The dependencies of the proposed API:

0 commit comments

Comments
 (0)