rack-idempotent 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/rack-idempotent/version.rb +1 -1
- data/lib/rack-idempotent.rb +19 -4
- data/spec/rack-idempotent_spec.rb +27 -1
- metadata +39 -20
data/lib/rack-idempotent.rb
CHANGED
@@ -3,7 +3,8 @@ require "rack-idempotent/version"
|
|
3
3
|
module Rack
|
4
4
|
class Idempotent
|
5
5
|
RETRY_LIMIT = 5
|
6
|
-
|
6
|
+
RETRY_HTTP_CODES = [502, 503, 504]
|
7
|
+
IDEMPOTENT_HTTP_CODES = [*RETRY_HTTP_CODES, 408]
|
7
8
|
IDEMPOTENT_ERROR_CLASSES = [Errno::ETIMEDOUT, Errno::ECONNREFUSED, Errno::EHOSTUNREACH]
|
8
9
|
|
9
10
|
class RetryLimitExceeded < Exception
|
@@ -24,6 +25,9 @@ module Rack
|
|
24
25
|
end
|
25
26
|
end
|
26
27
|
|
28
|
+
class Retryable < StandardError
|
29
|
+
end
|
30
|
+
|
27
31
|
def initialize(app)
|
28
32
|
@app= app
|
29
33
|
end
|
@@ -38,15 +42,26 @@ module Rack
|
|
38
42
|
raise HTTPException.new(status, headers, body) if IDEMPOTENT_HTTP_CODES.include?(status)
|
39
43
|
env.merge!(dup_env)
|
40
44
|
[status, headers, body]
|
41
|
-
rescue *IDEMPOTENT_ERROR_CLASSES, HTTPException => ie
|
45
|
+
rescue *IDEMPOTENT_ERROR_CLASSES, HTTPException, Retryable => ie
|
42
46
|
idempotent_exceptions << ie
|
43
47
|
if env['client.retries'] > RETRY_LIMIT - 1
|
44
48
|
raise(RetryLimitExceeded.new(idempotent_exceptions))
|
45
49
|
else
|
46
|
-
env[
|
47
|
-
|
50
|
+
if retry?(status, env["REQUEST_METHOD"])
|
51
|
+
env['client.retries'] += 1
|
52
|
+
retry
|
53
|
+
else
|
54
|
+
raise
|
55
|
+
end
|
48
56
|
end
|
49
57
|
end
|
50
58
|
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def retry?(response_status, request_method)
|
63
|
+
RETRY_HTTP_CODES.include?(response_status) || request_method == "GET"
|
64
|
+
end
|
65
|
+
|
51
66
|
end
|
52
67
|
end
|
@@ -37,6 +37,14 @@ describe Rack::Idempotent do
|
|
37
37
|
env['client.retries'].should == 2
|
38
38
|
end
|
39
39
|
|
40
|
+
it "should retry Rack::Idempotent::Retryable" do
|
41
|
+
RaiseUp.errors = [Rack::Idempotent::Retryable, Rack::Idempotent::Retryable]
|
42
|
+
client.get("/alsodoesntmatter")
|
43
|
+
|
44
|
+
env = CaptureEnv.env
|
45
|
+
env['client.retries'].should == 2
|
46
|
+
end
|
47
|
+
|
40
48
|
it "should raise Rack::Idempotent::RetryLimitExceeded when retry limit is reached" do
|
41
49
|
RaiseUp.errors = (Rack::Idempotent::RETRY_LIMIT + 1).times.map{|i| Errno::ETIMEDOUT}
|
42
50
|
|
@@ -47,7 +55,7 @@ describe Rack::Idempotent do
|
|
47
55
|
end
|
48
56
|
|
49
57
|
[502, 503, 504, 408].each do |code|
|
50
|
-
it "retries #{code}" do
|
58
|
+
it "retries GET #{code}" do
|
51
59
|
RaiseUp.errors = [code]
|
52
60
|
client.get("/something")
|
53
61
|
env = CaptureEnv.env
|
@@ -55,6 +63,24 @@ describe Rack::Idempotent do
|
|
55
63
|
end
|
56
64
|
end
|
57
65
|
|
66
|
+
[502, 503, 504].each do |code|
|
67
|
+
it "retries POST #{code}" do
|
68
|
+
RaiseUp.errors = [code]
|
69
|
+
client.post("/something")
|
70
|
+
env = CaptureEnv.env
|
71
|
+
env['client.retries'].should == 1
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
it "doesn't retry POST when return code is 408" do
|
76
|
+
RaiseUp.errors = [408]
|
77
|
+
lambda do
|
78
|
+
client.post("/something")
|
79
|
+
end.should raise_error(Rack::Idempotent::HTTPException)
|
80
|
+
env = CaptureEnv.env
|
81
|
+
env['client.retries'].should == 0
|
82
|
+
end
|
83
|
+
|
58
84
|
it "should store exceptions raised" do
|
59
85
|
RaiseUp.errors = [502, Errno::ECONNREFUSED, 408, 504, Errno::EHOSTUNREACH, Errno::ETIMEDOUT]
|
60
86
|
errors = RaiseUp.errors.dup
|
metadata
CHANGED
@@ -1,23 +1,33 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-idempotent
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
5
|
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 2
|
10
|
+
version: 0.0.2
|
6
11
|
platform: ruby
|
7
|
-
authors:
|
12
|
+
authors:
|
8
13
|
- Ines Sombra
|
9
14
|
autorequire:
|
10
15
|
bindir: bin
|
11
16
|
cert_chain: []
|
12
|
-
|
17
|
+
|
18
|
+
date: 2012-02-07 00:00:00 Z
|
13
19
|
dependencies: []
|
20
|
+
|
14
21
|
description: Retry logic for rack-client
|
15
|
-
email:
|
22
|
+
email:
|
16
23
|
- isombra@engineyard.com
|
17
24
|
executables: []
|
25
|
+
|
18
26
|
extensions: []
|
27
|
+
|
19
28
|
extra_rdoc_files: []
|
20
|
-
|
29
|
+
|
30
|
+
files:
|
21
31
|
- .gitignore
|
22
32
|
- .travis.yml
|
23
33
|
- Gemfile
|
@@ -29,30 +39,39 @@ files:
|
|
29
39
|
- rack-idempotent.gemspec
|
30
40
|
- spec/rack-idempotent_spec.rb
|
31
41
|
- spec/spec_helper.rb
|
32
|
-
homepage:
|
42
|
+
homepage: ""
|
33
43
|
licenses: []
|
44
|
+
|
34
45
|
post_install_message:
|
35
46
|
rdoc_options: []
|
36
|
-
|
47
|
+
|
48
|
+
require_paths:
|
37
49
|
- lib
|
38
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
39
51
|
none: false
|
40
|
-
requirements:
|
41
|
-
- -
|
42
|
-
- !ruby/object:Gem::Version
|
43
|
-
|
44
|
-
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
hash: 3
|
56
|
+
segments:
|
57
|
+
- 0
|
58
|
+
version: "0"
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
45
60
|
none: false
|
46
|
-
requirements:
|
47
|
-
- -
|
48
|
-
- !ruby/object:Gem::Version
|
49
|
-
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
hash: 3
|
65
|
+
segments:
|
66
|
+
- 0
|
67
|
+
version: "0"
|
50
68
|
requirements: []
|
69
|
+
|
51
70
|
rubyforge_project:
|
52
71
|
rubygems_version: 1.8.10
|
53
72
|
signing_key:
|
54
73
|
specification_version: 3
|
55
74
|
summary: Retry logic for rack-client
|
56
|
-
test_files:
|
75
|
+
test_files:
|
57
76
|
- spec/rack-idempotent_spec.rb
|
58
77
|
- spec/spec_helper.rb
|