Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
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...
commit c0c3d4d95722b84e29a00792d81ee0364347c243 1 parent 3366163
@nberardi nberardi authored
View
2  src/CassandraColumnFamilyOperations.cs
@@ -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)
View
2  src/CassandraSuperColumnFamilyOperations.cs
@@ -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)
View
1  src/FluentCassandra.csproj
@@ -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
6 src/FluentColumn.cs
@@ -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
2  src/FluentMutation.cs
@@ -9,7 +9,7 @@ public class FluentMutation
/// </summary>
internal FluentMutation()
{
- ColumnTimestamp = DateTimePrecise.UtcNowOffset;
+ ColumnTimestamp = TimestampHelper.UtcNow();
}
/// <summary>
View
10 src/GuidGeneration.cs
@@ -0,0 +1,10 @@
+using System;
+
+namespace FluentCassandra
+{
+ public enum GuidGeneration
+ {
+ Fast,
+ NoDuplicates
+ }
+}
View
66 src/GuidGenerator.cs
@@ -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
2  src/Operations/Remove.cs
@@ -26,7 +26,7 @@ public override Void Execute()
Session.GetClient().remove(
Key,
path,
- DateTimePrecise.UtcNowOffset.ToCassandraTimestamp(),
+ TimestampHelper.UtcNow().ToCassandraTimestamp(),
Session.WriteConsistency
);
View
5 src/TimestampHelper.cs
@@ -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
View
46 test/FluentCassandra.Tests/GuidGeneratorTest.cs
@@ -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));
+ }
}
}
Please sign in to comment.
Something went wrong with that request. Please try again.