示例#1
0
        public void Construct_WithClockAndEpoch_ShouldUseThose()
        {
            var factory = new FluidIdGenerator(isProduction: false, Clock, ApplicationInstanceId, epoch: Epoch, BitDistribution);
            var fluid   = factory.CreateFluid();

            Assert.Equal(GetMilliseconds(ClockValue, factory.Epoch), GetMilliseconds(fluid));
        }
示例#2
0
        public void Construct_WithApplicationInstanceId_ShouldUseThatValue()
        {
            var factory = new FluidIdGenerator(isProduction: false, FluidIdGenerator.GetUtcNow, applicationInstanceId: ApplicationInstanceId, bitDistribution: BitDistribution);
            var fluid   = factory.CreateFluid();

            Assert.Equal(ApplicationInstanceId, GetApplicationInstanceId(fluid));
        }
示例#3
0
        public void GetUtcNow_WhenInvoked_ShouldReturnUtcNow()
        {
            var getUtcNowResult = FluidIdGenerator.GetUtcNow();
            var utcNow          = DateTime.UtcNow;

            Assert.True((utcNow - getUtcNowResult) < TimeSpan.FromMilliseconds(1000));
        }
示例#4
0
        public void CreateFluid_TheFirstTime_ShouldUseCounterValue0()
        {
            var factory = new FluidIdGenerator(isProduction: false, Clock, ApplicationInstanceId, epoch: Epoch, BitDistribution);

            var fluid = factory.CreateFluid();

            Assert.Equal(0UL, GetCounterValue(fluid));
        }
示例#5
0
        public void GetMillisecondsSinceEpoch_Regularly_ShouldReturnExpectedValue()
        {
            var factory = new FluidIdGenerator(isProduction: false,
                                               utcClock: () => new DateTime(2020, 01, 01, 0, 0, 0, millisecond: 1, DateTimeKind.Utc),
                                               ApplicationInstanceId,
                                               epoch: new DateTime(2020, 01, 01, 0, 0, 0, DateTimeKind.Utc));

            var milliseconds = factory.GetMillisecondsSinceEpoch();

            Assert.Equal(1UL, milliseconds);
        }
示例#6
0
        public void Construct_WithClock_ShouldUseThat()
        {
            var clockValue = FluidIdGenerator.DefaultEpoch;

            DateTime FixedClock() => clockValue;

            var factory = new FluidIdGenerator(isProduction: false, FixedClock, applicationInstanceId: ApplicationInstanceId, bitDistribution: BitDistribution);
            var fluid   = factory.CreateFluid();

            Assert.Equal(GetMilliseconds(clockValue, factory.Epoch), GetMilliseconds(fluid));
        }
        private static IIdGenerator CreateIdGenerator(InvocationDetector invocationDetector)
        {
            return(new FluidIdGenerator(isProduction: false, utcClock: GetUtcNow, applicationInstanceId: 1));

            // Local function that acts as the UTC clock and also toggles a ref bool
            DateTime GetUtcNow()
            {
                invocationDetector.WasInvoked = true;
                return(FluidIdGenerator.GetUtcNow());
            }
        }
示例#8
0
        public void CreateFluid_TwiceWithAdvancingTimestamps_ShouldCount0Twice()
        {
            var invocationCount = 0;

            DateTime AutoAdvancingClock() => Epoch.AddMilliseconds(invocationCount++);

            var factory = new FluidIdGenerator(isProduction: false, utcClock: AutoAdvancingClock, ApplicationInstanceId, epoch: Epoch, BitDistribution);

            var fluid1 = factory.CreateFluid();
            var fluid2 = factory.CreateFluid();

            Assert.Equal(0UL, GetCounterValue(fluid1));
            Assert.Equal(0UL, GetCounterValue(fluid2));
        }
示例#9
0
        public void CreateFluid_Regularly_ShouldCreateExpectedValue()
        {
            var factory = new FluidIdGenerator(isProduction: false, Clock, ApplicationInstanceId, Epoch, BitDistribution);

            var fluid = factory.CreateFluid();

            Debug.Assert((ulong)(ClockValue - Epoch).TotalMilliseconds == 1);
            var         milliseconds          = 1UL << BitDistribution.ApplicationInstanceIdBitCount << BitDistribution.CounterBitCount;
            var         applicationInstanceId = (ulong)ApplicationInstanceId << BitDistribution.CounterBitCount;
            const ulong counterValue          = 0;
            var         expectedValue         = milliseconds | applicationInstanceId | counterValue;

            Assert.Equal(expectedValue, (ulong)fluid);
        }
