rsolr 1.0.8 → 1.0.9

Sign up to get free protection for your applications and to get access to all the features.
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: