rsolr 1.0.8 → 1.0.9

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/CHANGES.txt CHANGED
@@ -1,5 +1,7 @@
1
1
  1.0.8
2
2
  - Fix connection refused errors in specs + add basic auth support (Denis Goeury)
3
+ - Ability to set :retry_503, the number of retry attempts for a 503 response
4
+ with a Retry-After header.
3
5
  1.0.7
4
6
  - Response body encoding is set to response charset in Ruby >= 1.9
5
7
  - Ability to set :read_timeout and :open_timeout when creating new instance of RSolr
@@ -14,4 +16,4 @@
14
16
  - Jeweler is no longer used for building the gemspec
15
17
  1.0.3
16
18
  - Proper encodings in Ruby 1.9
17
- - Applied pull request from https://github.com/mwmitchell/rsolr/pull/20
19
+ - Applied pull request from https://github.com/mwmitchell/rsolr/pull/20
data/README.rdoc CHANGED
@@ -36,6 +36,13 @@ The :response attribute contains the original response. This object contains the
36
36
  The read and connect timeout settings can be set when creating a new instance of RSolr:
37
37
  solr = RSolr.connect(:read_timeout => 120, :open_timeout => 120)
38
38
 
39
+ == Retry 503s
40
+ A 503 is usually a temporary error which RSolr may retry if requested. You may specify the number of retry attempts with the :retry_503 option.
41
+
42
+ Only requests which specify a Retry-After header will be retried, after waiting the indicated retry interval, otherwise RSolr will treat the request as a 500. You may specify a maximum Retry-After interval to wait with the :retry_after_limit option (default: one second).
43
+ solr = RSolr.connect(:retry_503 => 1, :retry_after_limit => 1)
44
+
45
+
39
46
  == Querying
40
47
  Use the #get / #post method to send search requests to the /select handler:
