Permalink
Browse files

decided to add an option of Fast vs NoDuplicates, since NoDuplicates …

…is 10 times slower, this should satisfy issue #68 and #66
  • Loading branch information...
1 parent 3366163 commit c0c3d4d95722b84e29a00792d81ee0364347c243 @nberardi nberardi committed Sep 25, 2012
@@ -43,7 +43,7 @@ public static void InsertColumn(this CassandraColumnFamily family, CassandraObje
public static void InsertColumn(this CassandraColumnFamily family, CassandraObject key, CassandraObject columnName, BytesType columnValue)
{
- InsertColumn(family, key, columnName, columnValue, DateTimePrecise.UtcNowOffset, null);
+ InsertColumn(family, key, columnName, columnValue, TimestampHelper.UtcNow(), null);
}
public static void InsertColumn(this CassandraColumnFamily family, CassandraObject key, CassandraObject columnName, BytesType columnValue, DateTimeOffset timestamp, int? timeToLive)
@@ -56,7 +56,7 @@ public static void InsertColumn(this CassandraSuperColumnFamily family, Cassandr
public static void InsertColumn(this CassandraSuperColumnFamily family, CassandraObject key, CassandraObject superColumnName, CassandraObject name, BytesType value)
{
- InsertColumn(family, key, superColumnName, name, value, DateTimePrecise.UtcNowOffset, null);
+ InsertColumn(family, key, superColumnName, name, value, TimestampHelper.UtcNow(), null);
}
public static void InsertColumn(this CassandraSuperColumnFamily family, CassandraObject key, CassandraObject superColumnName, CassandraObject name, BytesType value, DateTimeOffset timestamp, int? timeToLive)
@@ -127,6 +127,7 @@
<Compile Include="CassandraCqlRowSchema.cs" />
<Compile Include="CqlHelper.cs" />
<Compile Include="FluentCqlRow.cs" />
+ <Compile Include="GuidGeneration.cs" />
<Compile Include="ILoadable.cs" />
<Compile Include="FluentCassandraException.cs" />
<Compile Include="FluentColumn.cs">
View
@@ -32,7 +32,7 @@ public FluentColumn(CassandraColumnSchema schema = null)
{
SetSchema(schema);
- ColumnTimestamp = DateTimePrecise.UtcNowOffset;
+ ColumnTimestamp = TimestampHelper.UtcNow();
ColumnSecondsUntilDeleted = null;
ColumnTimeUntilDeleted = null;
}
@@ -46,7 +46,7 @@ public CassandraObject ColumnName
set
{
_name = value.GetValue(GetSchema().NameType);
- ColumnTimestamp = DateTimePrecise.UtcNowOffset;
+ ColumnTimestamp = TimestampHelper.UtcNow();
}
}
@@ -59,7 +59,7 @@ public CassandraObject ColumnValue
set
{
_value = value.GetValue(GetSchema().ValueType);
- ColumnTimestamp = DateTimePrecise.UtcNowOffset;
+ ColumnTimestamp = TimestampHelper.UtcNow();
}
}
View
@@ -9,7 +9,7 @@ public class FluentMutation
/// </summary>
internal FluentMutation()
{
- ColumnTimestamp = DateTimePrecise.UtcNowOffset;
+ ColumnTimestamp = TimestampHelper.UtcNow();
}
/// <summary>
View
@@ -0,0 +1,10 @@
+using System;
+
+namespace FluentCassandra
+{
+ public enum GuidGeneration
+ {
+ Fast,
+ NoDuplicates
+ }
+}
View
@@ -1,5 +1,4 @@
using System;
-using System.Diagnostics;
using System.Net;
using System.Net.NetworkInformation;
@@ -12,6 +11,9 @@ namespace FluentCassandra
public static class GuidGenerator
{
private static readonly Random Random;
+ private static readonly object Lock = new object();
+
+ private static DateTimeOffset _lastTimestampGenerated = TimestampHelper.UtcNow();
// number of bytes in uuid
private const int ByteArraySize = 16;
@@ -34,19 +36,25 @@ public static class GuidGenerator
// offset to move from 1/1/0001, which is 0-time for .NET, to gregorian 0-time of 10/15/1582
private static readonly DateTimeOffset GregorianCalendarStart = new DateTimeOffset(1582, 10, 15, 0, 0, 0, TimeSpan.Zero);
- public static byte[] DefaultNode { get; set; }
+ public static GuidGeneration GuidGeneration { get; set; }
+
+ public static byte[] NodeBytes { get; set; }
+ public static byte[] ClockSequenceBytes { get; set; }
static GuidGenerator()
{
Random = new Random();
- DefaultNode = GetNodeBytes();
+
+ GuidGeneration = GuidGeneration.NoDuplicates;
+ NodeBytes = GenerateNodeBytes();
+ ClockSequenceBytes = GenerateClockSequenceBytes();
}
/// <summary>
/// Generates a random value for the node.
/// </summary>
/// <returns></returns>
- public static byte[] GetNodeBytes()
+ public static byte[] GenerateNodeBytes()
{
var node = new byte[6];
@@ -58,7 +66,7 @@ public static byte[] GetNodeBytes()
/// Generates a node based on the first 6 bytes of an IP address.
/// </summary>
/// <param name="ip"></param>
- public static byte[] GetNodeBytes(IPAddress ip)
+ public static byte[] GenerateNodeBytes(IPAddress ip)
{
if (ip == null)
throw new ArgumentNullException("ip");
@@ -79,7 +87,7 @@ public static byte[] GetNodeBytes(IPAddress ip)
/// </summary>
/// <param name="mac"></param>
/// <remarks>The machines MAC address can be retrieved from <see cref="NetworkInterface.GetPhysicalAddress"/>.</remarks>
- public static byte[] GetNodeBytes(PhysicalAddress mac)
+ public static byte[] GenerateNodeBytes(PhysicalAddress mac)
{
if (mac == null)
throw new ArgumentNullException("mac");
@@ -89,19 +97,19 @@ public static byte[] GetNodeBytes(PhysicalAddress mac)
return node;
}
- public static GuidVersion GetVersion(this Guid guid)
- {
- byte[] bytes = guid.ToByteArray();
- return (GuidVersion)((bytes[VersionByte] & 0xFF) >> VersionByteShift);
- }
-
- public static byte[] GetClockSequenceBytes()
+ public static byte[] GenerateClockSequenceBytes()
{
var bytes = new byte[2];
Random.NextBytes(bytes);
return bytes;
}
+ public static GuidVersion GetUuidVersion(this Guid guid)
+ {
+ byte[] bytes = guid.ToByteArray();
+ return (GuidVersion)((bytes[VersionByte] & 0xFF) >> VersionByteShift);
+ }
+
public static DateTimeOffset GetDateTimeOffset(Guid guid)
{
byte[] bytes = guid.ToByteArray();
@@ -136,37 +144,55 @@ public static DateTime GetUtcDateTime(Guid guid)
public static Guid GenerateTimeBasedGuid()
{
- return GenerateTimeBasedGuid(DateTimePrecise.UtcNowOffset, GetClockSequenceBytes(), DefaultNode);
+ var ts = TimestampHelper.UtcNow();
+
+ switch(GuidGeneration)
+ {
+ case GuidGeneration.Fast:
+ return GenerateTimeBasedGuid(ts, ClockSequenceBytes, NodeBytes);
+
+ case GuidGeneration.NoDuplicates:
+ default:
+ lock (Lock)
+ {
+ if (ts <= _lastTimestampGenerated)
+ ClockSequenceBytes = GenerateClockSequenceBytes();
+
+ _lastTimestampGenerated = ts;
+
+ return GenerateTimeBasedGuid(ts, ClockSequenceBytes, NodeBytes);
+ }
+ }
}
public static Guid GenerateTimeBasedGuid(DateTime dateTime)
{
- return GenerateTimeBasedGuid(dateTime, GetClockSequenceBytes(), DefaultNode);
+ return GenerateTimeBasedGuid(dateTime, GenerateClockSequenceBytes(), NodeBytes);
}
public static Guid GenerateTimeBasedGuid(DateTimeOffset dateTime)
{
- return GenerateTimeBasedGuid(dateTime, GetClockSequenceBytes(), DefaultNode);
+ return GenerateTimeBasedGuid(dateTime, GenerateClockSequenceBytes(), NodeBytes);
}
public static Guid GenerateTimeBasedGuid(DateTime dateTime, PhysicalAddress mac)
{
- return GenerateTimeBasedGuid(dateTime, GetClockSequenceBytes(), GetNodeBytes(mac));
+ return GenerateTimeBasedGuid(dateTime, GenerateClockSequenceBytes(), GenerateNodeBytes(mac));
}
public static Guid GenerateTimeBasedGuid(DateTimeOffset dateTime, PhysicalAddress mac)
{
- return GenerateTimeBasedGuid(dateTime, GetClockSequenceBytes(), GetNodeBytes(mac));
+ return GenerateTimeBasedGuid(dateTime, GenerateClockSequenceBytes(), GenerateNodeBytes(mac));
}
public static Guid GenerateTimeBasedGuid(DateTime dateTime, IPAddress ip)
{
- return GenerateTimeBasedGuid(dateTime, GetClockSequenceBytes(), GetNodeBytes(ip));
+ return GenerateTimeBasedGuid(dateTime, GenerateClockSequenceBytes(), GenerateNodeBytes(ip));
}
public static Guid GenerateTimeBasedGuid(DateTimeOffset dateTime, IPAddress ip)
{
- return GenerateTimeBasedGuid(dateTime, GetClockSequenceBytes(), GetNodeBytes(ip));
+ return GenerateTimeBasedGuid(dateTime, GenerateClockSequenceBytes(), GenerateNodeBytes(ip));
}
public static Guid GenerateTimeBasedGuid(DateTime dateTime, byte[] clockSequence, byte[] node)
View
@@ -26,7 +26,7 @@ public override Void Execute()
Session.GetClient().remove(
Key,
path,
- DateTimePrecise.UtcNowOffset.ToCassandraTimestamp(),
+ TimestampHelper.UtcNow().ToCassandraTimestamp(),
Session.WriteConsistency
);
View
@@ -22,6 +22,11 @@ static TimestampHelper()
MaxUnixMicroseconds = Convert.ToInt64((DateTimeOffset.MaxValue - UnixStart).Ticks / TicksInOneMicrosecond);
}
+ /// <summary>
+ /// Allows for the use of alternative timestamp providers.
+ /// </summary>
+ public static Func<DateTimeOffset> UtcNow = () => DateTimePrecise.UtcNowOffset;
+
public static long ToCassandraTimestamp(this DateTimeOffset dt)
{
// we are using the microsecond format from 1/1/1970 00:00:00 UTC same as the Cassandra server
@@ -4,7 +4,6 @@
namespace FluentCassandra
{
-
public class GuidGeneratorTest
{
[Fact]
@@ -15,7 +14,7 @@ public void Type1Check()
var guid = GuidGenerator.GenerateTimeBasedGuid();
// act
- var actual = guid.GetVersion();
+ var actual = guid.GetUuidVersion();
// assert
Assert.Equal(expected, actual);
@@ -29,7 +28,7 @@ public void SanityType1Check()
var guid = Guid.NewGuid();
// act
- var actual = guid.GetVersion();
+ var actual = guid.GetUuidVersion();
// assert
Assert.NotEqual(expected, actual);
@@ -90,5 +89,46 @@ public void GetDateTimeOffset()
// assert
Assert.Equal(expected, actual);
}
+
+ [Fact]
+ public void DoesNotCreateDuplicateWhenTimeHasNotPassed()
+ {
+ // arrange
+ DateTimeOffset currentTime = DateTimeOffset.UtcNow;
+
+ TimestampHelper.UtcNow = () => currentTime; //make sure all calls will return the same time
+ Guid firstGuid = GuidGenerator.GenerateTimeBasedGuid();
+
+ // act
+ Guid secondGuid = GuidGenerator.GenerateTimeBasedGuid();
+
+ // assert
+ Assert.True(firstGuid != secondGuid, "first: " + firstGuid + " second: " + secondGuid);
+ //Assert.NotEqual(firstGuid,secondGuid);
+ }
+
+ [Fact]
+ public void ClockSequenceChangesWhenTimeMovesBackward()
+ {
+ Func<Guid, short> getClockSequence = (guid) => {
+ byte[] clockSequenceBytes = new byte[2];
+ Array.Copy(guid.ToByteArray(), 8, clockSequenceBytes, 0, 2);
+ return BitConverter.ToInt16(clockSequenceBytes, 0);
+ };
+
+ // arrange
+ DateTimeOffset currentTime = DateTimeOffset.UtcNow;
+
+ TimestampHelper.UtcNow = () => currentTime;
+ Guid firstGuid = GuidGenerator.GenerateTimeBasedGuid();
+
+ // act
+ TimestampHelper.UtcNow = () => currentTime.AddTicks(-1); //make sure clock went backwards
+ Guid secondGuid = GuidGenerator.GenerateTimeBasedGuid();
+
+ // assert
+ //clock sequence is not equal
+ Assert.NotEqual(getClockSequence(firstGuid), getClockSequence(secondGuid));
+ }
}
}

0 comments on commit c0c3d4d

Please sign in to comment.