elasticsearch-transport 7.1.0 → 7.8.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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +12 -8
  3. data/{LICENSE.txt → LICENSE} +0 -0
  4. data/README.md +160 -72
  5. data/Rakefile +1 -1
  6. data/elasticsearch-transport.gemspec +42 -60
  7. data/lib/elasticsearch/transport/client.rb +70 -19
  8. data/lib/elasticsearch/transport/redacted.rb +1 -1
  9. data/lib/elasticsearch/transport/transport/base.rb +86 -12
  10. data/lib/elasticsearch/transport/transport/connections/collection.rb +1 -1
  11. data/lib/elasticsearch/transport/transport/connections/connection.rb +1 -1
  12. data/lib/elasticsearch/transport/transport/connections/selector.rb +18 -6
  13. data/lib/elasticsearch/transport/transport/errors.rb +1 -1
  14. data/lib/elasticsearch/transport/transport/http/curb.rb +26 -9
  15. data/lib/elasticsearch/transport/transport/http/faraday.rb +18 -4
  16. data/lib/elasticsearch/transport/transport/http/manticore.rb +25 -10
  17. data/lib/elasticsearch/transport/transport/loggable.rb +1 -1
  18. data/lib/elasticsearch/transport/transport/response.rb +1 -2
  19. data/lib/elasticsearch/transport/transport/serializer/multi_json.rb +1 -1
  20. data/lib/elasticsearch/transport/transport/sniffer.rb +3 -2
  21. data/lib/elasticsearch/transport/version.rb +2 -2
  22. data/lib/elasticsearch/transport.rb +1 -1
  23. data/lib/elasticsearch-transport.rb +1 -1
  24. data/spec/elasticsearch/connections/collection_spec.rb +254 -0
  25. data/spec/elasticsearch/connections/selector_spec.rb +174 -0
  26. data/spec/elasticsearch/transport/base_spec.rb +177 -9
  27. data/spec/elasticsearch/transport/client_spec.rb +525 -29
  28. data/spec/elasticsearch/transport/sniffer_spec.rb +1 -1
  29. data/spec/spec_helper.rb +25 -1
  30. data/test/integration/transport_test.rb +1 -1
  31. data/test/profile/client_benchmark_test.rb +1 -1
  32. data/test/test_helper.rb +1 -1
  33. data/test/unit/connection_test.rb +1 -1
  34. data/test/unit/response_test.rb +2 -2
  35. data/test/unit/serializer_test.rb +1 -1
  36. data/test/unit/transport_base_test.rb +1 -1
  37. data/test/unit/transport_curb_test.rb +2 -2
  38. data/test/unit/transport_faraday_test.rb +1 -1
  39. data/test/unit/transport_manticore_test.rb +28 -12
  40. metadata +85 -61
  41. data/test/unit/connection_collection_test.rb +0 -147
  42. data/test/unit/connection_selector_test.rb +0 -81
