Willy,

I'm not particularly happy with out the patch turned out, but this is the
best I could come up with. If you are also unhappy with it, then please
consider this email to be a bug report and adjust my patch as necessary /
fix it yourself. Unfortunately I don't currently have the time to spend
further time on this. Of course if you are happy, then feel free to apply :-)

Best regards
Tim Düsterhus

Apply with `git am --scissors` to automatically cut the commit message.

-- >8 --
RFC 9562 specifies specifies that the top-most 48 bits of a UUIDv7 represent a
unix timestamp with millisecond resolution, allowing a user to extract the
timestamp. The remaining bits (except for version and variant) are left
unspecified. However it still requires that generated UUIDv7 are monotonic.

Section 6.2 describes several methods to generate monotonic UUIDs. This patch
employs "Method 3", filling the left-most bits "rnd_a" with increased clock
precision, guaranteeing monotonicity up to a 244ns resolution.

UUIDv7 was added with HAProxy 3.0, this patch should be backported there.
---
 src/tools.c | 40 ++++++++++++++++++++++++++++++----------
 1 file changed, 30 insertions(+), 10 deletions(-)

diff --git a/src/tools.c b/src/tools.c
index b297d046b4..420c647932 100644
--- a/src/tools.c
+++ b/src/tools.c
@@ -5778,24 +5778,44 @@ void ha_generate_uuid_v4(struct buffer *output)
  */
 void ha_generate_uuid_v7(struct buffer *output)
 {
-       uint32_t rnd[3];
-       uint64_t last;
+       static THREAD_LOCAL uint64_t nano_offset = 0;
+       static THREAD_LOCAL uint last_ms = 0;
+       uint32_t rnd_a;
+       uint64_t rnd_b;
        uint64_t time;
 
+ again:
        time = (date.tv_sec * 1000) + (date.tv_usec / 1000);
-       last = ha_random64();
-       rnd[0] = last;
-       rnd[1] = last >> 32;
+       if (last_ms != now_ms) {
+               last_ms = now_ms;
+               nano_offset = now_mono_time();
+               rnd_a = 0;
+       } else {
+               /* There are 1000000ns in a ms and we have 4096 bits available,
+                * thus we must map 244ns to a single value. Shifting by 8 bits
+                * is efficient and close to the value of 244. This results in
+                * a rnd_a value between 0 and 3906.
+                */
+               rnd_a = (now_mono_time() - nano_offset) >> 8;
 
-       last = ha_random64();
-       rnd[2] = last;
+               /* If rnd_a exceed 4000 then this means that 1ms passed without
+                * the date being updated in the event loop. Force an update to
+                * prevent an overflow.
+                */
+               if (unlikely(rnd_a > 4000)) {
+                       clock_update_date(0, 1);
+                       goto again;
+               }
+       }
+
+       rnd_b = ha_random64();
 
        chunk_printf(output, "%8.8x-%4.4x-%4.4x-%4.4x-%12.12llx",
                     (uint)(time >> 16u),
                     (uint)(time & 0xFFFF),
-                    ((rnd[0] >> 16u) & 0xFFF) | 0x7000,  // highest 4 bits 
indicate the uuid version
-                    (rnd[1] & 0x3FFF) | 0x8000,  // the highest 2 bits 
indicate the UUID variant (10),
-                    (long long)((rnd[1] >> 14u) | ((uint64_t) rnd[2] << 18u)) 
& 0xFFFFFFFFFFFFull);
+                    (rnd_a & 0xFFF) | 0x7000,  // highest 4 bits indicate the 
uuid version
+                    (uint)(rnd_b & 0x3FFF) | 0x8000,  // the highest 2 bits 
indicate the UUID variant (10),
+                    (long long)(rnd_b >> 16u) & 0xFFFFFFFFFFFFull);
 }
 
 
-- 
2.45.2


Reply via email to