41
48
  response = solr.get 'select', :params => {
@@ -176,27 +183,20 @@ The default response format is Ruby. When the :wt param is set to :ruby, the res
176
183
  * Send me a pull request. Bonus points for topic branches.
177
184
 
178
185
  ==Contributors
179
- * Antoine Latter
180
- * Dmitry Lihachev
181
- * Lucas Souza
182
- * Peter Kieltyka
183
- * Rob Di Marco
186
+ * Nathan Witmer
184
187
  * Magnus Bergmark
185
- * Jonathan Rochkind
186
- * Chris Beer
187
- * Craig Smith
188
+ * shima
188
189
  * Randy Souza
189
- * Colin Steele
190
- * Peter Kieltyka
191
- * Lorenzo Riccucci
192
- * Mike Perham
193
190
  * Mat Brown
194
- * Shairon Toledo
195
- * Matthew Rudy
196
- * Fouad Mardini
197
191
  * Jeremy Hinegardner
198
- * Nathan Witmer
199
- * "shima"
192
+ * Denis Goeury
193
+ * shairon toledo
194
+ * Rob Di Marco
195
+ * Peter Kieltyka
196
+ * Mike Perham
197
+ * Lucas Souza
198
+ * Dmitry Lihachev
199
+ * Antoine Latter
200
200
 
201
201
  ==Author
202
202
 
@@ -204,4 +204,4 @@ Matt Mitchell <mailto:goodieboy@gmail.com>
204
204
 
205
205
  ==Copyright
206
206
 
207
- Copyright (c) 2008-2010 Matt Mitchell. See LICENSE for details.
207
+ Copyright (c) 2008-2010 Matt Mitchell. See LICENSE for details.
data/lib/rsolr/client.rb CHANGED
@@ -155,7 +155,7 @@ class RSolr::Client
155
155
  # then passes the request/response into +adapt_response+.
156
156
  def send_and_receive path, opts
157
157
  request_context = build_request path, opts
158
- [:open_timeout, :read_timeout].each do |k|
158
+ [:open_timeout, :read_timeout, :retry_503, :retry_after_limit].each do |k|
159
159
  request_context[k] = @options[k]
160
160
  end
161
161
  execute request_context
@@ -163,9 +163,47 @@ class RSolr::Client
163
163
 
164
164
  #
165
165
  def execute request_context
166
+
166
167
  raw_response = connection.execute self, request_context
168
+
169
+ while retry_503?(request_context, raw_response)
170
+ request_context[:retry_503] -= 1
171
+ sleep retry_after(raw_response)
172
+ raw_response = connection.execute self, request_context
173
+ end
174
+
167
175
  adapt_response(request_context, raw_response) unless raw_response.nil?
168
176
  end
177
+
178
+ def retry_503?(request_context, response)
179
+ return false if response.nil?
180
+ status = response[:status] && response[:status].to_i
181
+ return false unless status == 503
182
+ retry_503 = request_context[:retry_503]
183
+ return false unless retry_503 && retry_503 > 0
184
+ retry_after_limit = request_context[:retry_after_limit] || 1
185
+ retry_after = retry_after(response)
186
+ return false unless retry_after && retry_after <= retry_after_limit
187
+ true
188
+ end
189
+
190
+ # Retry-After can be a relative number of seconds from now, or an RFC 1123 Date.
191
+ # If the latter, attempt to convert it to a relative time in seconds.
192
+ def retry_after(response)
193
+ retry_after = Array(response[:headers]['Retry-After'] || response[:headers]['retry-after']).flatten.first.to_s
194
+ if retry_after =~ /\A[0-9]+\Z/
195
+ retry_after = retry_after.to_i
196
+ else
197
+ begin
198
+ retry_after_date = DateTime.parse(retry_after)
199
+ retry_after = retry_after_date.to_time - Time.now
200
+ retry_after = nil if retry_after < 0
201
+ rescue ArgumentError => e
202
+ retry_after = retry_after.to_i
203
+ end
204
+ end
205
+ retry_after
206
+ end
169
207
 
170
208
  # +build_request+ accepts a path and options hash,
171
209
  # then prepares a normalized hash to return for sending
@@ -15,6 +15,8 @@ class RSolr::Connection
15
15
  response = h.request request
16
16
  charset = response.type_params["charset"]
17
17
  {:status => response.code.to_i, :headers => response.to_hash, :body => force_charset(response.body, charset)}
18
+ rescue Errno::ECONNREFUSED => e
19
+ raise(Errno::ECONNREFUSED.new(request_context.inspect))
18
20
  # catch the undefined closed? exception -- this is a confirmed ruby bug
19
21
  rescue NoMethodError
20
22
  $!.message == "undefined method `closed?' for nil:NilClass" ?
@@ -5,8 +5,8 @@ module RSolr::Response
5
5
  base["response"]["docs"].tap do |d|
6
6
  d.extend PaginatedDocSet
7
7
  d.per_page = base.request[:params]["rows"]
8
- d.start = base.request[:params]["start"]
9
- d.total = base["response"]["numFound"].to_s.to_i
8
+ d.page_start = base.request[:params]["start"]
9
+ d.page_total = base["response"]["numFound"].to_s.to_i
10
10
  end
11
11
  end
12
12
  end
@@ -14,7 +14,13 @@ module RSolr::Response
14
14
  # A response module which gets mixed into the solr ["response"]["docs"] array.
15
15
  module PaginatedDocSet
16
16
 
17
- attr_accessor :start, :per_page, :total
17
+ attr_accessor :page_start, :per_page, :page_total
18
+ if not (Object.const_defined?("RUBY_ENGINE") and Object::RUBY_ENGINE=='rbx')
19
+ alias_method(:start,:page_start)
20
+ alias_method(:start=,:page_start=)
21
+ alias_method(:total,:page_total)
22
+ alias_method(:total=,:page_total=)
23
+ end
18
24
 
19
25
  # Returns the current page calculated from 'rows' and 'start'
20
26
  def current_page
@@ -48,4 +54,4 @@ module RSolr::Response
48
54
 
49
55
  end
50
56
 
51
- end
57
+ end
data/lib/rsolr.rb CHANGED
@@ -6,7 +6,7 @@ module RSolr
6
6
 
7
7
  %W(Response Char Client Error Connection Uri Xml).each{|n|autoload n.to_sym, "rsolr/#{n.downcase}"}
8
8
 
9
- def self.version; "1.0.8" end
9
+ def self.version; "1.0.9" end
10
10
 
11
11
  VERSION = self.version
12
12
 
@@ -19,4 +19,4 @@ module RSolr
19
19
  # RSolr.escape
20
20
  extend Char
21
21
 
22
- end
22
+ end
@@ -34,6 +34,38 @@ describe "RSolr::Client" do
34
34
  end
35
35
  end
36
36
 
37
+ context "execute" do
38
+ include ClientHelper
39
+ let :request_context do
40
+ {
41
+ :method => :post,
42
+ :params => {},
43
+ :data => nil,
44
+ :headers => {},
45
+ :path => '',
46
+ :uri => client.base_uri,
47
+ :retry_503 => 1
48
+ }
49
+ end
50
+ it "should retry 503s if requested" do
51
+ client.connection.should_receive(:execute).exactly(2).times.and_return(
52
+ {:status => 503, :body => "{}", :headers => {'Retry-After' => 0}},
53
+ {:status => 200, :body => "{}", :headers => {}}
54
+ )
55
+ client.execute request_context
56
+ end
57
+ it "should not retry a 503 if the retry-after is too large" do
58
+ client.connection.should_receive(:execute).exactly(1).times.and_return(
59
+ {:status => 503, :body => "{}", :headers => {'Retry-After' => 10}}
60
+ )
61
+ lambda {
62
+ Timeout.timeout(0.5) do
63
+ client.execute({:retry_after_limit => 0}.merge(request_context))
64
+ end
65
+ }.should raise_error(RSolr::Error::Http)
66
+ end
67
+ end
68
+
37
69
  context "post" do
38
70
  include ClientHelper
39
71
  it "should pass the expected params to the connection's #execute method" do
@@ -57,6 +57,27 @@ describe "RSolr::Connection" do
57
57
  subject.execute client, {:uri => URI.parse("http://localhost/some_uri"), :method => :get}
58
58
  end
59
59
  end
60
+
61
+ context "connection refused" do
62
+ let(:client) { mock.as_null_object }
63
+
64
+ let(:http) { mock(Net::HTTP).as_null_object }
65
+ let(:request_context) {
66
+ {:uri => URI.parse("http://localhost/some_uri"), :method => :get, :open_timeout => 42}
67
+ }
68
+ subject { RSolr::Connection.new }
69
+
70
+ before do
71
+ Net::HTTP.stub(:new) { http }
72
+ end
73
+
74
+ it "should configure Net:HTTP open_timeout" do
75
+ http.should_receive(:request).and_raise(Errno::ECONNREFUSED)
76
+ lambda {
77
+ subject.execute client, request_context
78
+ }.should raise_error(Errno::ECONNREFUSED, /#{request_context}/)
79
+ end
80
+ end
60
81
 
61
82
  describe "basic auth support" do
62
83
  let(:http) { mock(Net::HTTP).as_null_object }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rsolr
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.8
4
+ version: 1.0.9
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -29,11 +29,11 @@ authors:
29
29
  autorequire:
30
30
  bindir: bin
31
31
  cert_chain: []
32
- date: 2012-04-22 00:00:00.000000000Z
32
+ date: 2013-03-29 00:00:00.000000000Z
33
33
  dependencies:
34
34
  - !ruby/object:Gem::Dependency
35
35
  name: builder
36
- requirement: &70327300845420 !ruby/object:Gem::Requirement
36
+ requirement: &70317957140320 !ruby/object:Gem::Requirement
37
37
  none: false
38
38
  requirements:
39
39
  - - ! '>='
@@ -41,10 +41,10 @@ dependencies:
41
41
  version: 2.1.2
42
42
  type: :runtime
43
43
  prerelease: false
44
- version_requirements: *70327300845420
44
+ version_requirements: *70317957140320
45
45
  - !ruby/object:Gem::Dependency
46
46
  name: rake
47
- requirement: &70327300844920 !ruby/object:Gem::Requirement
47
+ requirement: &70317957139580 !ruby/object:Gem::Requirement
48
48
  none: false
49
49
  requirements:
50
50
  - - ~>
@@ -52,10 +52,10 @@ dependencies:
52
52
  version: 0.9.2
53
53
  type: :development
54
54
  prerelease: false
55
- version_requirements: *70327300844920
55
+ version_requirements: *70317957139580
56
56
  - !ruby/object:Gem::Dependency
57
57
  name: rdoc
58
- requirement: &70327300844460 !ruby/object:Gem::Requirement
58
+ requirement: &70317957138980 !ruby/object:Gem::Requirement
59
59
  none: false
60
60
  requirements:
61
61
  - - ~>
@@ -63,10 +63,10 @@ dependencies:
63
63
  version: 3.9.4
64
64
  type: :development
65
65
  prerelease: false
66
- version_requirements: *70327300844460
66
+ version_requirements: *70317957138980
67
67
  - !ruby/object:Gem::Dependency
68
68
  name: rspec
69
- requirement: &70327300844000 !ruby/object:Gem::Requirement
69
+ requirement: &70317957138400 !ruby/object:Gem::Requirement
70
70
  none: false
71
71
  requirements:
72
72
  - - ~>
@@ -74,7 +74,7 @@ dependencies:
74
74
  version: 2.6.0
75
75
  type: :development
76
76
  prerelease: false
77
- version_requirements: *70327300844000
77
+ version_requirements: *70317957138400
78
78
  description: RSolr aims to provide a simple and extensible library for working with
79
79
  Solr
80
80
  email: