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
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(...);
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?
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).
Yup, DateTime.UtcNow is probably a better choice than DateTime.Now
Ken: I did not even see that. Your database will have a hiccup twice a year because of daylight savings^^
You may be aware, but I wanted to point out that MS SQL Server sorts GUIDs in a non-intuitive manner compared to how we as humans would sort them.
sqlblog.com/.../...guids-sorted-by-sql-server.aspx
@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)
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?
@James_2JS: Some places do not observe DST.
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
Suirtimed,
I know, I am not using SQL sorting.
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
Torvin,
Huh?! I didn't know that.
Thanks
This can be said about Environment.TickCount IMHO.
Comment preview