@@ -0,0 +1,254 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ require 'spec_helper'
19
+
20
+ describe Elasticsearch::Transport::Transport::Connections::Collection do
21
+
22
+ describe '#initialize' do
23
+
24
+ let(:collection) do
25
+ described_class.new
26
+ end
27
+
28
+ it 'has an empty list of connections as a default' do
29
+ expect(collection.connections).to be_empty
30
+ end
31
+
32
+ it 'has a default selector class' do
33
+ expect(collection.selector).not_to be_nil
34
+ end
35
+
36
+ context 'when a selector class is specified' do
37
+
38
+ let(:collection) do
39
+ described_class.new(selector_class: Elasticsearch::Transport::Transport::Connections::Selector::Random)
40
+ end
41
+
42
+ it 'sets the selector' do
43
+ expect(collection.selector).to be_a(Elasticsearch::Transport::Transport::Connections::Selector::Random)
44
+ end
45
+ end
46
+ end
47
+
48
+ describe '#get_connection' do
49
+
50
+ let(:collection) do
51
+ described_class.new(selector_class: Elasticsearch::Transport::Transport::Connections::Selector::Random)
52
+ end
53
+
54
+ before do
55
+ expect(collection.selector).to receive(:select).and_return('OK')
56
+ end
57
+
58
+ it 'uses the selector to select a connection' do
59
+ expect(collection.get_connection).to eq('OK')
60
+ end
61
+ end
62
+
63
+ describe '#hosts' do
64
+
65
+ let(:collection) do
66
+ described_class.new(connections: [ double('connection', host: 'A'),
67
+ double('connection', host: 'B') ])
68
+ end
69
+
70
+ it 'returns a list of hosts' do
71
+ expect(collection.hosts).to eq([ 'A', 'B'])
72
+ end
73
+ end
74
+
75
+ describe 'enumerable' do
76
+
77
+ let(:collection) do
78
+ described_class.new(connections: [ double('connection', host: 'A', dead?: false),
79
+ double('connection', host: 'B', dead?: false) ])
80
+ end
81
+
82
+ describe '#map' do
83
+
84
+ it 'responds to the method' do
85
+ expect(collection.map { |c| c.host.downcase }).to eq(['a', 'b'])
86
+ end
87
+ end
88
+
89
+ describe '#[]' do
90
+
91
+ it 'responds to the method' do
92
+ expect(collection[0].host).to eq('A')
93
+ expect(collection[1].host).to eq('B')
94
+ end
95
+ end
96
+
97
+ describe '#size' do
98
+
99
+ it 'responds to the method' do
100
+ expect(collection.size).to eq(2)
101
+ end
102
+ end
103
+
104
+ context 'when a connection is marked as dead' do
105
+
106
+ let(:collection) do
107
+ described_class.new(connections: [ double('connection', host: 'A', dead?: true),
108
+ double('connection', host: 'B', dead?: false) ])
109
+ end
110
+
111
+ it 'does not enumerate the dead connections' do
112
+ expect(collection.size).to eq(1)
113
+ expect(collection.collect { |c| c.host }).to eq(['B'])
114
+ end
115
+
116
+ context '#alive' do
117
+
118
+ it 'enumerates the alive connections' do
119
+ expect(collection.alive.collect { |c| c.host }).to eq(['B'])
120
+ end
121
+ end
122
+
123
+ context '#dead' do
124
+
125
+ it 'enumerates the alive connections' do
126
+ expect(collection.dead.collect { |c| c.host }).to eq(['A'])
127
+ end
128
+ end
129
+ end
130
+ end
131
+
132
+ describe '#add' do
133
+
134
+ let(:collection) do
135
+ described_class.new(connections: [ double('connection', host: 'A', dead?: false),
136
+ double('connection', host: 'B', dead?: false) ])
137
+ end
138
+
139
+ context 'when an array is provided' do
140
+
141
+ before do
142
+ collection.add([double('connection', host: 'C', dead?: false),
143
+ double('connection', host: 'D', dead?: false)])
144
+ end
145
+
146
+ it 'adds the connections' do
147
+ expect(collection.size).to eq(4)
148
+ end
149
+ end
150
+
151
+ context 'when an element is provided' do
152
+
153
+ before do
154
+ collection.add(double('connection', host: 'C', dead?: false))
155
+ end
156
+
157
+ it 'adds the connection' do
158
+ expect(collection.size).to eq(3)
159
+ end
160
+ end
161
+ end
162
+
163
+ describe '#remove' do
164
+
165
+ let(:connections) do
166
+ [ double('connection', host: 'A', dead?: false),
167
+ double('connection', host: 'B', dead?: false) ]
168
+ end
169
+
170
+ let(:collection) do
171
+ described_class.new(connections: connections)
172
+ end
173
+
174
+ context 'when an array is provided' do
175
+
176
+ before do
177
+ collection.remove(connections)
178
+ end
179
+
180
+ it 'removes the connections' do
181
+ expect(collection.size).to eq(0)
182
+ end
183
+ end
184
+
185
+ context 'when an element is provided' do
186
+
187
+ let(:connections) do
188
+ [ double('connection', host: 'A', dead?: false),
189
+ double('connection', host: 'B', dead?: false) ]
190
+ end
191
+
192
+ before do
193
+ collection.remove(connections.first)
194
+ end
195
+
196
+ it 'removes the connection' do
197
+ expect(collection.size).to eq(1)
198
+ end
199
+ end
200
+ end
201
+
202
+ describe '#get_connection' do
203
+
204
+ context 'when all connections are dead' do
205
+
206
+ let(:connection_a) do
207
+ Elasticsearch::Transport::Transport::Connections::Connection.new(host: { host: 'A' })
208
+ end
209
+
210
+ let(:connection_b) do
211
+ Elasticsearch::Transport::Transport::Connections::Connection.new(host: { host: 'B' })
212
+ end
213
+
214
+ let(:collection) do
215
+ described_class.new(connections: [connection_a, connection_b])
216
+ end
217
+
218
+ before do
219
+ connection_a.dead!.dead!
220
+ connection_b.dead!
221
+ end
222
+
223
+ it 'returns the connection with the least failures' do
224
+ expect(collection.get_connection.host[:host]).to eq('B')
225
+ end
226
+ end
227
+
228
+ context 'when multiple threads are used' do
229
+
230
+ let(:connections) do
231
+ 20.times.collect do |i|
232
+ Elasticsearch::Transport::Transport::Connections::Connection.new(host: { host: i })
233
+ end
234
+ end
235
+
236
+ let(:collection) do
237
+ described_class.new(connections: connections)
238
+ end
239
+
240
+ it 'allows threads to select connections in parallel' do
241
+ expect(10.times.collect do
242
+ threads = []
243
+ 20.times do
244
+ threads << Thread.new do
245
+ collection.get_connection
246
+ end
247
+ end
248
+ threads.map { |t| t.join }
249
+ collection.get_connection.host[:host]
250
+ end).to eq((0..9).to_a)
251
+ end
252
+ end
253
+ end
254
+ end
@@ -0,0 +1,174 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ require 'spec_helper'
19
+
20
+ describe Elasticsearch::Transport::Transport::Connections::Selector do
21
+
22
+ before do
23
+ class BackupStrategySelector
24
+ include Elasticsearch::Transport::Transport::Connections::Selector::Base
25
+
26
+ def select(options={})
27
+ connections.reject do |c|
28
+ c.host[:attributes] && c.host[:attributes][:backup]
29
+ end.sample
30
+ end
31
+ end
32
+ end
33
+
34
+ after do
35
+ Object.send(:remove_const, :BackupStrategySelector)
36
+ end
37
+
38
+ let(:backup_strategy_selector) do
39
+ BackupStrategySelector.new
40
+ end
41
+
42
+ describe 'the Random selector' do
43
+
44
+ let(:connections) do
45
+ [1, 2]
46
+ end
47
+
48
+ let(:selector) do
49
+ described_class::Random.new(connections: connections)
50
+ end
51
+
52
+ it 'is initialized with connections' do
53
+ expect(selector.connections).to eq(connections)
54
+ end
55
+
56
+ describe '#select' do
57
+
58
+ let(:connections) do
59
+ (0..19).to_a
60
+ end
61
+
62
+ it 'returns a connection' do
63
+ expect(selector.select).to be_a(Integer)
64
+ end
65
+
66
+ context 'when multiple threads are used' do
67
+
68
+ it 'allows threads to select connections in parallel' do
69
+ expect(10.times.collect do
70
+ threads = []
71
+ 20.times do
72
+ threads << Thread.new do
73
+ selector.select
74
+ end
75
+ end
76
+ threads.map { |t| t.join }
77
+ selector.select
78
+ end).to all(be_a(Integer))
79
+ end
80
+ end
81
+ end
82
+ end
83
+
84
+ describe 'the RoundRobin selector' do
85
+
86
+ let(:connections) do
87
+ ['A', 'B', 'C']
88
+ end
89
+
90
+ let(:selector) do
91
+ described_class::RoundRobin.new(connections: connections)
92
+ end
93
+
94
+ it 'is initialized with connections' do
95
+ expect(selector.connections).to eq(connections)
96
+ end
97
+
98
+ describe '#select' do
99
+
100
+ it 'rotates over the connections' do
101
+ expect(selector.select).to eq('A')
102
+ expect(selector.select).to eq('B')
103
+ expect(selector.select).to eq('C')
104
+ expect(selector.select).to eq('A')
105
+ end
106
+
107
+ context 'when multiple threads are used' do
108
+
109
+ let(:connections) do
110
+ (0..19).to_a
111
+ end
112
+
113
+ it 'returns a connection' do
114
+ expect(selector.select).to be_a(Integer)
115
+ end
116
+
117
+ it 'allows threads to select connections in parallel' do
118
+ expect(10.times.collect do
119
+ threads = []
120
+ 20.times do
121
+ threads << Thread.new do
122
+ selector.select
123
+ end
124
+ end
125
+ threads.map { |t| t.join }
126
+ selector.select
127
+ end).to eq((0..9).to_a)
128
+ end
129
+ end
130
+ end
131
+ end
132
+
133
+ describe 'a custom selector' do
134
+
135
+ let(:connections) do
136
+ [ double(host: { hostname: 'host1' }),
137
+ double(host: { hostname: 'host2', attributes: { backup: true } }) ]
138
+ end
139
+
140
+ let(:selector) do
141
+ BackupStrategySelector.new(connections: connections)
142
+ end
143
+
144
+ it 'is initialized with connections' do
145
+ expect(selector.connections).to eq(connections)
146
+ end
147
+
148
+ describe '#select' do
149
+
150
+ it 'applies the custom strategy' do
151
+ 10.times { expect(selector.select.host[:hostname]).to eq('host1') }
152
+ end
153
+ end
154
+ end
155
+
156
+ context 'when the Base module is included in a class' do
157
+
158
+ before do
159
+ class ExampleSelector
160
+ include Elasticsearch::Transport::Transport::Connections::Selector::Base
161
+ end
162
+ end
163
+
164
+ after do
165
+ Object.send(:remove_const, :ExampleSelector)
166
+ end
167
+
168
+ it 'requires the #select method to be redefined' do
169
+ expect {
170
+ ExampleSelector.new.select
171
+ }.to raise_exception(NoMethodError)
172
+ end
173
+ end
174
+ end
@@ -6,7 +6,7 @@
6
6
  # not use this file except in compliance with the License.
