JsonApiDotNetCore icon indicating copy to clipboard operation
JsonApiDotNetCore copied to clipboard

Explores support for idempotency

Open bart-degreed opened this issue 3 years ago • 1 comments

This PR adds opt-in support for idempotent requests in JsonApiDotNetCore, as defined by IETF.

In a microservices world, where at-least-once delivery is common practice, APIs must be able to handle duplicate requests gracefully. According to the HTTP standard, only POST and PATCH are non-idempotent by nature. Since in JSON:API there is no way to express "multiply the existing value of attribute X by 3", we only need to handle POST.

When a client includes the "Idempotency-Key" HTTP header in a POST Resource (or atomic:operations) request, the server stores the response before sending it back. A subsequent POST Resource request with the same key just returns the recorded response instead of executing the request. In such cases, it sends back the idempotency key too, to inform the client this was a replay.

The mechanism applies to both success and error responses. If the client wants to retry a failed request, it should resend with another key.

The tricky part is handling concurrent requests with the same key. The chosen approach here is to wrap the entire request in a transaction, so that concurrent requests block on trying to insert a record with the same primary key. This varies per database provider and isolation level, so needs more testing. An even more scalable solution would be to use an expiring lease that is atomically obtained, but then we'd need to know the maximum duration that processing a request can take. This is problematic when non-idempotent downstream external network service calls are made from resource definitions.

References on idempotency:

  • https://tools.ietf.org/id/draft-idempotency-header-01.html
  • https://medium.com/airbnb-engineering/avoiding-double-payments-in-a-distributed-payments-system-2981f6b070bb
  • https://stripe.com/docs/idempotency / https://stripe.com/docs/api/idempotent_requests
  • https://brandur.org/idempotency-keys

References on capturing request/response bodies in ASP.NET:

  • https://elanderson.net/2019/12/log-requests-and-responses-in-asp-net-core-3/
  • https://stackoverflow.com/questions/43403941/how-to-read-asp-net-core-response-body
  • https://exceptionnotfound.net/using-middleware-to-log-requests-and-responses-in-asp-net-core/
  • https://github.com/Treit/LoggingMiddlewareExample

bart-degreed avatar Dec 23 '21 17:12 bart-degreed

Codecov Report

Merging #1132 (b98933e) into master (ea3ab72) will increase coverage by 0.03%. The diff coverage is 95.94%.

:exclamation: Current head b98933e differs from pull request most recent head 08f8141. Consider uploading reports for the commit 08f8141 to get more accurate results

@@            Coverage Diff             @@
##           master    #1132      +/-   ##
==========================================
+ Coverage   92.61%   92.64%   +0.03%     
==========================================
  Files         242      244       +2     
  Lines        7701     7847     +146     
==========================================
+ Hits         7132     7270     +138     
- Misses        569      577       +8     
Impacted Files Coverage Δ
...nApiDotNetCore/Middleware/NoIdempotencyProvider.cs 25.00% <25.00%> (ø)
...nApiDotNetCore/Middleware/IdempotencyMiddleware.cs 97.45% <97.45%> (ø)
...AtomicOperations/EntityFrameworkCoreTransaction.cs 100.00% <100.00%> (ø)
...perations/EntityFrameworkCoreTransactionFactory.cs 100.00% <100.00%> (ø)
...Core/Configuration/ApplicationBuilderExtensions.cs 100.00% <100.00%> (ø)
...NetCore/Configuration/JsonApiApplicationBuilder.cs 100.00% <100.00%> (ø)
...JsonApiDotNetCore/Middleware/IdempotentResponse.cs 100.00% <100.00%> (ø)
src/JsonApiDotNetCore.Annotations/ArgumentGuard.cs 73.33% <0.00%> (-6.67%) :arrow_down:
...ApiDotNetCore.SourceGenerators/SourceCodeWriter.cs 97.47% <0.00%> (-0.09%) :arrow_down:
...Core/Repositories/EntityFrameworkCoreRepository.cs 99.29% <0.00%> (-0.03%) :arrow_down:
... and 15 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

codecov[bot] avatar Dec 23 '21 18:12 codecov[bot]