Example #1
0
        private static void EnsureRedisCheckedIfNeeded()
        {
            if (_localRedisTested)
            {
                return;
            }

            lock (LocalRedisTestSynch)
            {
                if (_localRedisTested)
                {
                    return;
                }

                try
                {
                    var environmentVariableRedisConfig = Environment.GetEnvironmentVariable(RedisConfigurationEnvironmentVariable);
                    //environmentVariableRedisConfig = "";
                    _redisConfiguration = string.IsNullOrWhiteSpace(environmentVariableRedisConfig) ? DefaultLocalRedisConfiguration : environmentVariableRedisConfig;

                    // Skip tests instead of throwing errors if we are defaulting to localhost
                    // if we have configured redis via REDIS_CONFIGURATION_FOR_UNIT_TESTS
                    // we want a failed build/tests.
                    _localRedisConfiguredForTests = !string.IsNullOrWhiteSpace(environmentVariableRedisConfig);

                    _localRedisTestTime = DateTime.Now;
                    using (var basicRedis = new BasicRedisWrapper(_redisConfiguration, false))
                    {
                        basicRedis.Cache.StringSet(
                            $"{typeof(TestRedisConfig).FullName}:{nameof(GetTestEnvironmentRedis)}:{Guid.NewGuid():N}",
                            "value", TimeSpan.FromSeconds(3));

                        _localRedisAvailable = true;
                    }
                }
                catch (Exception ex)
                {
                    _localRedisTestFailDetails = ex.ToString();

                    _localRedisAvailable = false;
                }

                _localRedisTested = true;
            }
        }
Example #2
0
        public void Locks()
        {
            var redisConnection = TestRedisConfig.GetTestEnvironmentRedis();

            var testId = Guid.NewGuid();

            using (var basicRedis1 = new BasicRedisWrapper(redisConnection, false))
                using (var basicRedis2 = new BasicRedisWrapper(redisConnection, false))
                    using (var basicRedis3 = new BasicRedisWrapper(redisConnection, false))
                    {
                        using (var lck1A = basicRedis1.Lock.CreateLock($"{typeof(TestRedisConfig).FullName}:Lock1:{testId:N}", TimeSpan.FromSeconds(30)))
                            using (var lck2 = basicRedis2.Lock.CreateLock($"{typeof(TestRedisConfig).FullName}:Lock2:{testId:N}", TimeSpan.FromSeconds(30)))
                                using (var lck1B = basicRedis3.Lock.CreateLock($"{typeof(TestRedisConfig).FullName}:Lock1:{testId:N}", TimeSpan.FromSeconds(30)))
                                {
                                    Assert.NotNull(lck1A);
                                    Assert.NotNull(lck2);
                                    Assert.Null(lck1B);
                                }
                    }
        }
Example #3
0
        /// <summary>
        /// Create an interceptor based on real redis connection(s) or a local in memory redis mock.
        /// Note: local in memory redis mock does not support "multiple connections" (there is no actual connecting going on)
        /// </summary>
        /// <param name="redisConfiguration">redis connection config (e.g. "localhost:6379") or "mock-{uniqueid}"</param>
        /// <param name="useMultipleRedisConnections">use a single redis connection vs multiple for different concerns (locking, caching, messaging)</param>
        public RedisInterceptOrMock(string redisConfiguration, bool useMultipleRedisConnections = false)
        {
            if (redisConfiguration.ToLowerInvariant().StartsWith("mock-"))
            {
                if (useMultipleRedisConnections)
                {
                    throw new ArgumentException($"Mock redis and useMultipleRedisConnections is non-sensical");
                }

                if (redisConfiguration.ToLowerInvariant() == "mock-")
                {
                    throw new ArgumentException($"Mock redis id should be supplied (per fake redis instance)");
                }

                _basicRedisWrapper = null;
                _redisMock         = LocalMemMockOfRedis.Create(redisConfiguration);
            }
            else
            {
                _basicRedisWrapper = new BasicRedisWrapper(redisConfiguration, useMultipleRedisConnections);
                _redisMock         = null;
            }
        }
