Example #1
0
        internal async Task RegisterMetrics_WhenServerReturns200Ok_ReturnsTrue(
            UnleashSettings settings,
            [Frozen] Mock <FakeHttpMessageHandler> httpMessageHandler,
            [Frozen] UnleashApiClientRequestHeaders requestHeaders,
            MetricsBucket metricsBucket
            )
        {
            var jsonSerializerSettings = new NewtonsoftJsonSerializerSettings();
            var jsonSerializer         = new NewtonsoftJsonSerializer(jsonSerializerSettings);

            httpMessageHandler.SetupPostSendMetricsRequestForSuccess(requestHeaders);

            var httpClient = new HttpClient(httpMessageHandler.Object)
            {
                BaseAddress = settings.UnleashApi
            };
            var apiClient = new UnleashApiClient(httpClient, jsonSerializer, requestHeaders);

            var threadSafeMetricsBucket = new ThreadSafeMetricsBucket(metricsBucket);
            var successResult           = await apiClient.SendMetrics(threadSafeMetricsBucket, CancellationToken.None);

            Assert.True(successResult);

            httpMessageHandler.VerifyAll();
        }
Example #2
0
        internal async Task RegisterMetrics_WhenHttpExceptionOccurs_ReturnsFalse(
            HttpStatusCode responseStatusCode,
            string responseBody,
            string responseContentType,
            UnleashSettings settings,
            [Frozen] Mock <FakeHttpMessageHandler> httpMessageHandler,
            [Frozen] UnleashApiClientRequestHeaders requestHeaders,
            MetricsBucket metricsBucket
            )
        {
            var jsonSerializerSettings = new NewtonsoftJsonSerializerSettings();
            var jsonSerializer         = new NewtonsoftJsonSerializer(jsonSerializerSettings);

            httpMessageHandler.SetupPostSendMetricsRequestForException(requestHeaders, responseStatusCode, responseBody, responseContentType);

            var httpClient = new HttpClient(httpMessageHandler.Object)
            {
                BaseAddress = settings.UnleashApi
            };
            var apiClient = new UnleashApiClient(httpClient, jsonSerializer, requestHeaders);

            var threadSafeMetricsBucket = new ThreadSafeMetricsBucket(metricsBucket);
            var successResult           = await apiClient.SendMetrics(threadSafeMetricsBucket, CancellationToken.None);

            Assert.False(successResult);

            httpMessageHandler.VerifyAll();
        }
Example #3
0
        internal async Task ExecuteAsync_WhenHttpExceptionOccurs_CompletesSuccessfully(
            HttpStatusCode statusCode,
            string responseBody,
            string responseContentType,
            [Frozen] UnleashSettings settings,
            [Frozen] Mock <FakeHttpMessageHandler> httpMessageHandler,
            [Frozen] Mock <IUnleashApiClientFactory> apiClientFactoryMock,
            [Frozen] UnleashApiClientRequestHeaders requestHeaders,
            [Frozen] MetricsBucket metricsBucket)
        {
            var jsonSerializerSettings = new NewtonsoftJsonSerializerSettings();
            var jsonSerializer         = new NewtonsoftJsonSerializer(jsonSerializerSettings);

            var threadSafeMetricsBucket = new ThreadSafeMetricsBucket(metricsBucket);
            var backgroundTask          = new ClientMetricsBackgroundTask(apiClientFactoryMock.Object, settings, threadSafeMetricsBucket);

            httpMessageHandler.SetupPostSendMetricsRequestForException(requestHeaders, statusCode, responseBody, responseContentType);

            var httpClient = new HttpClient(httpMessageHandler.Object)
            {
                BaseAddress = settings.UnleashApi
            };
            var apiClient = new UnleashApiClient(httpClient, jsonSerializer, requestHeaders);

            apiClientFactoryMock.Setup(cf => cf.CreateClient()).Returns(apiClient);

            await backgroundTask.ExecuteAsync(CancellationToken.None);

            httpMessageHandler.VerifyAll();
            apiClientFactoryMock.VerifyAll();
        }