7
7
  # You may obtain a copy of the License at
8
8
  #
9
- # http://www.apache.org/licenses/LICENSE-2.0
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
10
  #
11
11
  # Unless required by applicable law or agreed to in writing,
12
12
  # software distributed under the License is distributed on an
@@ -18,11 +18,8 @@
18
18
  require 'spec_helper'
19
19
 
20
20
  describe Elasticsearch::Transport::Transport::Base do
21
-
22
21
  context 'when a host is printed in a logged message' do
23
-
24
22
  shared_examples_for 'a redacted string' do
25
-
26
23
  let(:client) do
27
24
  Elasticsearch::Transport::Client.new(arguments)
28
25
  end
@@ -33,6 +30,7 @@ describe Elasticsearch::Transport::Transport::Base do
33
30
 
34
31
  it 'does not include the password in the logged string' do
35
32
  expect(logger).not_to receive(:error).with(/secret_password/)
33
+
36
34
  expect {
37
35
  client.cluster.stats
38
36
  }.to raise_exception(Faraday::ConnectionFailed)
@@ -47,7 +45,6 @@ describe Elasticsearch::Transport::Transport::Base do
47
45
  end
48
46
 
49
47
  context 'when the user and password are provided as separate arguments' do
50
-
51
48
  let(:arguments) do