Example #4
0
        static void Main(string[] args)
        {
            var rndSrc = new Random(2);

            var testIdSrc = 0;

            var uniqueCacheId = 1;
            var ttl           = TimeSpan.FromSeconds(10);          // 20 min equiv
            var regen         = TimeSpan.FromSeconds(2.5);         // 1 min equiv

            var regenDuration        = TimeSpan.FromSeconds(1.25); // duration of call to generate value to store in cache
            var regenErrorPercentage = 0;
            var farmClockTollerance  = 0.1;

            var p1Duration  = TimeSpan.FromSeconds(10);
            var p1Frequency = TimeSpan.FromSeconds(0.5);

            var launchTwoInstances = false;
            var doRegTest          = true;
            var doPerfTest         = true;

            var writePerformanceCsv  = false;
            var writeTraceOutputFile = false;
            var writeHtmlOutputFile  = false;
            var outputFolder         = "c:\\temp\\";

            var perfTestCount   = 1000000;
            var perfTestMeasure = 100000;

            var keyspace = $"keyspace{Guid.NewGuid():N}";

            if (launchTwoInstances && args.Length == 0 && !Debugger.IsAttached)
            {
                Process.Start(Assembly.GetEntryAssembly().Location, $"nospawn 1 {keyspace}");
                //Task.Delay(2000).Wait();
                Process.Start(Assembly.GetEntryAssembly().Location, $"nospawn 2 {keyspace}");
                return;
            }

            var id = 0;

            if (args.Length >= 3)
            {
                int.TryParse(args[1], out id);
                keyspace = args[2];
            }

            var synchedConsole = new SynchedColouredConsoleTraceWriter(
                traceFileName: writeTraceOutputFile ? $"{outputFolder}RegenerativeCacheManagerDemo_{keyspace}_{id}.txt" : null,
                htmlFileName: writeHtmlOutputFile ? $"{outputFolder}RegenerativeCacheManagerDemo_{keyspace}_{id}.html" : null,
                performanceFileName: writePerformanceCsv ? $"{outputFolder}RegenerativeCacheManagerDemo_{keyspace}_{id}.csv" : null
                );

            using (var ct1R = new BasicRedisWrapper("localhost"))
                using (var cacheTest1 = new RegenerativeCacheManager("keyspace3", ct1R.Cache, ct1R.Lock, ct1R.Bus))
                    using (var ct2R = new BasicRedisWrapper("localhost"))
                        using (var cacheTest2 = new RegenerativeCacheManager("Keyspace3", ct2R.Cache, ct2R.Lock, ct2R.Bus))
                        {
                            new List <IDisposable> {
                                cacheTest1, ct1R, cacheTest2, ct2R
                            }.ForEach(d => d.Dispose());
                        }

            synchedConsole.WriteLine("RegenerativeCacheManager dispose works.", ConsoleColor.Black, ConsoleColor.Green);

            var redis = new BasicRedisWrapper("localhost", true);
            var cache = new RegenerativeCacheManager(keyspace, redis.Cache, redis.Lock, redis.Bus, synchedConsole)
            {
                // low tollerences in this test due to extremely low cache item re-generation and expiry times, normally 30s to minutes
                CacheExpiryToleranceSeconds     = 1,
                TriggerDelaySeconds             = 1,
                MinimumForwardSchedulingSeconds = 1,
                FarmClockToleranceSeconds       = farmClockTollerance,
            };

            var generateFunc = new Func <string>(() =>
            {
                Task.Delay(regenDuration).Wait();
                if (rndSrc.Next(0, 100) < regenErrorPercentage)
                {
                    throw new ApplicationException("Synthetic Error");
                }
                return($" >>> CacheItemVal_{DateTime.Now:ss.fff}_{Interlocked.Increment(ref uniqueCacheId)} <<< ");
            });

            if (doRegTest)
            {
                synchedConsole.WriteLine($"Inactive TTL: {ttl.TotalSeconds}s, Regeneration every: {regen.TotalSeconds}s, Regeneration performance: {regenDuration.TotalSeconds}s");
                synchedConsole.WriteLine($"------- running get per second (in background) for {p1Duration.TotalSeconds} seconds");
                synchedConsole.WriteLine("=================================================");
                var start = DateTime.UtcNow;
                while (DateTime.UtcNow.Subtract(start).TotalSeconds < p1Duration.TotalSeconds)
                {
                    var testId = Interlocked.Increment(ref testIdSrc);
                    Task.Run(() => synchedConsole.WriteLine($"* PART 1 * Cache Value: {MonitorWork(() => cache.GetOrAdd("test1", generateFunc, ttl, regen), synchedConsole, testId)}"));
                    Task.Delay(p1Frequency).Wait();
                }

                synchedConsole.WriteLine("=================================================");
                synchedConsole.DebugWait(6);
                var testX1 = Interlocked.Increment(ref testIdSrc);
                synchedConsole.WriteLine($"* PART 2 * Cache Value: {MonitorWork(() => cache.GetOrAdd("test1", generateFunc, ttl, regen), synchedConsole, testX1)}");

                synchedConsole.WriteLine("=================================================");
                synchedConsole.DebugWait(15);
                var testX2 = Interlocked.Increment(ref testIdSrc);
                Task.Run(() => synchedConsole.WriteLine($"* PART 3 * Cache Value: {MonitorWork(() => cache.GetOrAdd("test1", generateFunc, ttl, regen), synchedConsole, testX2)}"));
                var testX3 = Interlocked.Increment(ref testIdSrc);
                Task.Run(() => synchedConsole.WriteLine($"* PART 3 * Cache Value: {MonitorWork(() => cache.GetOrAdd("test1", generateFunc, ttl, regen), synchedConsole, testX3)}"));
                var testX4 = Interlocked.Increment(ref testIdSrc);
                synchedConsole.WriteLine($"* PART 3 * Cache Value: {MonitorWork(() => cache.GetOrAdd("test1", generateFunc, ttl, regen), synchedConsole, testX4)}");
                var testX5 = Interlocked.Increment(ref testIdSrc);
                synchedConsole.WriteLine($"* PART 3 * Cache Value: {MonitorWork(() => cache.GetOrAdd("test1", generateFunc, ttl, regen), synchedConsole, testX5)}");
                synchedConsole.DebugWait(2);
                synchedConsole.DebugWait(20);

                ShowStats(synchedConsole);

                try
                {
                    Task.WaitAll(MonitoredWorkBag.Select(t => t.Item1).ToArray());
                }
                catch (Exception ex)
                {
                    synchedConsole.WriteLine($"First error: {(ex as AggregateException)?.InnerExceptions.First().ToString() ?? ex.ToString()}", ConsoleColor.White, ConsoleColor.Red);
                }

                while (MonitoredWorkBag.TryTake(out Tuple <Task, TimeSpan> tr))
                {
                    tr.Item1.Dispose();
                }
            }

            if (doRegTest && doPerfTest)
            {
                synchedConsole.OpenNewOutputFile();
            }

            if (doPerfTest)
            {
                cache.Dispose();

                _perTestMeasureEveryN = Math.Max(1, perfTestCount / perfTestMeasure);

                cache = new RegenerativeCacheManager(keyspace, redis.Cache, redis.Lock, redis.Bus, synchedConsole)
                {
                    // low tollerences in this test due to extremely low cache item re-generation and expiry times, normally 30s to minutes
                    TriggerDelaySeconds             = 1,
                    MinimumForwardSchedulingSeconds = 1,
                    FarmClockToleranceSeconds       = farmClockTollerance,
                    CacheExpiryToleranceSeconds     = 5,
                };

                synchedConsole.WriteLine($"Running performance test of {perfTestCount:#,##0} cache calls to GetOrAdd in parallel.", ConsoleColor.White, ConsoleColor.DarkBlue);

                testIdSrc = 1;

                while (MonitoredWorkBag.TryTake(out Tuple <Task, TimeSpan> tr))
                {
                    ;
                }

                synchedConsole.ShowFullOutputToConsole = false;
                // warm up
                cache.GetOrAdd("test1", generateFunc, ttl, regen);

                var swWait = Stopwatch.StartNew();

                var dop = 10;
                Parallel.ForEach(Enumerable.Range(0, dop), new ParallelOptions {
                    MaxDegreeOfParallelism = dop
                }, (i) =>
                {
                    for (int j = 0; j < perfTestCount / dop; j++)
                    {
                        var testId = Interlocked.Increment(ref testIdSrc);
                        if (j % 10 == 0)
                        {
                            Thread.Sleep(1);
                        }
                        synchedConsole.WriteLine($"* PART 4 * Cache Value: {MonitorWork(() => cache.GetOrAdd("test1", generateFunc, ttl, regen), synchedConsole, testId)}");
                    }
                });

                swWait.Stop();

                synchedConsole.WriteLine($"\r\n\r\n\t\t\t{perfTestCount:#,##0} cache GetOrAdd calls completed with {dop} degrees of parallelism." +
                                         $"\r\n\r\n\t\t\t Duration: {swWait.Elapsed.TotalMilliseconds * 1000:#,##0.0}us." +
                                         $"\r\n\t\t\t Requests/s: {perfTestCount / swWait.Elapsed.TotalSeconds:#,##0.0} ({perfTestCount / swWait.Elapsed.TotalHours:#,##0}/h)" +
                                         $"\r\n\r\n"
                                         , ConsoleColor.White, overrideShowOutput: true);

                ShowStats(synchedConsole);

                while (MonitoredWorkBag.TryTake(out Tuple <Task, TimeSpan> tr))
                {
                    tr.Item1.Dispose();
                }

                GC.Collect();
            }

            synchedConsole.CloseAndStopAllWriting();

            if (launchTwoInstances)
            {
                synchedConsole.WriteLine("Press enter to exit.", overrideShowOutput: true);
                Console.ReadLine();
            }
        }
