Skip to content

Commit

Permalink
Add SearchFlip::Criteria#http_timeout (#28)
Browse files Browse the repository at this point in the history
  • Loading branch information
mrkamel authored Jul 30, 2021
1 parent f9126e2 commit 3a37397
Show file tree
Hide file tree
Showing 9 changed files with 85 additions and 19 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@

# CHANGELOG

## v3.5.0

* Add `SearchFlip::Criteria#http_timeout` to allow specifying timeouts on
a query level

## v3.4.0

* Expose `Http#timeout` via `SearchFlip::HTTPClient`
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,14 @@ Specify a timeout to limit query processing time:
CommentIndex.timeout("3s").execute
```

* `http_timeout`

Specify a http timeout for the request which will be send to Elasticsearch:

```ruby
CommentIndex.http_timeout(3).execute
```

* `terminate_after`

Activate early query termination to stop query processing after the specified
Expand Down
11 changes: 8 additions & 3 deletions lib/search_flip.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,15 @@
require "search_flip/model"

module SearchFlip
class NotSupportedError < StandardError; end
class ConnectionError < StandardError; end
class Error < StandardError; end

class ResponseError < StandardError
class NotSupportedError < Error; end

class HttpError < Error; end
class ConnectionError < HttpError; end
class TimeoutError < HttpError; end

class ResponseError < Error
attr_reader :code, :body

def initialize(code:, body:)
Expand Down
35 changes: 28 additions & 7 deletions lib/search_flip/criteria.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ class Criteria

attr_accessor :target, :profile_value, :source_value, :suggest_values, :includes_values,
:eager_load_values, :preload_values, :failsafe_value, :scroll_args, :terminate_after_value,
:timeout_value, :preference_value, :search_type_value, :routing_value, :track_total_hits_value
:timeout_value, :preference_value, :search_type_value, :routing_value, :track_total_hits_value,
:http_timeout_value

# Creates a new criteria while merging the attributes (constraints,
# settings, etc) of the current criteria with the attributes of another one
Expand All @@ -47,7 +48,7 @@ def merge(other)
[
:profile_value, :failsafe_value, :terminate_after_value, :timeout_value, :offset_value,
:limit_value, :scroll_args, :source_value, :preference_value, :search_type_value,
:routing_value, :track_total_hits_value, :explain_value
:routing_value, :track_total_hits_value, :explain_value, :http_timeout_value
].each do |name|
criteria.send(:"#{name}=", other.send(name)) unless other.send(name).nil?
end
Expand Down Expand Up @@ -148,6 +149,22 @@ def timeout(value)
end
end

# Specifies a http timeout, such that a SearchFlip::TimeoutError will be
# thrown when the request times out.
#
# @example
# ProductIndex.http_timeout(3).search("hello world")
#
# @param value [Fixnum] The timeout value
#
# @return [SearchFlip::Criteria] A newly created extended criteria

def http_timeout(value)
fresh.tap do |criteria|
criteria.http_timeout_value = value
end
end

# Specifies early query termination, such that the processing will be
# stopped after the specified number of results has been accumulated.
#
Expand Down Expand Up @@ -330,10 +347,13 @@ def delete(params = {})
dupped_request.delete(:from)
dupped_request.delete(:size)

http_request = connection.http_client
http_request = http_request.timeout(http_timeout_value) if http_timeout_value

if connection.version.to_i >= 5
connection.http_client.post("#{target.type_url}/_delete_by_query", params: request_params.merge(params), json: dupped_request)
http_request.post("#{target.type_url}/_delete_by_query", params: request_params.merge(params), json: dupped_request)
else
connection.http_client.delete("#{target.type_url}/_query", params: request_params.merge(params), json: dupped_request)
http_request.delete("#{target.type_url}/_query", params: request_params.merge(params), json: dupped_request)
end

target.refresh if SearchFlip::Config[:auto_refresh]
Expand Down Expand Up @@ -501,8 +521,8 @@ def find_each_result(options = {})
end

# Executes the search request for the current criteria, ie sends the
# request to Elasticsearch and returns the response. Connection and
# response errors will be rescued if you specify the criteria to be
# request to Elasticsearch and returns the response. Connection, timeout
# and response errors will be rescued if you specify the criteria to be
# #failsafe, such that an empty response is returned instead.
#
# @example
Expand Down Expand Up @@ -590,6 +610,7 @@ def method_missing(name, *args, &block)

def execute!
http_request = connection.http_client.headers(accept: "application/json")
http_request = http_request.timeout(http_timeout_value) if http_timeout_value

http_response =
if scroll_args && scroll_args[:id]
Expand All @@ -609,7 +630,7 @@ def execute!
end

SearchFlip::Response.new(self, SearchFlip::JSON.parse(http_response.to_s))
rescue SearchFlip::ConnectionError, SearchFlip::ResponseError => e
rescue SearchFlip::ConnectionError, SearchFlip::TimeoutError, SearchFlip::ResponseError => e
raise e unless failsafe_value

SearchFlip::Response.new(self, "took" => 0, "hits" => { "total" => 0, "hits" => [] })
Expand Down
4 changes: 4 additions & 0 deletions lib/search_flip/http_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ def execute(method, uri, options = {})
response
rescue HTTP::ConnectionError => e
raise SearchFlip::ConnectionError, e.message
rescue HTTP::TimeoutError => e
raise SearchFlip::TimeoutError, e.message
rescue HTTP::Error => e
raise SearchFlip::HttpError, e.message
end
end
end
2 changes: 1 addition & 1 deletion lib/search_flip/index.rb
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ def criteria
:page, :per, :search, :highlight, :suggest, :custom, :find_in_batches, :find_results_in_batches,
:find_each, :find_each_result, :failsafe, :total_entries, :total_count, :timeout, :terminate_after,
:records, :results, :must, :must_not, :should, :preference, :search_type, :routing,
:track_total_hits, :explain
:track_total_hits, :explain, :http_timeout

# Override to specify the type name used within Elasticsearch. Recap,
# this gem uses an individual index for each index class, because
Expand Down
2 changes: 1 addition & 1 deletion lib/search_flip/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module SearchFlip
VERSION = "3.4.0"
VERSION = "3.5.0"
end
35 changes: 29 additions & 6 deletions spec/search_flip/criteria_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@
methods = [
:profile_value, :failsafe_value, :terminate_after_value, :timeout_value,
:offset_value, :limit_value, :scroll_args, :source_value, :preference_value,
:search_type_value, :routing_value, :track_total_hits_value, :explain_value
:search_type_value, :routing_value, :track_total_hits_value, :explain_value,
:http_timeout_value
]

methods.each do |method|
Expand Down Expand Up @@ -191,6 +192,22 @@
end
end

describe "#http_timeout" do
it "sets the query timeout" do
http_client = double("client").as_null_object
allow(http_client).to receive(:timeout).and_return(http_client)
allow(http_client).to receive(:post).and_raise(SearchFlip::TimeoutError)
allow(ProductIndex.connection).to receive(:http_client).and_return(http_client)

expect { ProductIndex.http_timeout(1).execute }.to raise_error(SearchFlip::TimeoutError)
expect(http_client).to have_received(:timeout).with(1)
end

it "executes without errors" do
expect { ProductIndex.http_timeout(1).execute }.not_to raise_error
end
end

describe "#terminate_after" do
it "sets the terminate after value" do
query = ProductIndex.terminate_after(1)
Expand Down Expand Up @@ -1204,13 +1221,19 @@
end

describe "#failsafe" do
it "prevents query syntax exceptions" do
expect { ProductIndex.search("syntax/error").records }.to raise_error(SearchFlip::ResponseError)
[SearchFlip::ConnectionError, SearchFlip::TimeoutError, SearchFlip::ResponseError.new(code: "code", body: "body")].each do |error|
it "prevents #{error}" do
http_client = double("client").as_null_object
allow(http_client).to receive(:post).and_raise(error)
allow(ProductIndex.connection).to receive(:http_client).and_return(http_client)

expect { ProductIndex.all.execute }.to raise_error(error)

query = ProductIndex.failsafe(true).search("syntax/error")
query = ProductIndex.failsafe(true)

expect(query.records).to eq([])
expect(query.total_entries).to eq(0)
expect(query.records).to eq([])
expect(query.total_entries).to eq(0)
end
end
end

Expand Down
2 changes: 1 addition & 1 deletion spec/search_flip/index_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
:total_entries, :total_count, :terminate_after, :timeout, :records, :results,
:must, :must_not, :should, :find_each_result,
:find_results_in_batches, :preference, :search_type, :routing,
:track_total_hits, :explain
:track_total_hits, :explain, :http_timeout
]

methods.each do |method|
Expand Down

0 comments on commit 3a37397

Please sign in to comment.