- Notifications
You must be signed in to change notification settings - Fork14
Rack middleware ensuring at most once requests for mutating endpoints.
License
qonto/idempotent-request
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Rack middleware ensuring at most once requests for mutating endpoints.
Add this line to your application's Gemfile:
gem'idempotent-request'
And then execute:
$ bundle
Or install it yourself as:
$ gem install idempotent-request
- Front-end generates a unique
key
then a user goes to a specific route (for example, transfer page). - When user clicks "Submit" button, the
key
is sent in the headeridempotency-key
and back-end stores server response into redis. - All the consecutive requests with the
key
won't be executer by the server and the result of previous response (2) will be fetched from redis. - Once the user leaves or refreshes the page, front-end should re-generate the key.
# application.rbconfig.middleware.useIdempotentRequest::Middleware,storage:IdempotentRequest::RedisStorage.new(::Redis.current,expire_time:1.day),policy:YOUR_CLASS
To define a policy, whether a request should be idempotent, you have to provider a class with the following interface:
classPolicyattr_reader:requestdefinitialize(request)@request=requestenddefshould?# request is Rack::Request classendend
# application.rbconfig.middleware.useIdempotentRequest::Middleware,storage:IdempotentRequest::RedisStorage.new(::Redis.current,expire_time:1.day),policy:IdempotentRequest::Policyconfig.idempotent_routes=[{controller::'v1/transfers',action::create},]
# lib/idempotent-request/policy.rbmoduleIdempotentRequestclassPolicyattr_reader:requestdefinitialize(request)@request=requestenddefshould?route=Rails.application.routes.recognize_path(request.path,method:request.request_method)Rails.application.config.idempotent_routes.any?do |idempotent_route|idempotent_route[:controller] ==route[:controller].to_sym &&idempotent_route[:action] ==route[:action].to_symendendendend
# config/initializers/idempotent_request.rbActiveSupport::Notifications.subscribe('idempotent.request')do |name,start,finish,request_id,payload|notification=payload[:request].env['idempotent.request']ifnotification['read']Rails.logger.info"IdempotentRequest: Hit cached response from key#{notification['key']}, response:#{notification['read']}"elsifnotification['write']Rails.logger.info"IdempotentRequest: Write: key#{notification['key']}, status:#{notification['write'][0]}, headers:#{notification['write'][1]}, unlocked?#{notification['unlocked']}"elsifnotification['concurrent_request_response']Rails.logger.warn"IdempotentRequest: Concurrent request detected with key#{notification['key']}"endend
# application.rbconfig.middleware.useIdempotentRequest::Middleware,header_key:'X-Qonto-Idempotency-Key',# by default Idempotency-keypolicy:IdempotentRequest::Policy,callback:IdempotentRequest::RailsCallback,storage:IdempotentRequest::RedisStorage.new(::Redis.current,expire_time:1.day,namespace:'idempotency_keys'),conflict_response_status:409
Custom class to decide whether the request should be idempotent.
SeeExample of integration for rails
Where the response will be stored. Can be any class that implements the following interface:
defread(key)# read from a storageenddefwrite(key,payload)# write to a storageend
Get notified when a client sends a request with the same idempotency key:
classRailsCallbackattr_reader:requestdefinitialize(request)@request=requestenddefdetected(key:)Rails.logger.warn"IdempotentRequest request detected, key:#{key}"endend
Define http status code that should be returned when a client sends concurrent requests with the same idempotency key.
Bug reports and pull requests are welcome on GitHub athttps://github.com/[USERNAME]/idempotent-request. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to theContributor Covenant code of conduct.
The gem is available as open source under the terms of theMIT License.
Everyone interacting in the Idempotent::Request project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow thecode of conduct.
To publish a new version to rubygems, update the version inlib/version.rb
, and merge.
About
Rack middleware ensuring at most once requests for mutating endpoints.