52
49
  { hosts: 'fake',
53
50
  logger: logger,
@@ -59,9 +56,8 @@ describe Elasticsearch::Transport::Transport::Base do
59
56
  end
60
57
 
61
58
  context 'when the user and password are provided in the string URI' do
62
-
63
59
  let(:arguments) do
64
- { hosts: 'http://test:secret_password@fake.com',
60
+ { hosts: 'https://test:secret_password@fake_local_elasticsearch',
65
61
  logger: logger }
66
62
  end
67
63
 
@@ -69,13 +65,185 @@ describe Elasticsearch::Transport::Transport::Base do
69
65
  end
70
66
 
71
67
  context 'when the user and password are provided in the URI object' do
72
-
73
68
  let(:arguments) do
74
- { hosts: URI.parse('http://test:secret_password@fake.com'),
69
+ { hosts: URI.parse('https://test:secret_password@fake_local_elasticsearch'),
75
70
  logger: logger }
76
71
  end
77
72
 
78
73
  it_behaves_like 'a redacted string'
79
74
  end
80
75
  end
76
+
77
+ context 'when reload_on_failure is true and and hosts are unreachable' do
78
+
79
+ let(:client) do
80
+ Elasticsearch::Transport::Client.new(arguments)
81
+ end
82
+
83
+ let(:arguments) do
84
+ {
85
+ hosts: ['http://unavailable:9200', 'http://unavailable:9201'],
86
+ reload_on_failure: true,
87
+ sniffer_timeout: 5
88
+ }
89
+ end
90
+
91
+ it 'raises an exception' do
92
+ expect {
93
+ client.info
94
+ }.to raise_exception(Faraday::ConnectionFailed)
95
+ end
96
+ end
97
+
98
+ context 'when the client has `retry_on_failure` set to an integer' do
99
+
100
+ let(:client) do
101
+ Elasticsearch::Transport::Client.new(arguments)
102
+ end
103
+
104
+ let(:arguments) do
105
+ {
106
+ hosts: ['http://unavailable:9200', 'http://unavailable:9201'],
107
+ retry_on_failure: 2
108
+ }
109
+ end
110
+
111
+ context 'when `perform_request` is called without a `retry_on_failure` option value' do
112
+ before do
113
+ expect(client.transport).to receive(:get_connection).exactly(3).times.and_call_original
114
+ end
115
+
116
+ it 'uses the client `retry_on_failure` value' do
117
+ expect {
118
+ client.transport.perform_request('GET', '/info')
119
+ }.to raise_exception(Faraday::ConnectionFailed)
120
+ end
121
+ end
122
+
123
+ context 'when `perform_request` is called with a `retry_on_failure` option value' do
124
+ before do
125
+ expect(client.transport).to receive(:get_connection).exactly(6).times.and_call_original
126
+ end
127
+
128
+ it 'uses the option `retry_on_failure` value' do
129
+ expect {
130
+ client.transport.perform_request('GET', '/info', {}, nil, nil, retry_on_failure: 5)
131
+ }.to raise_exception(Faraday::ConnectionFailed)
132
+ end
133
+ end
134
+ end
135
+
136
+ context 'when the client has `retry_on_failure` set to true' do
137
+ let(:client) do
138
+ Elasticsearch::Transport::Client.new(arguments)
139
+ end
140
+
141
+ let(:arguments) do
142
+ {
143
+ hosts: ['http://unavailable:9200', 'http://unavailable:9201'],
144
+ retry_on_failure: true
145
+ }
146
+ end
147
+
148
+ context 'when `perform_request` is called without a `retry_on_failure` option value' do
149
+ before do
150
+ expect(client.transport).to receive(:get_connection).exactly(4).times.and_call_original
151
+ end
152
+
153
+ it 'uses the default `MAX_RETRIES` value' do
154
+ expect {
155
+ client.transport.perform_request('GET', '/info')
156
+ }.to raise_exception(Faraday::ConnectionFailed)
157
+ end
158
+ end
159
+
160
+ context 'when `perform_request` is called with a `retry_on_failure` option value' do
161
+ before do
162
+ expect(client.transport).to receive(:get_connection).exactly(6).times.and_call_original
163
+ end
164
+
165
+ it 'uses the option `retry_on_failure` value' do
166
+ expect {
167
+ client.transport.perform_request('GET', '/info', {}, nil, nil, retry_on_failure: 5)
168
+ }.to raise_exception(Faraday::ConnectionFailed)
169
+ end
170
+ end
171
+ end
172
+
173
+ context 'when the client has `retry_on_failure` set to false' do
174
+ let(:client) do
175
+ Elasticsearch::Transport::Client.new(arguments)
176
+ end
177
+
178
+ let(:arguments) do
179
+ {
180
+ hosts: ['http://unavailable:9200', 'http://unavailable:9201'],
181
+ retry_on_failure: false
182
+ }
183
+ end
184
+
185
+ context 'when `perform_request` is called without a `retry_on_failure` option value' do
186
+ before do
187
+ expect(client.transport).to receive(:get_connection).once.and_call_original
188
+ end
189
+
190
+ it 'does not retry' do
191
+ expect {
192
+ client.transport.perform_request('GET', '/info')
193
+ }.to raise_exception(Faraday::ConnectionFailed)
194
+ end
195
+ end
196
+
197
+ context 'when `perform_request` is called with a `retry_on_failure` option value' do
198
+
199
+ before do
200
+ expect(client.transport).to receive(:get_connection).exactly(6).times.and_call_original
201
+ end
202
+
203
+ it 'uses the option `retry_on_failure` value' do
204
+ expect {
205
+ client.transport.perform_request('GET', '/info', {}, nil, nil, retry_on_failure: 5)
206
+ }.to raise_exception(Faraday::ConnectionFailed)
207
+ end
208
+ end
209
+ end
210
+
211
+ context 'when the client has no `retry_on_failure` set' do
212
+
213
+ let(:client) do
214
+ Elasticsearch::Transport::Client.new(arguments)
215
+ end
216
+
217
+ let(:arguments) do
218
+ {
219
+ hosts: ['http://unavailable:9200', 'http://unavailable:9201'],
220
+ }
221
+ end
222
+
223
+ context 'when `perform_request` is called without a `retry_on_failure` option value' do
224
+
225
+ before do
226
+ expect(client.transport).to receive(:get_connection).exactly(1).times.and_call_original
227
+ end
228
+
229
+ it 'does not retry' do
230
+ expect {
231
+ client.transport.perform_request('GET', '/info')
232
+ }.to raise_exception(Faraday::ConnectionFailed)
233
+ end
234
+ end
235
+
236
+ context 'when `perform_request` is called with a `retry_on_failure` option value' do
237
+
238
+ before do
239
+ expect(client.transport).to receive(:get_connection).exactly(6).times.and_call_original
240
+ end
241
+
242
+ it 'uses the option `retry_on_failure` value' do
243
+ expect {
244
+ client.transport.perform_request('GET', '/info', {}, nil, nil, retry_on_failure: 5)
245
+ }.to raise_exception(Faraday::ConnectionFailed)
246
+ end
247
+ end
248
+ end
81
249
  end