Example #5
0
        public void Caching()
        {
            var redisConnection = TestRedisConfig.GetTestEnvironmentRedis();

            using (var basicRedis1 = new BasicRedisWrapper(redisConnection, false))
                using (var basicRedis2 = new BasicRedisWrapper(redisConnection, false))
                {
                    var cacheKey        = $"{typeof(TestRedisConfig).FullName}:Cache:{Guid.NewGuid():N}";
                    var cacheKeyMissing = $"{typeof(TestRedisConfig).FullName}:CacheMissing:{Guid.NewGuid():N}";
                    var cacheVal1       = $"{typeof(TestRedisConfig).FullName}:CacheVal1:{Guid.NewGuid():N}";
                    var cacheVal2       = $"{typeof(TestRedisConfig).FullName}:CacheVal2:{Guid.NewGuid():N}";

                    // basic write/read
                    basicRedis1.Cache.StringSet(cacheKey, cacheVal1, TimeSpan.FromSeconds(3));
                    var node2Val1    = basicRedis2.Cache.StringGetWithExpiry(cacheKey, out TimeSpan node2Val1Expires1);
                    var node2Subval1 = basicRedis2.Cache.GetStringStart(cacheKey, 10);
                    var node1Subval1 = basicRedis1.Cache.GetStringStart(cacheKey, 10);
                    var node1Val1    = basicRedis1.Cache.StringGetWithExpiry(cacheKey, out TimeSpan node1Val1Expires1);

                    Assert.Equal(node2Val1, node1Val1);
                    Assert.Equal(node2Subval1, node1Subval1);
                    Assert.NotEqual(node1Val1, node1Subval1);
                    Assert.StartsWith(node1Subval1, node1Val1);

                    basicRedis2.Cache.StringSet(cacheKey, cacheVal2, TimeSpan.FromSeconds(3));
                    var node2Val2    = basicRedis2.Cache.StringGetWithExpiry(cacheKey, out TimeSpan node2Val2Expires1);
                    var node2Subval2 = basicRedis2.Cache.GetStringStart(cacheKey, 10);
                    var node1Val2    = basicRedis1.Cache.StringGetWithExpiry(cacheKey, out TimeSpan node1Val2Expires1);
                    var node1Subval2 = basicRedis1.Cache.GetStringStart(cacheKey, 10);

                    Assert.NotEqual(node1Val1, node2Val2);

                    Assert.Equal(node2Val2, node1Val2);
                    Assert.Equal(node2Subval2, node1Subval2);
                    Assert.NotEqual(node1Val2, node1Subval2);
                    Assert.StartsWith(node1Subval2, node1Val2);

                    Assert.Equal(node1Subval1, node1Subval2);
                    Assert.Equal(node1Subval1, node2Subval1);
                    Assert.Equal(node1Subval1, node1Subval2);
                    Assert.Equal(node1Subval1, node2Subval2);

                    Assert.True(node2Val1Expires1.TotalSeconds > 1);
                    Assert.True(node1Val1Expires1.TotalSeconds > 1);
                    Assert.True(node2Val2Expires1.TotalSeconds > 1);
                    Assert.True(node1Val2Expires1.TotalSeconds > 1);

                    var missingVal    = basicRedis2.Cache.StringGetWithExpiry(cacheKeyMissing, out TimeSpan missingExpiryVal);
                    var missingSubVal = basicRedis2.Cache.StringGetWithExpiry(cacheKeyMissing, out TimeSpan missingExpirySubVal);

                    Assert.Null(missingVal);
                    Assert.Null(missingSubVal);

                    Thread.Sleep(3500);
                    var expiredValNull    = basicRedis2.Cache.StringGetWithExpiry(cacheKey, out TimeSpan expiredValExpiry);
                    var expiredSubValNull = basicRedis2.Cache.GetStringStart(cacheKey, 10);

                    Assert.Null(expiredValNull);
                    Assert.Null(expiredSubValNull);
                }
        }