示例#10
0
        public void CreateFluid_With10CounterBitsAnd1024Calls_ShouldCountUpTo1023()
        {
            Debug.Assert(BitDistribution.CounterBitCount == 10);

            // Timestamp advances after 1024 invocations
            var counter = 0;
            var factory = new FluidIdGenerator(isProduction: false, utcClock: () => Epoch.AddMilliseconds(counter++ >= 1024 ? 2 : 1), ApplicationInstanceId, epoch: Epoch, BitDistribution);

            counter = 0;             // Reset from initial internal call

            for (var i = 0UL; i < 1024; i++)
            {
                Assert.Equal(i, GetCounterValue(factory.CreateFluid()));
            }
        }
示例#11
0
        public void CreateFluid_With10CounterBitsAnd1025Calls_ShouldSleepNoMoreThan1Second()
        {
            Debug.Assert(BitDistribution.CounterBitCount == 10);

            var sleepMs = 0;
            var factory = new FluidIdGenerator(isProduction: false, utcClock: () => Epoch.AddMilliseconds(sleepMs == 0 ? 1 : 2), ApplicationInstanceId, epoch: Epoch, BitDistribution,
                                               sleepAction: ms => sleepMs += ms);

            for (var i = 0; i < 1025; i++)
            {
                factory.CreateFluid();
            }

            Assert.True(sleepMs <= 1000);
        }
示例#12
0
        public void CreateFluid_With10CounterBitsAnd1024Calls_ShouldNotSleep()
        {
            Debug.Assert(BitDistribution.CounterBitCount == 10);

            var sleepCount = 0;
            var factory    = new FluidIdGenerator(isProduction: false, utcClock: () => Epoch.AddMilliseconds(sleepCount == 0 ? 1 : 2), ApplicationInstanceId, epoch: Epoch, BitDistribution,
                                                  sleepAction: ms => sleepCount++);

            for (var i = 0; i < 1024; i++)
            {
                factory.CreateFluid();
            }

            Assert.Equal(0, sleepCount);
        }
示例#13
0
        public void CreateFluid_With10CounterBitsAnd1025CallsAndStuckClock_ShouldThrowOnLastCall()
        {
            Debug.Assert(BitDistribution.CounterBitCount == 10);

            var sleepMs = 0;
            var factory = new FluidIdGenerator(isProduction: false, utcClock: () => Epoch.AddDays(1), ApplicationInstanceId, epoch: Epoch, BitDistribution,
                                               sleepAction: ms => sleepMs += ms);

            for (var i = 0; i < 1024; i++)
            {
                factory.CreateFluid();
            }

            Assert.Throws <TimeoutException>(() => factory.CreateFluid());
        }
示例#14
0
        public void CreateFluid_TwiceWithRewindingClockMoreThanMaxAllowedMilliseconds_ShouldThrow()
        {
            var invocationCount = 0UL;

            DateTime AutorewindingClock() =>
            Epoch.AddDays(1).AddMilliseconds(++invocationCount == 2 ? 0 : (1UL + FluidIdGenerator.MaxClockCatchupSleepMilliseconds) * invocationCount);                     // E.g. +2001, 0, +4002, +6003, ...

            var sleepMs = 0;
            var factory = new FluidIdGenerator(isProduction: false, utcClock: AutorewindingClock, ApplicationInstanceId, epoch: Epoch, BitDistribution,
                                               sleepAction: ms => sleepMs += ms);

            invocationCount = 0;             // Reset the clock

            factory.CreateFluid();
            Assert.Throws <TimeoutException>(() => factory.CreateFluid());
        }
示例#15
0
        public void CreateFluid_TwiceWithRewindingClock1Second_ShouldSleep1Second()
        {
            var invocationCount = 0;

            DateTime AutorewindingClock() => Epoch.AddDays(1).AddSeconds(++invocationCount == 2 ? 0 : invocationCount);             // +1, 0, +2, +3, +4, ...

            var sleepMs = 0;
            var factory = new FluidIdGenerator(isProduction: false, utcClock: AutorewindingClock, ApplicationInstanceId, epoch: Epoch, BitDistribution,
                                               sleepAction: ms => sleepMs += ms);

            invocationCount = 0;             // Reset the clock

            factory.CreateFluid();
            factory.CreateFluid();

            Assert.Equal(1000, sleepMs);             // We would need a minute to catch up, but will never sleep for more than a second
        }
示例#16
0
        public void CreateFluid_TwiceWithRewindingClockMaxAllowedMilliseconds_ShouldSleepThatLong()
        {
            var invocationCount = 0UL;

            DateTime AutorewindingClock() =>
            Epoch.AddDays(1).AddMilliseconds(++invocationCount == 2 ? 0 : FluidIdGenerator.MaxClockCatchupSleepMilliseconds * invocationCount);                     // E.g. +2000, 0, +4000, +6000, ...

            var sleepMs = 0;
            var factory = new FluidIdGenerator(isProduction: false, utcClock: AutorewindingClock, ApplicationInstanceId, epoch: Epoch, BitDistribution,
                                               sleepAction: ms => sleepMs += ms);

            invocationCount = 0;             // Reset the clock

            factory.CreateFluid();
            factory.CreateFluid();

            Assert.Equal(FluidIdGenerator.MaxClockCatchupSleepMilliseconds, (ulong)sleepMs);
        }
