Interview question
Test an idempotent POST retry
Proves repeated requests with one idempotency key create one resource and return a consistent result.
TL;DR
Proves repeated requests with one idempotency key create one resource and return a consistent result.
Idempotency keys, duplicate suppression, response replay, concurrency, and isolated relational constraints.
Practice the problem like a real interview: restate, reason, implement, and test.
Send the same checkout command twice with one idempotency key. Then issue two concurrent requests using a fresh key. Assert one order per key and a consistent response. Use an isolated relational database or test container because uniqueness and races are part of the behavior.
Next in Contracts & Reliability: Pagination Regression
A client times out after the first commit and retries. It receives the original order id rather than creating a second order.
I test both sequential replay and concurrency. The sequential case proves response replay; the concurrent case proves the database boundary, not just an application cache. Reusing a key with a different payload should produce a deliberate conflict.
[Fact]
public async Task Sequential_retry_replays_the_original_order()
{
await using var factory = await ApiTestFactory.WithIsolatedDatabaseAsync();
var client = factory.CreateAuthenticatedClient("buyer-1", "orders.create");
var key = "checkout-sequential-42";
var command = new { ProductId = "p-1", Quantity = 2 };
var first = await PostOrderAsync(client, key, command);
var replay = await PostOrderAsync(client, key, command);
first.EnsureSuccessStatusCode();
replay.EnsureSuccessStatusCode();
var a = await first.Content.ReadFromJsonAsync<CreateOrderResponse>();
var b = await replay.Content.ReadFromJsonAsync<CreateOrderResponse>();
Assert.Equal(a!.OrderId, b!.OrderId);
Assert.Equal(1, await factory.CountOrdersForKeyAsync(key));
var conflict = await PostOrderAsync(
client,
key,
new { ProductId = "p-1", Quantity = 3 });
Assert.Equal(HttpStatusCode.Conflict, conflict.StatusCode);
}
[Fact]
public async Task Concurrent_first_attempts_create_one_logical_order()
{
await using var factory = await ApiTestFactory.WithIsolatedDatabaseAsync();
var firstClient = factory.CreateAuthenticatedClient("buyer-1", "orders.create");
var secondClient = factory.CreateAuthenticatedClient("buyer-1", "orders.create");
var key = "checkout-concurrent-42";
var command = new { ProductId = "p-1", Quantity = 2 };
var start = new TaskCompletionSource<bool>(
TaskCreationOptions.RunContinuationsAsynchronously);
async Task<HttpResponseMessage> SendAsync(HttpClient client)
{
await start.Task;
return await PostOrderAsync(client, key, command);
}
var requests = new[] { SendAsync(firstClient), SendAsync(secondClient) };
start.SetResult(true);
var responses = await Task.WhenAll(requests);
Assert.All(responses, response => response.EnsureSuccessStatusCode());
var bodies = await Task.WhenAll(responses.Select(response =>
response.Content.ReadFromJsonAsync<CreateOrderResponse>()));
Assert.Single(bodies.Select(body => body!.OrderId).Distinct());
Assert.Equal(1, await factory.CountOrdersForKeyAsync(key));
}
private static async Task<HttpResponseMessage> PostOrderAsync(
HttpClient client,
string key,
object command)
{
using var request = new HttpRequestMessage(HttpMethod.Post, "/api/orders")
{
Content = JsonContent.Create(command)
};
request.Headers.Add("Idempotency-Key", key);
return await client.SendAsync(request);
}
The relational unique constraint is the authoritative race guard. The test database is disposable and isolated. A second test should use two clients and Task.WhenAll to force concurrent first attempts.
Share the question with a concise Core-answer excerpt and invite other developers to add their perspective.
Help improve the interview library.
Say thanks with a standalone, one-time $5 contribution. No account required.
Aporeon is shaped by Aleksandar Tomovski, a software developer with experience on both sides of technical interviews. Content is reviewed for accuracy, natural spoken delivery, useful depth, and honest trade-offs.