MySQL Lists are EOL. Please join:

List:Commits« Previous MessageNext Message »
From:Tatiana A. Nurnberg Date:July 16 2008 4:21am
Subject:bzr commit into mysql-6.0 branch (azundris:2719) Bug#35848
View as plain text  
#At file:///misc/mysql/forest/35848/60-35848/

 2719 Tatiana A. Nurnberg	2008-07-16
      Bug#35848: UUID() returns UUIDs with the wrong time
      
      offset for time part in UUIDs was 1/1000 of what it
      should be. In other words, offset was off. (35848)
      
      Also handle the case where we count into the future
      when several UUIDs are generated in one "tick", and
      then the next call is late enough for us to unwind
      some but not all of those borrowed ticks.
      
      Lastly, handle the case where we keep borrowing and
      borrowing until the tick-counter overflows by also
      changing into a new "numberspace" by creating a new
      random suffix. (35514)
      
      Fix incorrect hyphenization of UUIDs. (38160, 6.0 only)
modified:
  mysql-test/r/func_misc.result
  mysql-test/t/func_misc.test
  mysys/my_uuid.c

per-file messages:
  mysql-test/r/func_misc.result
    Show that time-part of UUIDs is correct now.
  mysql-test/t/func_misc.test
    Show that time-part of UUIDs is correct now
    by replicating the C-code's resultin SQL.
    Results also decode to expect date-data on
    command-line (external validation).
    
    No test for unwinding of borrowed ticks as
    this a) is a race and b) depends on what timer
    we get.
  mysys/my_uuid.c
    correct offset for date/time-part of UUID. (35848)
    also make sure that when we counted into
    the future earlier (several UUIDs generated
    in same tick), we only give back as many
    "borrowed" ticks as we can without duplicating
    past timestamps. If our tick-counter overflows
    before we can give back, or if the system-clock
    is set back (by user or Daylight Saving Time),
    we create a new random suffix to avoid
    collisions and clear the tick-counter. (35514)
    Lastly, remove off-by-one in uuid2str() that
    resulted in hyphens being two nibbles to the
    right of where they should be. (38160)
=== modified file 'mysql-test/r/func_misc.result'
--- a/mysql-test/r/func_misc.result	2008-07-10 23:18:00 +0000
+++ b/mysql-test/r/func_misc.result	2008-07-16 04:21:38 +0000
@@ -310,6 +310,21 @@ drop table t1;
 SELECT NAME_CONST('var', 'value') COLLATE latin1_general_cs;
 NAME_CONST('var', 'value') COLLATE latin1_general_cs
 value
+select @@session.time_zone into @save_tz;
+set @@session.time_zone='UTC';
+select uuid() into @my_uuid;
+select mid(@my_uuid,15,1);
+mid(@my_uuid,15,1)
+1
+select 24 * 60 * 60 * 1000 * 1000 * 10 into @my_uuid_one_day;
+select concat('0',mid(@my_uuid,16,3),mid(@my_uuid,10,4),left(@my_uuid,8)) into
+@my_uuidate;
+select floor(conv(@my_uuidate,16,10)/@my_uuid_one_day) into @my_uuid_date;
+select 141427 + datediff(curdate(),'1970-01-01') into @my_uuid_synthetic;
+select @my_uuid_date - @my_uuid_synthetic;
+@my_uuid_date - @my_uuid_synthetic
+0
+set @@session.time_zone=@save_tz;
 End of 5.0 tests
 select connection_id() > 0;
 connection_id() > 0

=== modified file 'mysql-test/t/func_misc.test'
--- a/mysql-test/t/func_misc.test	2008-07-10 23:18:00 +0000
+++ b/mysql-test/t/func_misc.test	2008-07-16 04:21:38 +0000
@@ -426,6 +426,26 @@ drop table t1;
 #
 SELECT NAME_CONST('var', 'value') COLLATE latin1_general_cs;
 
+#
+# Bug #35848: UUID() returns UUIDs with the wrong time
+#
+select @@session.time_zone into @save_tz;
+
+# make sure all times are UTC so the DayNr won't differ
+set @@session.time_zone='UTC';
+select uuid() into @my_uuid;
+# if version nibble isn't 1, the following calculations will be rubbish
+select mid(@my_uuid,15,1);
+select 24 * 60 * 60 * 1000 * 1000 * 10 into @my_uuid_one_day;
+select concat('0',mid(@my_uuid,16,3),mid(@my_uuid,10,4),left(@my_uuid,8)) into
+@my_uuidate;
+select floor(conv(@my_uuidate,16,10)/@my_uuid_one_day) into @my_uuid_date;
+select 141427 + datediff(curdate(),'1970-01-01') into @my_uuid_synthetic;
+# these should be identical; date part of UUID should be current date
+select @my_uuid_date - @my_uuid_synthetic;
+
+set @@session.time_zone=@save_tz;
+
 --echo End of 5.0 tests
 
 #