示例#17
0
        public void CreateFluid_With10CounterBitsAnd1026Calls_ShouldCount0And1OnTheLastTwoCalls()
        {
            Debug.Assert(BitDistribution.CounterBitCount == 10);

            // Timestamp advances after 1024 invocations
            var counter = 0;
            var factory = new FluidIdGenerator(isProduction: false, utcClock: () => Epoch.AddMilliseconds(counter++ >= 1024 ? 2 : 1), ApplicationInstanceId, epoch: Epoch, BitDistribution);

            counter = 0;

            for (var i = 0UL; i < 1024; i++)
            {
                factory.CreateFluid();
            }
            var fluid1024 = factory.CreateFluid();
            var fluid1025 = factory.CreateFluid();

            Assert.Equal(0UL, GetCounterValue(fluid1024));
            Assert.Equal(1UL, GetCounterValue(fluid1025));
        }
示例#18
0
        public void CreateFluid_With10CounterBitsAnd1025CallsAndTemporarilyStuckClock_ShouldSleep1Millisecond()
        {
            Debug.Assert(BitDistribution.CounterBitCount == 10);

            var i                 = 0;
            var lastI             = 0;
            var clockMilliseconds = 0;

            DateTime StuckClockUnlesInvokedAgainBySameIteration() => Epoch.AddMilliseconds(clockMilliseconds += i == lastI ? 1 : (lastI = i) - i);             // Advances only when invoked multiple times by same iteration

            var sleepMs = 0;
            var factory = new FluidIdGenerator(isProduction: false, utcClock: StuckClockUnlesInvokedAgainBySameIteration, ApplicationInstanceId, epoch: Epoch, BitDistribution,
                                               sleepAction: ms => sleepMs += ms);

            for (i = 0; i < 1025; i++)
            {
                factory.CreateFluid();
            }

            Assert.Equal(1, sleepMs);
        }
示例#19
0
        public void CreateFluid_WithClockThrowingOnSecondInvocationForOneFluid_ShouldNotChangeCounter()
        {
            var invocationCount = 0;

            DateTime ClockThatThrowsOnThirdInvocation()
            {
                if (++invocationCount == 3)
                {
                    throw new Exception("Deliberately throws on third invocation.");
                }
                return(ClockValue);
            }

            var factory = new FluidIdGenerator(isProduction: false, ClockThatThrowsOnThirdInvocation, ApplicationInstanceId, Epoch, BitDistribution);

            factory.CreateFluid();                                  // Ensure that a "previous timestamp" was registered

            Assert.Throws <Exception>(() => factory.CreateFluid()); // This one throws

            var fluid = factory.CreateFluid();                      // This one's counter value should not have been affected by the throwing invocation

            Assert.Equal(1UL, GetCounterValue(fluid));
        }
示例#20
0
        public void Construct_WithBitDistribution_ShouldDistributeBitsAsExpected()
        {
            var factory      = new FluidIdGenerator(isProduction: false, FluidIdGenerator.GetUtcNow, ApplicationInstanceId, bitDistribution: BitDistribution);
            var maxTimestamp = (ulong)(FluidIdGenerator.DefaultEpoch.AddMilliseconds((1UL << FluidBitDistribution.Default.TimestampBitCount) - 1) - FluidIdGenerator.DefaultEpoch).TotalMilliseconds;

            var timestampPartial             = factory.GetTimestampPartialFluid(maxTimestamp);
            var applicationInstanceIdPartial = factory.GetApplicationInstanceIdPartialFluid();
            var counterValuePartial          = factory.GetCounterValuePartialFluid(2UL);

            var timestampMask = BitDistribution.MaxTimestamp << BitDistribution.ApplicationInstanceIdBitCount << BitDistribution.CounterBitCount;

            Assert.Equal(0UL, timestampPartial & ~timestampMask);
            Assert.Equal(maxTimestamp, timestampPartial >> BitDistribution.ApplicationInstanceIdBitCount >> BitDistribution.CounterBitCount);

            var applicationInstanceIdMask = BitDistribution.MaxApplicationInstanceId << BitDistribution.CounterBitCount;

            Assert.Equal(0UL, applicationInstanceIdPartial & ~applicationInstanceIdMask);
            Assert.Equal(ApplicationInstanceId, applicationInstanceIdPartial >> BitDistribution.CounterBitCount);

            var counterMask = BitDistribution.MaxCounterValue;

            Assert.Equal(0UL, counterValuePartial & ~counterMask);
            Assert.Equal(2UL, counterValuePartial);
        }
示例#21
0
 public void Construct_WithZeroApplicationInstanceIdOutsideProduction_ShouldSucceed()
 {
     _ = new FluidIdGenerator(isProduction: false, FluidIdGenerator.GetUtcNow, applicationInstanceId: 0);
 }