Skip to content

Commit

Permalink
dh: Add Procrastinating after N requests handler
Browse files Browse the repository at this point in the history
  • Loading branch information
rmandvikar committed Feb 2, 2024
1 parent 2d93045 commit 96450ae
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 0 deletions.
57 changes: 57 additions & 0 deletions src/rm.DelegatingHandlers/ProcrastinatingAfterNRequestsHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace rm.DelegatingHandlers;

/// <summary>
/// Causes delay after N requests to induce http timeouts.
/// </summary>
public class ProcrastinatingAfterNRequestsHandler : DelegatingHandler
{
private readonly IProcrastinatingAfterNRequestsHandlerSettings procrastinatingAfterNRequestsHandlerSettings;

private long n;

/// <inheritdoc cref="ProcrastinatingAfterNRequestsHandler" />
public ProcrastinatingAfterNRequestsHandler(
IProcrastinatingAfterNRequestsHandlerSettings procrastinatingAfterNRequestsHandlerSettings)
{
this.procrastinatingAfterNRequestsHandlerSettings = procrastinatingAfterNRequestsHandlerSettings
?? throw new ArgumentNullException(nameof(procrastinatingAfterNRequestsHandlerSettings));
}

protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
if (Interlocked.Read(ref n) >= procrastinatingAfterNRequestsHandlerSettings.N)
{
await Task.Delay(procrastinatingAfterNRequestsHandlerSettings.DelayInMilliseconds, cancellationToken)
.ConfigureAwait(false);
}

var response = await base.SendAsync(request, cancellationToken)
.ConfigureAwait(false);

if (Interlocked.Read(ref n) < procrastinatingAfterNRequestsHandlerSettings.N)
{
Interlocked.Increment(ref n);
}

return response;
}
}

public interface IProcrastinatingAfterNRequestsHandlerSettings
{
long N { get; }
int DelayInMilliseconds { get; }
}

public record class ProcrastinatingAfterNRequestsHandlerSettings : IProcrastinatingAfterNRequestsHandlerSettings
{
public long N { get; init; }
public int DelayInMilliseconds { get; init; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
using System.Diagnostics;
using System.Net.Http;
using AutoFixture;
using AutoFixture.AutoMoq;
using NUnit.Framework;
using rm.DelegatingHandlers;

namespace rm.DelegatingHandlersTest;

[TestFixture]
public class ProcrastinatingAfterNRequestsHandlerTests
{
[Retry(5)]
[Test]
public async Task Procrastinates()
{
var fixture = new Fixture().Customize(new AutoMoqCustomization());

var n = 5;
var delayInMilliseconds = 25;
var procrastinatingAfterNRequestsHandler = new ProcrastinatingAfterNRequestsHandler(
new ProcrastinatingAfterNRequestsHandlerSettings
{
N = n,
DelayInMilliseconds = delayInMilliseconds,
});

using var invoker = HttpMessageInvokerFactory.Create(
fixture.Create<HttpMessageHandler>(), procrastinatingAfterNRequestsHandler);

for (int i = 0; i < n; i++)
{
using var _ = await invoker.SendAsync(fixture.Create<HttpRequestMessage>(), CancellationToken.None);
}
using var requestMessage = fixture.Create<HttpRequestMessage>();
var stopwatch = Stopwatch.StartNew();
using var response = await invoker.SendAsync(requestMessage, CancellationToken.None);
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedMilliseconds);

Assert.GreaterOrEqual(stopwatch.ElapsedMilliseconds, delayInMilliseconds);
}

[Retry(5)]
[Test]
public async Task Procrastinates_After()
{
var fixture = new Fixture().Customize(new AutoMoqCustomization());

var n = 5;
var delayInMilliseconds = 25;
var procrastinatingAfterNRequestsHandler = new ProcrastinatingAfterNRequestsHandler(
new ProcrastinatingAfterNRequestsHandlerSettings
{
N = n,
DelayInMilliseconds = delayInMilliseconds,
});

using var invoker = HttpMessageInvokerFactory.Create(
fixture.Create<HttpMessageHandler>(), procrastinatingAfterNRequestsHandler);

for (int i = 0; i < n + 1; i++)
{
using var _ = await invoker.SendAsync(fixture.Create<HttpRequestMessage>(), CancellationToken.None);
}
using var requestMessage = fixture.Create<HttpRequestMessage>();
var stopwatch = Stopwatch.StartNew();
using var response = await invoker.SendAsync(requestMessage, CancellationToken.None);
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedMilliseconds);

Assert.GreaterOrEqual(stopwatch.ElapsedMilliseconds, delayInMilliseconds);
}

[Test]
public async Task Does_Not_Procrastinate()
{
var fixture = new Fixture().Customize(new AutoMoqCustomization());

var n = 5;
var delayInMilliseconds = 1000;
var procrastinatingAfterNRequestsHandler = new ProcrastinatingAfterNRequestsHandler(
new ProcrastinatingAfterNRequestsHandlerSettings
{
N = n,
DelayInMilliseconds = delayInMilliseconds,
});

using var invoker = HttpMessageInvokerFactory.Create(
fixture.Create<HttpMessageHandler>(), procrastinatingAfterNRequestsHandler);

using var requestMessage = fixture.Create<HttpRequestMessage>();
var stopwatch = Stopwatch.StartNew();
using var response = await invoker.SendAsync(requestMessage, CancellationToken.None);
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedMilliseconds);

Assert.Less(stopwatch.ElapsedMilliseconds, delayInMilliseconds);
}
}

0 comments on commit 96450ae

Please sign in to comment.