=== modified file 'mysys/my_uuid.c'
--- a/mysys/my_uuid.c	2008-05-29 15:44:11 +0000
+++ b/mysys/my_uuid.c	2008-07-16 04:21:38 +0000
@@ -58,7 +58,8 @@ static pthread_mutex_t LOCK_uuid_generat
   1582-10-15 00:00:00.00 and 1970-01-01 00:00:00.00
 */
 
-#define UUID_TIME_OFFSET ((ulonglong) 141427 * 24 * 60 * 60 * 1000 * 10)
+#define UUID_TIME_OFFSET ((ulonglong) 141427 * 24 * 60 * 60 * \
+                          1000 * 1000 * 10)
 #define UUID_VERSION      0x1000
 #define UUID_VARIANT      0x8000
 
@@ -134,22 +135,63 @@ void my_uuid(uchar *to)
 
   pthread_mutex_lock(&LOCK_uuid_generator);
   tv= my_getsystime() + UUID_TIME_OFFSET + nanoseq;
-  if (unlikely(tv < uuid_time))
-    set_clock_seq();
-  else if (unlikely(tv == uuid_time))
+
+  if (likely(tv > uuid_time))
   {
-    /* special protection for low-res system clocks */
-    nanoseq++;
-    tv++;
+    /*
+      Current time is ahead of last timestamp, as it should be.
+      If we "borrowed time", give it back, just as long as we
+      stay ahead of the previous timestamp.
+    */
+    if (nanoseq)
+    {
+      DBUG_ASSERT((tv > uuid_time) && (nanoseq > 0));
+      /*
+        -1 so we won't make tv= uuid_time for nanoseq >= (tv - uuid_time)
+      */
+      long delta= min(nanoseq, tv - uuid_time -1);
+      tv-= delta;
+      nanoseq-= delta;
+    }
   }
   else
   {
-    if (nanoseq && likely(tv-nanoseq >= uuid_time))
+    if (unlikely(tv == uuid_time))
     {
-      tv-=nanoseq;
-      nanoseq=0;
+      /*
+        For low-res system clocks. If several requests for UUIDs
+        end up on the same tick, we add a nano-second to make them
+        different.
+        ( current_timestamp + nanoseq * calls_in_this_period )
+        may end up > next_timestamp; this is OK. Nonetheless, we'll
+        try to unwind nanoseq when we get a chance to.
+        If nanoseq overflows, we'll start over with a new numberspace
+        (so the if() below is needed so we can avoid the ++tv and thus
+        match the follow-up if() if nanoseq overflows!).
+      */
+      if (likely(++nanoseq))
+        ++tv;
+    }
+
+    if (unlikely(tv <= uuid_time))
+    {
+      /*
+        If the admin changes the system clock (or due to Daylight
+        Saving Time), the system clock may be turned *back* so we
+        go through a period once more for which we already gave out
+        UUIDs.  To avoid duplicate UUIDs despite potentially identical
+        times, we make a new random component.
+        We also come here if the nanoseq "borrowing" overflows.
+        In either case, we throw away any nanoseq borrowing since it's
+        irrelevant in the new numberspace.
+      */
+      set_clock_seq();
+      tv= my_getsystime() + UUID_TIME_OFFSET;
+      nanoseq= 0;
+      DBUG_PRINT("uuid",("making new numberspace"));
     }
   }
+
   uuid_time=tv;
   pthread_mutex_unlock(&LOCK_uuid_generator);
 
@@ -185,7 +227,7 @@ void my_uuid2str(const uchar *guid, char
   {
     *s++= _dig_vec_lower[guid[i] >>4];
     *s++= _dig_vec_lower[guid[i] & 15];
-    if(i == 4 || i == 6 || i == 8 || i == 10)
+    if(i == 3 || i == 5 || i == 7 || i == 9)
       *s++= '-';
   }
 }

Thread
bzr commit into mysql-6.0 branch (azundris:2719) Bug#35848Tatiana A. Nurnberg16 Jul