Class: Idempo
- Inherits:
-
Object
- Object
- Idempo
- Defined in:
- lib/idempo.rb,
lib/idempo/version.rb
Defined Under Namespace
Modules: RequestFingerprint Classes: ActiveRecordBackend, ConcurrentRequest, ConcurrentRequestErrorApp, Error, MalformedIdempotencyKey, MalformedKeyErrorApp, MemoryBackend, MemoryLock, RedisBackend, ResponseStore
Constant Summary collapse
- DEFAULT_TTL_SECONDS =
30- SAVED_RESPONSE_BODY_SIZE_LIMIT =
4 * 1024 * 1024
- VERSION =
"1.2.1"
Instance Method Summary collapse
- #call(env) ⇒ Object
-
#initialize(app, backend: MemoryBackend.new, malformed_key_error_app: MalformedKeyErrorApp, compute_fingerprint_via: RequestFingerprint, concurrent_request_error_app: ConcurrentRequestErrorApp, persist_for_seconds: DEFAULT_TTL_SECONDS) ⇒ Idempo
constructor
A new instance of Idempo.
Constructor Details
#initialize(app, backend: MemoryBackend.new, malformed_key_error_app: MalformedKeyErrorApp, compute_fingerprint_via: RequestFingerprint, concurrent_request_error_app: ConcurrentRequestErrorApp, persist_for_seconds: DEFAULT_TTL_SECONDS) ⇒ Idempo
31 32 33 34 35 36 37 38 |
# File 'lib/idempo.rb', line 31 def initialize(app, backend: MemoryBackend.new, malformed_key_error_app: MalformedKeyErrorApp, compute_fingerprint_via: RequestFingerprint, concurrent_request_error_app: ConcurrentRequestErrorApp, persist_for_seconds: DEFAULT_TTL_SECONDS) @backend = backend @app = app @concurrent_request_error_app = concurrent_request_error_app @malformed_key_error_app = malformed_key_error_app @fingerprint_calculator = compute_fingerprint_via @persist_for_seconds = persist_for_seconds.to_i end |
Instance Method Details
#call(env) ⇒ Object
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/idempo.rb', line 40 def call(env) req = Rack::Request.new(env) return @app.call(env) if request_verb_idempotent?(req) return @app.call(env) unless (idempotency_key_header = extract_idempotency_key_from(env)) # The RFC requires that the Idempotency-Key header value is enclosed in quotes idempotency_key_header_value = unquote(idempotency_key_header) raise MalformedIdempotencyKey if idempotency_key_header_value == "" request_key = @fingerprint_calculator.call(idempotency_key_header_value, req) @backend.with_idempotency_key(request_key) do |store| if (stored_response = store.lookup) Measurometer.increment_counter("idempo.responses_served_from", 1, from: "store") return from_persisted_response(stored_response) end status, headers, body = @app.call(env) expires_in_seconds = (headers.delete("X-Idempo-Persist-For-Seconds") || @persist_for_seconds).to_i if response_may_be_persisted?(status, headers, body) # Body is replaced with a cached version since a Rack response body is not rewindable marshaled_response, body = serialize_response(status, headers, body) store.store(data: marshaled_response, ttl: expires_in_seconds) end Measurometer.increment_counter("idempo.responses_served_from", 1, from: "freshly-generated") [status, headers, body] end rescue MalformedIdempotencyKey Measurometer.increment_counter("idempo.responses_served_from", 1, from: "malformed-idempotency-key") @malformed_key_error_app.call(env) rescue ConcurrentRequest Measurometer.increment_counter("idempo.responses_served_from", 1, from: "conflict-concurrent-request") @concurrent_request_error_app.call(env) end |