elastomer-client 2.0.1 → 2.1.0
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.
- 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
|