Example #4
0
        public void RegisterCount_WhenInvokedFromMultipleThreadsAndCheckedIncrementally_BehavesPredictably(int maxDegreeOfParallelism, long totalRegistrations, int featureToggleCount, double breakPercent)
        {
            var metricsBucket = new ThreadSafeMetricsBucket();

            var options = new ParallelOptions
            {
                MaxDegreeOfParallelism = maxDegreeOfParallelism
            };

            var breaks = totalRegistrations * breakPercent;

            var toggleCounter = 0L;
            var recordedTasks = 0L;
            var exceptions    = new ConcurrentBag <Exception>();
            var result        = Parallel.For(1, totalRegistrations + 1, options, state =>
            {
                try
                {
                    Interlocked.Increment(ref recordedTasks);

                    var toggleName = (state % featureToggleCount).ToString();
                    var active     = DateTime.Now.Ticks % 2 == 0;

                    metricsBucket.RegisterCount(toggleName, active);

                    if (state % breaks == 0)
                    {
                        using (metricsBucket.StopCollectingMetrics(out var bucket))
                        {
                            var count = GetNumberOfRegistrations(bucket);

                            Interlocked.Add(ref toggleCounter, count);
                            _testOutputHelper.WriteLine($"Locking bucket at #{state} with {toggleCounter} toggle registrations");
                        }
                    }
        public void SingleBucket_OneCountPerTask3()
        {
            var metricsBucket = new ThreadSafeMetricsBucket();

            var options = new ParallelOptions
            {
                MaxDegreeOfParallelism = 10,
            };

            var  totalTasks    = 1000000L;
            var  breaks        = totalTasks / 10;
            long toggleCounter = 0;
            long recordedTasks = 0;

            var result = Parallel.For(1, totalTasks + 1, options, state =>
            {
                Interlocked.Increment(ref recordedTasks);

                var toggleName = (state % 50).ToString();
                var active     = DateTime.Now.Ticks % 2 == 0;

                metricsBucket.RegisterCount(toggleName, active);

                if (state % breaks == 0)
                {
                    using (metricsBucket.StopCollectingMetrics(out var bucket))
                    {
                        var count = GetNumberOfRegistrations(bucket);

                        Interlocked.Add(ref toggleCounter, count);
                        Console.WriteLine($"Locking bucket at #{state} with {toggleCounter} toggle registrations");
                    }
                }
        public void SingleBucket_No_WriteOperations()
        {
            var metricsBucket = new ThreadSafeMetricsBucket();

            var options = new ParallelOptions
            {
                MaxDegreeOfParallelism = 50,
            };

            var totalTasks        = 1000000;
            var numFeatureToggles = 50;

            Parallel.For(0, totalTasks, options, state =>
            {
                var i         = (state % numFeatureToggles).ToString();
                var predicate = DateTime.Now.Ticks % 2 == 0;

                metricsBucket.RegisterCount(i, predicate);
            });

            using (metricsBucket.StopCollectingMetrics(out var bucket))
            {
                bucket.Toggles.Count.Should().Be(numFeatureToggles);

                // Should be cleared
                var count = GetNumberOfRegistrations(bucket);
                count.Should().Be(totalTasks);
            }
        }
        public UnleashServices(UnleashSettings settings, IRandom random, IUnleashApiClientFactory unleashApiClientFactory,
                               IUnleashScheduledTaskManager scheduledTaskManager, IToggleCollectionCache toggleCollectionCache,
                               IEnumerable <IStrategy> strategies)
        {
            this.Random = random ?? throw new ArgumentNullException(nameof(random));

            this.scheduledTaskManager = scheduledTaskManager ?? throw new ArgumentNullException(nameof(scheduledTaskManager));

            var settingsValidator = new UnleashSettingsValidator();

            settingsValidator.Validate(settings);

            StrategyMap = BuildStrategyMap(strategies?.ToArray() ?? new IStrategy[0]);

            CancellationToken = cancellationTokenSource.Token;

            var cachedFilesResult = toggleCollectionCache.Load(cancellationTokenSource.Token).GetAwaiter().GetResult();

            cacheMiss = cachedFilesResult.IsCacheMiss;


            ToggleCollection = new ThreadSafeToggleCollection
            {
                Instance = cachedFilesResult.InitialToggleCollection ?? new ToggleCollection()
            };

            MetricsBucket = new ThreadSafeMetricsBucket();

            var scheduledTasks = CreateScheduledTasks(settings, unleashApiClientFactory, toggleCollectionCache, cachedFilesResult);

            scheduledTaskManager.Configure(scheduledTasks, CancellationToken);
        }
 public ClientMetricsBackgroundTask(
     IUnleashApiClient apiClient,
     UnleashSettings settings,
     ThreadSafeMetricsBucket metricsBucket)
 {
     this.apiClient     = apiClient;
     this.settings      = settings;
     this.metricsBucket = metricsBucket;
 }
        public async Task SendMetrics_WhenInvoked_CompletesSuccessfully()
        {
            var metricsBucket = new ThreadSafeMetricsBucket();

            metricsBucket.RegisterCount("Demo123", true);
            metricsBucket.RegisterCount("Demo123", false);

            var result = await Client.SendMetrics(metricsBucket, CancellationToken.None);

            Assert.True(result);

            // Check result:
            // http://unleash.herokuapp.com/#/features/view/Demo123
            // http://unleash.herokuapp.com/api/admin/metrics/feature-toggles
        }
        public async Task SendMetrics_Success()
        {
            var metricsBucket = new ThreadSafeMetricsBucket();

            metricsBucket.RegisterCount("Demo123", true);
            metricsBucket.RegisterCount("Demo123", false);

            var result = await api.SendMetrics(metricsBucket, CancellationToken.None);

            result.Should().Be(true);

            // Check result:
            // http://unleash.herokuapp.com/#/features/view/Demo123
            // http://unleash.herokuapp.com/api/admin/metrics/feature-toggles
        }
Example #11
0
        public async Task <bool> SendMetrics(ThreadSafeMetricsBucket metrics, CancellationToken cancellationToken)
        {
            const string requestUri = "api/client/metrics";

            var memoryStream = new MemoryStream();

            using (metrics.StopCollectingMetrics(out var bucket))
            {
                jsonSerializer.Serialize(memoryStream, new ClientMetrics
                {
                    AppName    = clientRequestHeaders.AppName,
                    InstanceId = clientRequestHeaders.InstanceTag,
                    Bucket     = bucket
                });
            }

            return(await Post(requestUri, memoryStream, cancellationToken).ConfigureAwait(false));
        }
Example #12
0
        public void RegisterCount_WhenInvokedFromMultipleThreadsAndCheckedAtCompletion_BehavesPredictably(int maxDegreeOfParallelism, int totalRegistrations, int featureToggleCount)
        {
            var metricsBucket = new ThreadSafeMetricsBucket();

            var options = new ParallelOptions
            {
                MaxDegreeOfParallelism = maxDegreeOfParallelism
            };

            var exceptions = new ConcurrentBag <Exception>();
            var result     = Parallel.For(0, totalRegistrations, options, state =>
            {
                try
                {
                    var toggleName = (state % featureToggleCount).ToString();
                    var active     = DateTime.Now.Ticks % 2 == 0;

                    metricsBucket.RegisterCount(toggleName, active);
                }
                catch (Exception exc)
                {
                    exceptions.Add(exc);
                }
            });

            if (exceptions.Count > 0)
            {
                throw new AggregateException(exceptions);
            }

            Assert.True(result.IsCompleted);

            using (metricsBucket.StopCollectingMetrics(out var bucket))
            {
                Assert.Equal(featureToggleCount, bucket.Toggles.Count);

                var actualRegistrations = GetNumberOfRegistrations(bucket);
                Assert.Equal(totalRegistrations, actualRegistrations);
            }
        }
 public Task <bool> SendMetrics(ThreadSafeMetricsBucket metricsBucket, CancellationToken cancellationToken)
 {
     return(Task.FromResult(true));
 }
Example #14
0
        public UnleashServices(UnleashSettings settings, Dictionary <string, IStrategy> strategyMap)
        {
            var fileSystem = settings.FileSystem ?? new FileSystem(settings.Encoding);

            var backupFile     = settings.GetFeatureToggleFilePath();
            var etagBackupFile = settings.GetFeatureToggleETagFilePath();

            // Cancellation
            CancellationToken = cancellationTokenSource.Token;
            ContextProvider   = settings.UnleashContextProvider;


            var loader            = new CachedFilesLoader(settings.JsonSerializer, fileSystem, backupFile, etagBackupFile);
            var cachedFilesResult = loader.EnsureExistsAndLoad();

            ToggleCollection = new ThreadSafeToggleCollection
            {
                Instance = cachedFilesResult.InitialToggleCollection ?? new ToggleCollection()
            };

            MetricsBucket = new ThreadSafeMetricsBucket();

            IUnleashApiClient apiClient;

            if (settings.UnleashApiClient == null)
            {
                var httpClient = settings.HttpClientFactory.Create(settings.UnleashApi);
                apiClient = new UnleashApiClient(httpClient, settings.JsonSerializer, new UnleashApiClientRequestHeaders()
                {
                    AppName           = settings.AppName,
                    InstanceTag       = settings.InstanceTag,
                    CustomHttpHeaders = settings.CustomHttpHeaders
                });
                if (settings.LoadTogglesImmediately)
                {
                    var toggles = apiClient.FetchToggles("", CancellationToken.None);
                    ToggleCollection.Instance = toggles.Result.ToggleCollection;
                }
            }
            else
            {
                // Mocked backend: fill instance collection
                apiClient = settings.UnleashApiClient;
                var toggles = apiClient.FetchToggles("", CancellationToken.None);
                ToggleCollection.Instance = toggles.Result.ToggleCollection;
            }

            scheduledTaskManager = settings.ScheduledTaskManager;

            IsMetricsDisabled = settings.SendMetricsInterval == null;

            var fetchFeatureTogglesTask = new FetchFeatureTogglesTask(
                apiClient,
                ToggleCollection,
                settings.JsonSerializer,
                fileSystem,
                backupFile,
                etagBackupFile)
            {
                ExecuteDuringStartup = true,
                Interval             = settings.FetchTogglesInterval,
                Etag = cachedFilesResult.InitialETag
            };

            var scheduledTasks = new List <IUnleashScheduledTask>()
            {
                fetchFeatureTogglesTask
            };

            if (settings.SendMetricsInterval != null)
            {
                var clientRegistrationBackgroundTask = new ClientRegistrationBackgroundTask(
                    apiClient,
                    settings,
                    strategyMap.Select(pair => pair.Key).ToList())
                {
                    Interval             = TimeSpan.Zero,
                    ExecuteDuringStartup = true
                };

                scheduledTasks.Add(clientRegistrationBackgroundTask);

                var clientMetricsBackgroundTask = new ClientMetricsBackgroundTask(
                    apiClient,
                    settings,
                    MetricsBucket)
                {
                    ExecuteDuringStartup = false,
                    Interval             = settings.SendMetricsInterval.Value
                };

                scheduledTasks.Add(clientMetricsBackgroundTask);
            }

            scheduledTaskManager.Configure(scheduledTasks, CancellationToken);
        }