Example #6
0
        public void Messaging()
        {
            var redisConnection = TestRedisConfig.GetTestEnvironmentRedis();

            var testId = Guid.NewGuid();

            using (var basicRedis1 = new BasicRedisWrapper(redisConnection, false))
                using (var basicRedis2 = new BasicRedisWrapper(redisConnection, false))
                {
                    var topic1 = $"{typeof(TestRedisConfig).FullName}:Bus:{Guid.NewGuid():N}";
                    var topic2 = $"{typeof(TestRedisConfig).FullName}:Bus:{Guid.NewGuid():N}";

                    string
                        t1M1B1S1 = $"testmsg-{Guid.NewGuid():N}",
                        t1M2B1S2 = $"testmsg-{Guid.NewGuid():N}",
                        t1M3B1S2 = $"testmsg-{Guid.NewGuid():N}",
                        t1M4B2S1 = $"testmsg-{Guid.NewGuid():N}",
                        t1M5B2S1 = $"testmsg-{Guid.NewGuid():N}",
                        t1M6B2S1 = $"testmsg-{Guid.NewGuid():N}",
                        t2M7B1S1 = $"testmsg-{Guid.NewGuid():N}",
                        t2M8B2S1 = $"testmsg-{Guid.NewGuid():N}",
                        t1M9B1S1 = $"testmsg-{Guid.NewGuid():N}",
                        x        = $"testmsg-x-{Guid.NewGuid():N}";

                    var waitForMessage = new Action <List <string>, string>((bag, msg) =>
                    {
                        var end = DateTime.Now.AddSeconds(1);
                        while (!bag.Contains(msg) && DateTime.Now < end)
                        {
                            Thread.Sleep(100);
                        }
                    });

                    var msgsFromB1Sub1T1 = new List <string>();
                    var msgsFromB1Sub2T1 = new List <string>();

                    var msgsFromB2Sub1T1 = new List <string>();
                    var msgsFromB2Sub2T1 = new List <string>();

                    var msgsFromB1Sub1T2 = new List <string>();
                    var msgsFromB2Sub1T2 = new List <string>();

                    // bus 1 subscription 1
                    basicRedis1.Bus.Subscribe(topic1, s => { lock (msgsFromB1Sub1T1) msgsFromB1Sub1T1.Add(s); });
                    basicRedis1.Bus.Publish(topic1, t1M1B1S1);
                    waitForMessage(msgsFromB1Sub1T1, t1M1B1S1);

                    // bus 1 subscription 2
                    basicRedis1.Bus.Subscribe(topic1, s => { lock (msgsFromB1Sub2T1) msgsFromB1Sub2T1.Add(s); });
                    basicRedis1.Bus.Publish(topic1, t1M2B1S2);
                    basicRedis1.Bus.Publish(topic1, t1M3B1S2);
                    waitForMessage(msgsFromB1Sub2T1, t1M3B1S2);

                    // bus 2 subscription 1
                    basicRedis2.Bus.Subscribe(topic1, s => { lock (msgsFromB2Sub1T1) msgsFromB2Sub1T1.Add(s); });
                    basicRedis2.Bus.Publish(topic1, t1M4B2S1);
                    waitForMessage(msgsFromB2Sub1T1, t1M4B2S1);

                    // bus 2 subscription 2
                    basicRedis2.Bus.Subscribe(topic1, s => { lock (msgsFromB2Sub2T1) msgsFromB2Sub2T1.Add(s); });
                    basicRedis2.Bus.Publish(topic1, t1M5B2S1);
                    basicRedis2.Bus.Publish(topic1, t1M6B2S1);
                    waitForMessage(msgsFromB2Sub2T1, t1M6B2S1);

                    // topic2 via bus1 and bus2
                    basicRedis1.Bus.Subscribe(topic2, s => { lock (msgsFromB1Sub1T2) msgsFromB1Sub1T2.Add(s); });
                    basicRedis2.Bus.Subscribe(topic2, s => { lock (msgsFromB2Sub1T2) msgsFromB2Sub1T2.Add(s); });
                    basicRedis1.Bus.Publish(topic2, t2M7B1S1);
                    basicRedis2.Bus.Publish(topic2, t2M8B2S1);
                    waitForMessage(msgsFromB1Sub1T2, t2M8B2S1);

                    Assert.Equal(new List <string>(new[] { t1M1B1S1, t1M2B1S2, t1M3B1S2, t1M4B2S1, t1M5B2S1, t1M6B2S1 }), msgsFromB1Sub1T1);
                    Assert.Equal(new List <string>(new[] { t1M2B1S2, t1M3B1S2, t1M4B2S1, t1M5B2S1, t1M6B2S1 }), msgsFromB1Sub2T1);
                    Assert.Equal(new List <string>(new[] { t1M4B2S1, t1M5B2S1, t1M6B2S1 }), msgsFromB2Sub1T1);
                    Assert.Equal(new List <string>(new[] { t1M5B2S1, t1M6B2S1 }), msgsFromB2Sub2T1);
                    Assert.Equal(new List <string>(new[] { t2M7B1S1, t2M8B2S1, }), msgsFromB1Sub1T2);
                    Assert.Equal(new List <string>(new[] { t2M7B1S1, t2M8B2S1, }), msgsFromB2Sub1T2);
                }
        }