private static void Run_Limit_Test(int?intervalLimit, int numberPerBurst, int numberOfBursts, int millisecondsBetweenBursts) { var actualIntervalLimit = intervalLimit ?? DefaultLimitPerSecond; var test = new RateLimitLoadTest() { NumberPerBurst = numberPerBurst, TimeBetweenBursts = TimeSpan.FromMilliseconds(millisecondsBetweenBursts), NumberOfBursts = numberOfBursts }; var result = RunTest(intervalLimit, test); var totalMilliseconds = result.TimeElapsed.TotalMilliseconds; var expectedLimit = totalMilliseconds * actualIntervalLimit / 1_000; var acceptableUpperVariance = (actualIntervalLimit * 1.0); var acceptableLowerVariance = (actualIntervalLimit * 1.15); // Allow for increased tolerance on lower limit since the rolling window does not get dequeued as quickly as it can queued var upperLimit = expectedLimit + acceptableUpperVariance; var lowerLimit = expectedLimit - acceptableLowerVariance; Assert.True( result.TotalAllowed >= lowerLimit && result.TotalAllowed <= upperLimit, $"Expected between {lowerLimit} and {upperLimit}, received {result.TotalAllowed} out of {result.TotalAttempted} within {totalMilliseconds} milliseconds."); // Rate should match for the last two intervals, which is a total of two seconds var numberOfBurstsWithinTwoIntervals = 2_000 / millisecondsBetweenBursts; var totalExpectedSent = numberOfBurstsWithinTwoIntervals * numberPerBurst; var totalExpectedAllowed = 2 * actualIntervalLimit; var expectedRate = totalExpectedAllowed / (float)totalExpectedSent; var lowestRate = expectedRate - 0.40f; if (lowestRate < 0) { lowestRate = expectedRate / 2; } var highestRate = expectedRate + 0.40f; Assert.True( result.ReportedRate >= lowestRate && result.ReportedRate <= highestRate, $"Expected rate between {lowestRate} and {highestRate}, received {result.ReportedRate}."); }
private static void Run_Limit_Test(int?intervalLimit, int numberPerBurst, int numberOfBursts, int millisecondsBetweenBursts) { var actualIntervalLimit = intervalLimit ?? DefaultLimitPerSecond; var test = new RateLimitLoadTest() { NumberPerBurst = numberPerBurst, TimeBetweenBursts = TimeSpan.FromMilliseconds(millisecondsBetweenBursts), NumberOfBursts = numberOfBursts }; var result = RunTest(intervalLimit, test); var totalMilliseconds = result.TimeElapsed.TotalMilliseconds; var expectedLimit = totalMilliseconds * actualIntervalLimit / 1_000; var acceptableDifference = (actualIntervalLimit * 0.80); var upperLimit = expectedLimit + acceptableDifference; var lowerLimit = expectedLimit - acceptableDifference; Assert.True( result.TotalAllowed >= lowerLimit && result.TotalAllowed <= upperLimit, $"Expected between {lowerLimit} and {upperLimit}, received {result.TotalAllowed} out of {result.TotalAttempted} within {totalMilliseconds} milliseconds."); // Rate should match for the last two intervals, which is a total of two seconds var numberOfBurstsWithinTwoIntervals = 2_000 / millisecondsBetweenBursts; var totalExpectedSent = numberOfBurstsWithinTwoIntervals * numberPerBurst; var totalExpectedAllowed = 2 * actualIntervalLimit; var expectedRate = totalExpectedAllowed / (float)totalExpectedSent; var maxPercentVariance = 0.35f; var lowestRate = expectedRate - maxPercentVariance; var highestRate = expectedRate + maxPercentVariance; Assert.True( result.ReportedRate >= lowestRate && result.ReportedRate <= highestRate, $"Expected rate between {lowestRate} and {highestRate}, received {result.ReportedRate}."); }
private static RateLimitResult RunTest(int?intervalLimit, RateLimitLoadTest test) { var parallelism = test.NumberPerBurst; if (parallelism > Environment.ProcessorCount) { parallelism = Environment.ProcessorCount; } var clock = new SimpleClock(); var limiter = new RateLimiter(maxTracesPerInterval: intervalLimit); var barrier = new Barrier(parallelism + 1, _ => clock.UtcNow += test.TimeBetweenBursts); var numberPerThread = test.NumberPerBurst / parallelism; var workers = new Task[parallelism]; int totalAttempted = 0; int totalAllowed = 0; for (int i = 0; i < workers.Length; i++) { workers[i] = Task.Factory.StartNew( () => { using var lease = Clock.SetForCurrentThread(clock); for (var i = 0; i < test.NumberOfBursts; i++) { // Wait for every worker to be ready for next burst barrier.SignalAndWait(); for (int j = 0; j < numberPerThread; j++) { // trace id and span id are not used in rate-limiting var spanContext = new SpanContext(TraceId.CreateFromInt(1), spanId: 1, serviceName: "Weeeee"); // pass a specific start time since there is no TraceContext var span = new Span(spanContext, DateTimeOffset.UtcNow); Interlocked.Increment(ref totalAttempted); if (limiter.Allowed(span)) { Interlocked.Increment(ref totalAllowed); } } } }, TaskCreationOptions.LongRunning); } // Wait for all workers to be ready barrier.SignalAndWait(); // We do not need to synchronize with workers anymore barrier.RemoveParticipant(); // Wait for workers to finish Task.WaitAll(workers); var result = new RateLimitResult { RateLimiter = limiter, ReportedRate = limiter.GetEffectiveRate(), TotalAttempted = totalAttempted, TotalAllowed = totalAllowed }; return(result); }
private static RateLimitResult RunTest(int?intervalLimit, RateLimitLoadTest test) { var parallelism = test.NumberPerBurst; if (parallelism > 10) { parallelism = 10; } var resetEvent = new ManualResetEventSlim(initialState: false); // Start blocked var workerReps = Enumerable.Range(1, parallelism).ToArray(); var registry = new ConcurrentQueue <Thread>(); var result = new RateLimitResult(); var start = DateTime.Now; var limiter = new RateLimiter(maxTracesPerInterval: intervalLimit); var end = DateTime.Now; var endLock = new object(); var traceContext = new TraceContext(Tracer.Instance); for (var i = 0; i < test.NumberOfBursts; i++) { var remaining = test.NumberPerBurst; var workers = workerReps .Select(t => new Thread( thread => { resetEvent.Wait(); while (remaining > 0) { Interlocked.Decrement(ref remaining); var spanContext = new SpanContext(null, traceContext, "Weeeee"); var span = new Span(spanContext, null); if (limiter.Allowed(span)) { result.Allowed.Add(span.SpanId); } else { result.Denied.Add(span.SpanId); } } lock (endLock) { end = DateTime.Now; } })); foreach (var worker in workers) { registry.Enqueue(worker); worker.Start(); } resetEvent.Set(); Thread.Sleep(test.TimeBetweenBursts); resetEvent.Reset(); } while (!registry.IsEmpty) { if (registry.TryDequeue(out var item)) { if (item.IsAlive) { registry.Enqueue(item); } } } result.RateLimiter = limiter; result.ReportedRate = limiter.GetEffectiveRate(); result.TimeElapsed = end.Subtract(start); return(result); }
private static RateLimitResult RunTest(int?intervalLimit, RateLimitLoadTest test) { var parallelism = test.NumberPerBurst; if (parallelism > 10) { parallelism = 10; } var result = new RateLimitResult(); var limiter = new RateLimiter(maxTracesPerInterval: intervalLimit); var traceContext = new TraceContext(Tracer.Instance); var barrier = new Barrier(parallelism + 1); var numberPerThread = test.NumberPerBurst / parallelism; var workers = new Task[parallelism]; for (int i = 0; i < workers.Length; i++) { workers[i] = Task.Factory.StartNew( () => { var stopwatch = new Stopwatch(); for (var i = 0; i < test.NumberOfBursts; i++) { // Wait for every worker to be ready for next burst barrier.SignalAndWait(); stopwatch.Restart(); for (int j = 0; j < numberPerThread; j++) { var spanContext = new SpanContext(null, traceContext, "Weeeee"); var span = new Span(spanContext, null); if (limiter.Allowed(span)) { result.Allowed.Add(span.SpanId); } else { result.Denied.Add(span.SpanId); } } var remainingTime = (test.TimeBetweenBursts - stopwatch.Elapsed).TotalMilliseconds; if (remainingTime > 0) { Thread.Sleep((int)remainingTime); } } }, TaskCreationOptions.LongRunning); } // Wait for all workers to be ready barrier.SignalAndWait(); var sw = Stopwatch.StartNew(); // We do not need to synchronize with workers anymore barrier.RemoveParticipant(); // Wait for workers to finish Task.WaitAll(workers); result.TimeElapsed = sw.Elapsed; result.RateLimiter = limiter; result.ReportedRate = limiter.GetEffectiveRate(); return(result); }
private static RateLimitResult RunTest(int?intervalLimit, RateLimitLoadTest test) { var parallelism = test.NumberPerBurst; if (parallelism > 10) { parallelism = 10; } var limiter = new RateLimiter(maxTracesPerInterval: intervalLimit); var barrier = new Barrier(parallelism + 1); var numberPerThread = test.NumberPerBurst / parallelism; var workers = new Task[parallelism]; int totalAttempted = 0; int totalAllowed = 0; for (int i = 0; i < workers.Length; i++) { workers[i] = Task.Factory.StartNew( () => { var stopwatch = new Stopwatch(); for (var i = 0; i < test.NumberOfBursts; i++) { // Wait for every worker to be ready for next burst barrier.SignalAndWait(); stopwatch.Restart(); for (int j = 0; j < numberPerThread; j++) { // trace id and span id are not used in rate-limiting var spanContext = new SpanContext(traceId: 1, spanId: 1, serviceName: "Weeeee"); // pass a specific start time since there is no TraceContext var span = new Span(spanContext, DateTimeOffset.UtcNow); Interlocked.Increment(ref totalAttempted); if (limiter.Allowed(span)) { Interlocked.Increment(ref totalAllowed); } } var remainingTime = (test.TimeBetweenBursts - stopwatch.Elapsed).TotalMilliseconds; if (remainingTime > 0) { Thread.Sleep((int)remainingTime); } } }, TaskCreationOptions.LongRunning); } // Wait for all workers to be ready barrier.SignalAndWait(); var sw = Stopwatch.StartNew(); // We do not need to synchronize with workers anymore barrier.RemoveParticipant(); // Wait for workers to finish Task.WaitAll(workers); var result = new RateLimitResult { TimeElapsed = sw.Elapsed, RateLimiter = limiter, ReportedRate = limiter.GetEffectiveRate(), TotalAttempted = totalAttempted, TotalAllowed = totalAllowed }; return(result); }