Ayende @ Rahien

It's a girl

CreateSequetialUuid – Answer

I got a lot of interesting answers for the riddle, and here is my solution:

private static int sequentialUuidCounter;
public static Guid CreateSequentialUuid()
{
    var ticksAsBytes = BitConverter.GetBytes(DateTime.Now.Ticks);
    Array.Reverse(ticksAsBytes);
    var increment = Interlocked.Increment(ref sequentialUuidCounter);
    var currentAsBytes = BitConverter.GetBytes(increment);
    Array.Reverse(currentAsBytes);
    var bytes = new byte[16];
    Array.Copy(ticksAsBytes, 0, bytes, 0, ticksAsBytes.Length);
    Array.Copy(currentAsBytes, 0, bytes, 12, currentAsBytes.Length);
    return bytes.TransfromToGuidWithProperSorting();
}

Basically, we use the current system ticks as the 1 – 8 bytes, and a counter incremented atomically on the 12 – 16 bytes. This ensures that even concurrent calls on the same tick will have a different value.

Note that this code explicitly allows the same guid on multiple machines. You can fix that by adding the MAC address as 9 – 12 bytes, which will make this globally unique, but this isn’t something that I actually need.

Additional challenge, why am I reversing the bytes?

Comments

Daniel Grunwald
09/28/2010 10:28 AM by
Daniel Grunwald

You are reversing the bytes because you want a big-endian representation of the numbers so that the GUIDs sort the same way as the numbers.

BitConverter returns little-endian on all architectures where MS.NET (currently) runs, so you need to reverse the bytes.

However, for portability, I would write:

if (BitConverter.IsLittleEndian) Array.Reverse(...);

Peter Morris
09/28/2010 11:17 AM by
Peter Morris

You want the most significant data on the left so that index entries are always added to the end of the index rather than having to be inserted?

tobi
09/28/2010 11:36 AM by
tobi

There is a nasty bug in here: When network time sync kicks in the clock can jump back a bit. Then the guids are not sequential anymore. You can switch to recording DT.UtcNow on app startup and then adding Environment.TickCount to it (that will wrap every 40 days however).

Ken Egozi
09/28/2010 01:17 PM by
Ken Egozi

Yup, DateTime.UtcNow is probably a better choice than DateTime.Now

tobi
09/28/2010 01:23 PM by
tobi

Ken: I did not even see that. Your database will have a hiccup twice a year because of daylight savings^^

James_2JS
09/28/2010 04:25 PM by
James_2JS

@tobi: Actually, it would only be a hiccup once per year, as clocks going forward to daylight savings time won't have any affect... it's just when the clocks go back that you get an issue! :O)

Mark J. Miller
09/28/2010 06:50 PM by
Mark J. Miller

I had been writing a Comb class when you first posted about this and pointed out the Sql Server sorting tidbit. I had started a post a day or two ago (honest) to show my version. Since you posted this one, I thought I'd just throw mine up real quick for comparison. www.developmentalmadness.com/.../...s-in-.net.aspx

I can't say I know why you're reversing your byte arrays though. As far as SQL Server is concerned I know from your last post on this topic that you specifically mentioned that and your means of fixing the issue (which I copied). But I know you're doing this for RavenDb, however from your "make this code run faster" post it looked like you were transforming values back and forth between Sql Server and Ascent sorting - could that have something to do with it?

Michael Morton
09/28/2010 08:21 PM by
Michael Morton

@James_2JS: Some places do not observe DST.

Ayende Rahien
09/29/2010 10:05 AM by
Ayende Rahien

Tobi,

Ticks aren't local time, they are time from known location in the past. So it doesn't matter about UTC.

TickCount would mean that I can only work for ~41 days or so, which is bad.

I am willing to leave with the issue of the clock sync, though. The current time would change as well, which is fine by me

Ayende Rahien
09/29/2010 10:05 AM by
Ayende Rahien

Suirtimed,

I know, I am not using SQL sorting.

Torvin
09/30/2010 12:44 PM by
Torvin

Ticks aren't local time, they are time from known location in the past. So it doesn't matter about UTC.

That's not true :) Doing

DateTime.Now.Ticks - DateTime.UtcNow.Ticks;

will give you the difference it ticks according to your time zone. for me it's reporting ~ 144000000000 ticks, which is 4 hours, because I'm in UTC+4 zone.

so, in a few weeks the value of DateTime.Now.Ticks will jump, because we will move to UTC+3 due to DST, while DateTime.UtcNow.Ticks won't jump

Ayende Rahien
10/01/2010 11:22 AM by
Ayende Rahien

Torvin,

Huh?! I didn't know that.

Thanks

Augi
10/14/2010 08:02 AM by
Augi

Ticks aren't local time, they are time from known location in the past. So it doesn't matter about UTC.<<

This can be said about Environment.TickCount IMHO.

Comments have been closed on this topic.