elastomer-client 2.0.1 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/lib/elastomer/client.rb +33 -9
- data/lib/elastomer/client/bulk.rb +51 -19
- data/lib/elastomer/client/errors.rb +6 -0
- data/lib/elastomer/middleware/limit_size.rb +30 -0
- data/lib/elastomer/version.rb +1 -1
- data/test/client/bulk_test.rb +27 -3
- data/test/client/docs_test.rb +8 -0
- data/test/client/index_test.rb +7 -0
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b2f48910ab846142b9a55a868ce832dbc4a8c1ec
|
4
|
+
data.tar.gz: 3dd8c645093818313fd9f4fb5a974591bcd33516
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 94812c6c1f76f535858d04a1fa91fafa86d65e57990d56adea77117a374b450e1d1d5267afa5d937e12490a318be32d9fee399f6758f9977fa3c8803acc9f9c0
|
7
|
+
data.tar.gz: ec5999dafd6c14225eea303331db69ce85b19819e2fcda67df2f45ef78f95e7b7eaa73849c0a4fcb2e8c7bba819e731ec444f4f7c9e4b3797233bd07c144a79a
|
data/CHANGELOG.md
CHANGED
data/lib/elastomer/client.rb
CHANGED
@@ -8,6 +8,7 @@ require "elastomer/version"
|
|
8
8
|
module Elastomer
|
9
9
|
|
10
10
|
class Client
|
11
|
+
MAX_REQUEST_SIZE = 250 * 2**20 # 250 MB
|
11
12
|
|
12
13
|
# Create a new client that can be used to make HTTP requests to the
|
13
14
|
# Elasticsearch server.
|
@@ -20,6 +21,7 @@ module Elastomer
|
|
20
21
|
# :open_timeout - the timeout in seconds when opening an HTTP connection
|
21
22
|
# :adapter - the Faraday adapter to use (defaults to :excon)
|
22
23
|
# :opaque_id - set to `true` to use the 'X-Opaque-Id' request header
|
24
|
+
# :max_request_size - the maximum allowed request size in bytes (defaults to 250 MB)
|
23
25
|
#
|
24
26
|
def initialize( opts = {} )
|
25
27
|
host = opts.fetch :host, "localhost"
|
@@ -30,14 +32,15 @@ module Elastomer
|
|
30
32
|
@host = uri.host
|
31
33
|
@port = uri.port
|
32
34
|
|
33
|
-
@read_timeout
|
34
|
-
@open_timeout
|
35
|
-
@adapter
|
36
|
-
@opaque_id
|
35
|
+
@read_timeout = opts.fetch :read_timeout, 5
|
36
|
+
@open_timeout = opts.fetch :open_timeout, 2
|
37
|
+
@adapter = opts.fetch :adapter, Faraday.default_adapter
|
38
|
+
@opaque_id = opts.fetch :opaque_id, false
|
39
|
+
@max_request_size = opts.fetch :max_request_size, MAX_REQUEST_SIZE
|
37
40
|
end
|
38
41
|
|
39
42
|
attr_reader :host, :port, :url
|
40
|
-
attr_reader :read_timeout, :open_timeout
|
43
|
+
attr_reader :read_timeout, :open_timeout, :max_request_size
|
41
44
|
|
42
45
|
# Returns true if the server is available; returns false otherwise.
|
43
46
|
def ping
|
@@ -71,9 +74,10 @@ module Elastomer
|
|
71
74
|
# Returns a Faraday::Connection
|
72
75
|
def connection
|
73
76
|
@connection ||= Faraday.new(url) do |conn|
|
74
|
-
conn.request
|
75
|
-
conn.response
|
76
|
-
conn.request
|
77
|
+
conn.request(:encode_json)
|
78
|
+
conn.response(:parse_json)
|
79
|
+
conn.request(:opaque_id) if @opaque_id
|
80
|
+
conn.request(:limit_size, :max_request_size => max_request_size) if max_request_size
|
77
81
|
|
78
82
|
if @adapter.is_a?(Array)
|
79
83
|
conn.adapter(*@adapter)
|
@@ -295,7 +299,27 @@ module Elastomer
|
|
295
299
|
# containing and 'error' field.
|
296
300
|
def handle_errors( response )
|
297
301
|
raise ServerError, response if response.status >= 500
|
298
|
-
|
302
|
+
|
303
|
+
if response.body.is_a?(Hash) && (error = response.body["error"])
|
304
|
+
# ES 2.X style
|
305
|
+
if error.is_a?(Hash)
|
306
|
+
root_cause = Array(error["root_cause"]).first || error
|
307
|
+
case root_cause["type"]
|
308
|
+
when "index_not_found_exception"; raise IndexNotFoundError, response
|
309
|
+
when "query_parsing_exception"; raise QueryParsingError, response
|
310
|
+
end
|
311
|
+
|
312
|
+
# ES 1.X style
|
313
|
+
elsif error.is_a?(String)
|
314
|
+
case error
|
315
|
+
when %r/IndexMissingException/; raise IndexNotFoundError, response
|
316
|
+
when %r/QueryParsingException/; raise QueryParsingError, response
|
317
|
+
when %r/ParseException/; raise QueryParsingError, response
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
raise RequestError, response
|
322
|
+
end
|
299
323
|
|
300
324
|
response
|
301
325
|
end
|
@@ -158,7 +158,7 @@ module Elastomer
|
|
158
158
|
# immediately.
|
159
159
|
#
|
160
160
|
class Bulk
|
161
|
-
DEFAULT_REQUEST_SIZE = 10 * 2**20
|
161
|
+
DEFAULT_REQUEST_SIZE = 10 * 2**20 # 10 MB
|
162
162
|
|
163
163
|
# Create a new bulk client for handling some of the details of
|
164
164
|
# accumulating documents to index and then formatting them properly for
|
@@ -194,7 +194,10 @@ module Elastomer
|
|
194
194
|
if value.nil?
|
195
195
|
@request_size = nil
|
196
196
|
else
|
197
|
-
|
197
|
+
value = value.to_i
|
198
|
+
value = nil if value <= 0
|
199
|
+
value = client.max_request_size if value > client.max_request_size
|
200
|
+
@request_size = value
|
198
201
|
end
|
199
202
|
end
|
200
203
|
|
@@ -370,26 +373,55 @@ module Elastomer
|
|
370
373
|
# document - Optional document for the action as a Hash or JSON encoded String
|
371
374
|
#
|
372
375
|
# Returns the response from the bulk call if one was made or nil.
|
376
|
+
# Raises RequestSizeError if the given action is larger than the
|
377
|
+
# configured requst size or the client.max_request_size
|
373
378
|
def add_to_actions( action, document = nil )
|
374
|
-
action = MultiJson.dump
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
document = MultiJson.dump document unless String === document
|
381
|
-
@actions << document
|
382
|
-
@current_request_size += document.bytesize
|
379
|
+
action = MultiJson.dump(action)
|
380
|
+
size = action.bytesize
|
381
|
+
|
382
|
+
if document
|
383
|
+
document = MultiJson.dump(document) unless document.is_a?(String)
|
384
|
+
size += document.bytesize
|
383
385
|
end
|
384
386
|
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
387
|
+
check_action_size!(size)
|
388
|
+
|
389
|
+
response = nil
|
390
|
+
begin
|
391
|
+
response = call if ready_to_send?(size)
|
392
|
+
rescue StandardError => err
|
393
|
+
raise err
|
394
|
+
ensure
|
395
|
+
@actions << action
|
396
|
+
@actions << document unless document.nil?
|
397
|
+
@current_request_size += size
|
398
|
+
@current_action_count += 1
|
390
399
|
end
|
400
|
+
|
401
|
+
response
|
402
|
+
end
|
403
|
+
|
404
|
+
# Internal: Determines if adding `size` more bytes and one more action
|
405
|
+
# will bring the current bulk request over the `request_size` limit or the
|
406
|
+
# `action_count` limit. If this method returns true, then it is time to
|
407
|
+
# dispatch the bulk request.
|
408
|
+
#
|
409
|
+
# Returns `true` of `false`
|
410
|
+
def ready_to_send?( size )
|
411
|
+
total_request_size = @current_request_size + size
|
412
|
+
total_action_count = @current_action_count + 1
|
413
|
+
|
414
|
+
(request_size && total_request_size >= request_size) ||
|
415
|
+
(action_count && total_action_count > action_count)
|
391
416
|
end
|
392
417
|
|
393
|
-
|
394
|
-
|
395
|
-
|
418
|
+
# Internal: Raises a RequestSizeError if the given size is larger than
|
419
|
+
# the configured requst size or the client.max_request_size
|
420
|
+
def check_action_size!( size )
|
421
|
+
return unless (request_size && size > request_size) || (size > client.max_request_size)
|
422
|
+
raise RequestSizeError, "Bulk action of size `#{size}` is too large"
|
423
|
+
end
|
424
|
+
|
425
|
+
end
|
426
|
+
end
|
427
|
+
end
|
@@ -81,6 +81,12 @@ module Elastomer
|
|
81
81
|
SSLError = Class.new Error
|
82
82
|
ServerError = Class.new Error
|
83
83
|
RequestError = Class.new Error
|
84
|
+
RequestSizeError = Class.new Error
|
85
|
+
|
86
|
+
# Provide some nice errors for common Elasticsearch exceptions. These are
|
87
|
+
# all subclasses of the more general RequestError
|
88
|
+
IndexNotFoundError = Class.new RequestError
|
89
|
+
QueryParsingError = Class.new RequestError
|
84
90
|
|
85
91
|
ServerError.fatal = false
|
86
92
|
TimeoutError.fatal = false
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Elastomer
|
2
|
+
module Middleware
|
3
|
+
|
4
|
+
# Request middleware that raises an exception if the request body exceeds a
|
5
|
+
# `max_request_size`.
|
6
|
+
class LimitSize < Faraday::Middleware
|
7
|
+
|
8
|
+
def initialize(app = nil, options = {})
|
9
|
+
super(app)
|
10
|
+
@max_request_size = options.fetch(:max_request_size)
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :max_request_size
|
14
|
+
|
15
|
+
def call(env)
|
16
|
+
if body = env[:body]
|
17
|
+
if body.is_a?(String) && body.bytesize > max_request_size
|
18
|
+
raise ::Elastomer::Client::RequestSizeError,
|
19
|
+
"Request of size `#{body.bytesize}` exceeds the maximum requst size: #{max_request_size}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
@app.call(env)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
Faraday::Request.register_middleware \
|
30
|
+
:limit_size => ::Elastomer::Middleware::LimitSize
|
data/lib/elastomer/version.rb
CHANGED
data/test/client/bulk_test.rb
CHANGED
@@ -177,14 +177,14 @@ describe Elastomer::Client::Bulk do
|
|
177
177
|
ary << b.index(document)
|
178
178
|
}
|
179
179
|
ary.compact!
|
180
|
-
assert_equal
|
180
|
+
assert_equal 4, ary.length
|
181
181
|
|
182
182
|
document = {:_id => 10, :_type => "tweet", :author => "pea53", :message => "tweet 10 is a 102 character request"}
|
183
183
|
ary << b.index(document)
|
184
184
|
end
|
185
185
|
ary.compact!
|
186
186
|
|
187
|
-
assert_equal
|
187
|
+
assert_equal 5, ary.length
|
188
188
|
ary.each { |a| a["items"].each { |b| assert_bulk_index(b) } }
|
189
189
|
|
190
190
|
@index.refresh
|
@@ -208,7 +208,7 @@ describe Elastomer::Client::Bulk do
|
|
208
208
|
ary << b.index(document)
|
209
209
|
}
|
210
210
|
ary.compact!
|
211
|
-
assert_equal
|
211
|
+
assert_equal 2, ary.length
|
212
212
|
|
213
213
|
document = {:_id => 10, :_type => "tweet", :author => "pea53", :message => "this is tweet number 10"}
|
214
214
|
ary << b.index(document)
|
@@ -224,6 +224,30 @@ describe Elastomer::Client::Bulk do
|
|
224
224
|
assert_equal 10, h["hits"]["total"]
|
225
225
|
end
|
226
226
|
|
227
|
+
it "rejects documents that excceed the allowed bulk request size" do
|
228
|
+
ary = []
|
229
|
+
ary << @index.bulk(:request_size => 300) do |b|
|
230
|
+
2.times { |num|
|
231
|
+
document = {:_id => num, :_type => "tweet", :author => "pea53", :message => "tweet #{num} is a 100 character request"}
|
232
|
+
ary << b.index(document)
|
233
|
+
}
|
234
|
+
ary.compact!
|
235
|
+
assert_equal 0, ary.length
|
236
|
+
|
237
|
+
document = {:_id => 342, :_type => "tweet", :author => "pea53", :message => "a"*290}
|
238
|
+
assert_raises(Elastomer::Client::RequestSizeError) { b.index(document) }
|
239
|
+
end
|
240
|
+
ary.compact!
|
241
|
+
|
242
|
+
assert_equal 1, ary.length
|
243
|
+
ary.each { |a| a["items"].each { |b| assert_bulk_index(b) } }
|
244
|
+
|
245
|
+
@index.refresh
|
246
|
+
h = @index.docs.search :q => "*:*", :size => 0
|
247
|
+
|
248
|
+
assert_equal 2, h["hits"]["total"]
|
249
|
+
end
|
250
|
+
|
227
251
|
it "uses :id from parameters" do
|
228
252
|
response = @index.bulk do |b|
|
229
253
|
document = { :_type => "tweet", :author => "pea53", :message => "just a test tweet" }
|
data/test/client/docs_test.rb
CHANGED
@@ -279,6 +279,14 @@ describe Elastomer::Client::Docs do
|
|
279
279
|
end
|
280
280
|
end
|
281
281
|
|
282
|
+
it "generates QueryParsingError exceptions on bad input when searching" do
|
283
|
+
query = {:query => {:query_string => {:query => "OR should fail"}}}
|
284
|
+
assert_raises(Elastomer::Client::QueryParsingError) { @docs.search(query, :type => %w[doc1 doc2]) }
|
285
|
+
|
286
|
+
query = {:query => {:foo_is_not_valid => {}}}
|
287
|
+
assert_raises(Elastomer::Client::QueryParsingError) { @docs.search(query, :type => %w[doc1 doc2]) }
|
288
|
+
end
|
289
|
+
|
282
290
|
it "counts documents" do
|
283
291
|
h = @docs.count :q => "*:*"
|
284
292
|
assert_equal 0, h["count"]
|
data/test/client/index_test.rb
CHANGED
@@ -259,6 +259,13 @@ describe Elastomer::Client::Index do
|
|
259
259
|
assert_equal %w[just few words analyze], tokens
|
260
260
|
end
|
261
261
|
|
262
|
+
describe "when an index does not exist" do
|
263
|
+
it "raises an IndexNotFoundError on delete" do
|
264
|
+
index = $client.index("index-that-does-not-exist")
|
265
|
+
assert_raises(Elastomer::Client::IndexNotFoundError) { index.delete }
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
262
269
|
describe "when an index exists" do
|
263
270
|
before do
|
264
271
|
suggest = {
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: elastomer-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tim Pease
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-09-
|
12
|
+
date: 2016-09-02 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: addressable
|
@@ -215,6 +215,7 @@ files:
|
|
215
215
|
- lib/elastomer/client/warmer.rb
|
216
216
|
- lib/elastomer/core_ext/time.rb
|
217
217
|
- lib/elastomer/middleware/encode_json.rb
|
218
|
+
- lib/elastomer/middleware/limit_size.rb
|
218
219
|
- lib/elastomer/middleware/opaque_id.rb
|
219
220
|
- lib/elastomer/middleware/parse_json.rb
|
220
221
|
- lib/elastomer/notifications.rb
|
@@ -267,7 +268,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
267
268
|
version: '0'
|
268
269
|
requirements: []
|
269
270
|
rubyforge_project:
|
270
|
-
rubygems_version: 2.2.
|
271
|
+
rubygems_version: 2.2.3
|
271
272
|
signing_key:
|
272
273
|
specification_version: 4
|
273
274
|
summary: A library for interacting with Elasticsearch
|