elasticsearch-transport 7.1.0 → 7.4.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 +3 -16
  3. data/{LICENSE.txt → LICENSE} +0 -0
  4. data/README.md +18 -19
  5. data/Rakefile +3 -16
  6. data/elasticsearch-transport.gemspec +5 -17
  7. data/lib/elasticsearch/transport/client.rb +26 -23
  8. data/lib/elasticsearch/transport/redacted.rb +3 -16
  9. data/lib/elasticsearch/transport/transport/base.rb +81 -26
  10. data/lib/elasticsearch/transport/transport/connections/collection.rb +3 -16
  11. data/lib/elasticsearch/transport/transport/connections/connection.rb +3 -16
  12. data/lib/elasticsearch/transport/transport/connections/selector.rb +20 -21
  13. data/lib/elasticsearch/transport/transport/errors.rb +3 -16
  14. data/lib/elasticsearch/transport/transport/http/curb.rb +28 -24
  15. data/lib/elasticsearch/transport/transport/http/faraday.rb +19 -18
  16. data/lib/elasticsearch/transport/transport/http/manticore.rb +27 -25
  17. data/lib/elasticsearch/transport/transport/loggable.rb +3 -16
  18. data/lib/elasticsearch/transport/transport/response.rb +3 -16
  19. data/lib/elasticsearch/transport/transport/serializer/multi_json.rb +3 -16
  20. data/lib/elasticsearch/transport/transport/sniffer.rb +5 -17
  21. data/lib/elasticsearch/transport/version.rb +4 -17
  22. data/lib/elasticsearch/transport.rb +3 -16
  23. data/lib/elasticsearch-transport.rb +3 -16
  24. data/spec/elasticsearch/connections/collection_spec.rb +241 -0
  25. data/spec/elasticsearch/connections/selector_spec.rb +161 -0
  26. data/spec/elasticsearch/transport/base_spec.rb +183 -16
  27. data/spec/elasticsearch/transport/client_spec.rb +350 -19
  28. data/spec/elasticsearch/transport/sniffer_spec.rb +3 -16
  29. data/spec/spec_helper.rb +6 -0
  30. data/test/integration/transport_test.rb +3 -16
  31. data/test/profile/client_benchmark_test.rb +3 -16
  32. data/test/test_helper.rb +3 -16
  33. data/test/unit/connection_test.rb +3 -16
  34. data/test/unit/response_test.rb +3 -16
  35. data/test/unit/serializer_test.rb +3 -16
  36. data/test/unit/transport_base_test.rb +3 -16
  37. data/test/unit/transport_curb_test.rb +4 -17
  38. data/test/unit/transport_faraday_test.rb +3 -16
  39. data/test/unit/transport_manticore_test.rb +30 -27
  40. metadata +23 -9
  41. data/test/unit/connection_collection_test.rb +0 -147
  42. data/test/unit/connection_selector_test.rb +0 -81
@@ -1,19 +1,6 @@
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.
1
+ # Licensed to Elasticsearch B.V under one or more agreements.
2
+ # Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3
+ # See the LICENSE file in the project root for more information
17
4
 
18
5
  module Elasticsearch
19
6
  module Transport
@@ -32,7 +19,7 @@ module Elasticsearch
32
19
  # @return [Response]
33
20
  # @see Transport::Base#perform_request
34
21
  #
35
- def perform_request(method, path, params={}, body=nil, headers=nil)
22
+ def perform_request(method, path, params={}, body=nil, headers=nil, opts={})
36
23
  super do |connection, url|
37
24
  connection.connection.url = connection.full_url(path, params)
38
25
 
@@ -43,7 +30,15 @@ module Elasticsearch
43
30
  connection.connection.set :nobody, false
44
31
 
45
32
  connection.connection.put_data = __convert_to_json(body) if body
46
- connection.connection.headers = headers if headers
33
+
34
+ if headers
35
+ if connection.connection.headers
36
+ connection.connection.headers.merge!(headers)
37
+ else
38
+ connection.connection.headers = headers
39
+ end
40
+ end
41
+
47
42
  else raise ArgumentError, "Unsupported HTTP method: #{method}"
48
43
  end
49
44
 
@@ -53,7 +48,7 @@ module Elasticsearch
53
48
  response_headers['content-type'] = 'application/json' if connection.connection.header_str =~ /\/json/
54
49
 
55
50
  Response.new connection.connection.response_code,
56
- connection.connection.body_str,
51
+ decompress_response(connection.connection.body_str),
57
52
  response_headers
58
53
  end
59
54
  end
@@ -65,10 +60,7 @@ module Elasticsearch
65
60
  def __build_connection(host, options={}, block=nil)
66
61
  client = ::Curl::Easy.new
67
62
 
68
- headers = options[:headers] || {}
69
- headers.update('User-Agent' => "Curb #{Curl::CURB_VERSION}")
70
-
71
- client.headers = headers
63
+ apply_headers(client, options)
72
64
  client.url = __full_url(host)
73
65
 
74
66
  if host[:user]
@@ -96,8 +88,20 @@ module Elasticsearch
96
88
  ::Curl::Err::TimeoutError
97
89
  ]
98
90
  end
99
- end
100
91
 
92
+ private
93
+
94
+ def user_agent_header(client)
95
+ @user_agent ||= begin
96
+ meta = ["RUBY_VERSION: #{RUBY_VERSION}"]
97
+ if RbConfig::CONFIG && RbConfig::CONFIG['host_os']
98
+ meta << "#{RbConfig::CONFIG['host_os'].split('_').first[/[a-z]+/i].downcase} #{RbConfig::CONFIG['target_cpu']}"
99
+ end
100
+ meta << "Curb #{Curl::CURB_VERSION}"
101
+ "elasticsearch-ruby/#{VERSION} (#{meta.join('; ')})"
102
+ end
103
+ end
104
+ end
101
105
  end
102
106
  end
103
107
  end
@@ -1,19 +1,6 @@
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.
1
+ # Licensed to Elasticsearch B.V under one or more agreements.
2
+ # Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3
+ # See the LICENSE file in the project root for more information
17
4
 
18
5
  module Elasticsearch
19
6
  module Transport
@@ -33,7 +20,7 @@ module Elasticsearch
33
20
  # @return [Response]
34
21
  # @see Transport::Base#perform_request
35
22
  #
36
- def perform_request(method, path, params={}, body=nil, headers=nil)
23
+ def perform_request(method, path, params={}, body=nil, headers=nil, opts={})
37
24
  super do |connection, url|
38
25
  headers = headers || connection.connection.headers
39
26
 
@@ -42,7 +29,7 @@ module Elasticsearch
42
29
  ( body ? __convert_to_json(body) : nil ),
43
30
  headers)
44
31
 
45
- Response.new response.status, response.body, response.headers
32
+ Response.new response.status, decompress_response(response.body), response.headers
46
33
  end
47
34
  end
48
35
 
@@ -52,6 +39,7 @@ module Elasticsearch
52
39
  #
53
40
  def __build_connection(host, options={}, block=nil)
54
41
  client = ::Faraday.new(__full_url(host), options, &block)
42
+ apply_headers(client, options)
55
43
  Connections::Connection.new :host => host, :connection => client
56
44
  end
57
45
 
@@ -62,6 +50,19 @@ module Elasticsearch
62
50
  def host_unreachable_exceptions
63
51
  [::Faraday::Error::ConnectionFailed, ::Faraday::Error::TimeoutError]
64
52
  end
53
+
54
+ private
55
+
56
+ def user_agent_header(client)
57
+ @user_agent ||= begin
58
+ meta = ["RUBY_VERSION: #{RUBY_VERSION}"]
59
+ if RbConfig::CONFIG && RbConfig::CONFIG['host_os']
60
+ meta << "#{RbConfig::CONFIG['host_os'].split('_').first[/[a-z]+/i].downcase} #{RbConfig::CONFIG['target_cpu']}"
61
+ end
62
+ meta << "#{client.headers[USER_AGENT_STR]}"
63
+ "elasticsearch-ruby/#{VERSION} (#{meta.join('; ')})"
64
+ end
65
+ end
65
66
  end
66
67
  end
67
68
  end
@@ -1,19 +1,6 @@
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.
1
+ # Licensed to Elasticsearch B.V under one or more agreements.
2
+ # Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3
+ # See the LICENSE file in the project root for more information
17
4
 
18
5
  require 'manticore'
19
6
 
@@ -80,7 +67,7 @@ module Elasticsearch
80
67
  # @return [Response]
81
68
  # @see Transport::Base#perform_request
82
69
  #
83
- def perform_request(method, path, params={}, body=nil, headers=nil)
70
+ def perform_request(method, path, params={}, body=nil, headers=nil, opts={})
84
71
  super do |connection, url|
85
72
  params[:body] = __convert_to_json(body) if body
86
73
  params[:headers] = headers if headers
@@ -110,14 +97,8 @@ module Elasticsearch
110
97
  #
111
98
  def __build_connections
112
99
  @request_options = {}
113
-
114
- if options[:transport_options] && options[:transport_options][:headers]
115
- @request_options[:headers] = options[:transport_options][:headers]
116
- end
117
-
118
- if options.key?(:headers)
119
- @request_options[:headers] = options[:headers]
120
- end
100
+ apply_headers(@request_options, options[:transport_options])
101
+ apply_headers(@request_options, options)
121
102
 
122
103
  Connections::Collection.new \
123
104
  :connections => hosts.map { |host|
@@ -157,6 +138,27 @@ module Elasticsearch
157
138
  ::Manticore::ResolutionFailure
158
139
  ]
159
140
  end
141
+
142
+ private
143
+
144
+ def apply_headers(request_options, options)
145
+ headers = (options && options[:headers]) || {}
146
+ headers[CONTENT_TYPE_STR] = find_value(headers, CONTENT_TYPE_REGEX) || DEFAULT_CONTENT_TYPE
147
+ headers[USER_AGENT_STR] = find_value(headers, USER_AGENT_REGEX) || user_agent_header
148
+ headers[ACCEPT_ENCODING] = GZIP if use_compression?
149
+ request_options.merge!(headers: headers)
150
+ end
151
+
152
+ def user_agent_header
153
+ @user_agent ||= begin
154
+ meta = ["RUBY_VERSION: #{JRUBY_VERSION}"]
155
+ if RbConfig::CONFIG && RbConfig::CONFIG['host_os']
156
+ meta << "#{RbConfig::CONFIG['host_os'].split('_').first[/[a-z]+/i].downcase} #{RbConfig::CONFIG['target_cpu']}"
157
+ end
158
+ meta << "Manticore #{::Manticore::VERSION}"
159
+ "elasticsearch-ruby/#{VERSION} (#{meta.join('; ')})"
160
+ end
161
+ end
160
162
  end
161
163
  end
162
164
  end
@@ -1,19 +1,6 @@
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.
1
+ # Licensed to Elasticsearch B.V under one or more agreements.
2
+ # Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3
+ # See the LICENSE file in the project root for more information
17
4
 
18
5
  module Elasticsearch
19
6
 
@@ -1,19 +1,6 @@
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.
1
+ # Licensed to Elasticsearch B.V under one or more agreements.
2
+ # Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3
+ # See the LICENSE file in the project root for more information
17
4
 
18
5
  module Elasticsearch
19
6
  module Transport
@@ -1,19 +1,6 @@
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.
1
+ # Licensed to Elasticsearch B.V under one or more agreements.
2
+ # Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3
+ # See the LICENSE file in the project root for more information
17
4
 
18
5
  module Elasticsearch
19
6
  module Transport
@@ -1,19 +1,6 @@
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.
1
+ # Licensed to Elasticsearch B.V under one or more agreements.
2
+ # Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3
+ # See the LICENSE file in the project root for more information
17
4
 
18
5
  module Elasticsearch
19
6
  module Transport
@@ -45,7 +32,8 @@ module Elasticsearch
45
32
  #
46
33
  def hosts
47
34
  Timeout::timeout(timeout, SnifferTimeoutError) do
48
- nodes = transport.perform_request('GET', '_nodes/http').body
35
+ nodes = transport.perform_request('GET', '_nodes/http', {}, nil, nil,
36
+ reload_on_failure: false).body
49
37
 
50
38
  hosts = nodes['nodes'].map do |id, info|
51
39
  if info[PROTOCOL]
@@ -1,22 +1,9 @@
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.
1
+ # Licensed to Elasticsearch B.V under one or more agreements.
2
+ # Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3
+ # See the LICENSE file in the project root for more information
17
4
 
18
5
  module Elasticsearch
19
6
  module Transport
20
- VERSION = "7.1.0"
7
+ VERSION = "7.4.0"
21
8
  end
22
9
  end
@@ -1,19 +1,6 @@
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.
1
+ # Licensed to Elasticsearch B.V under one or more agreements.
2
+ # Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3
+ # See the LICENSE file in the project root for more information
17
4
 
18
5
  require "uri"
19
6
  require "time"
@@ -1,18 +1,5 @@
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.
1
+ # Licensed to Elasticsearch B.V under one or more agreements.
2
+ # Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3
+ # See the LICENSE file in the project root for more information
17
4
 
18
5
  require 'elasticsearch/transport'
@@ -0,0 +1,241 @@
1
+ # Licensed to Elasticsearch B.V under one or more agreements.
2
+ # Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3
+ # See the LICENSE file in the project root for more information
4
+
5
+ require 'spec_helper'
6
+
7
+ describe Elasticsearch::Transport::Transport::Connections::Collection do
8
+
9
+ describe '#initialize' do
10
+
11
+ let(:collection) do
12
+ described_class.new
13
+ end
14
+
15
+ it 'has an empty list of connections as a default' do
16
+ expect(collection.connections).to be_empty
17
+ end
18
+
19
+ it 'has a default selector class' do
20
+ expect(collection.selector).not_to be_nil
21
+ end
22
+
23
+ context 'when a selector class is specified' do
24
+
25
+ let(:collection) do
26
+ described_class.new(selector_class: Elasticsearch::Transport::Transport::Connections::Selector::Random)
27
+ end
28
+
29
+ it 'sets the selector' do
30
+ expect(collection.selector).to be_a(Elasticsearch::Transport::Transport::Connections::Selector::Random)
31
+ end
32
+ end
33
+ end
34
+
35
+ describe '#get_connection' do
36
+
37
+ let(:collection) do
38
+ described_class.new(selector_class: Elasticsearch::Transport::Transport::Connections::Selector::Random)
39
+ end
40
+
41
+ before do
42
+ expect(collection.selector).to receive(:select).and_return('OK')
43
+ end
44
+
45
+ it 'uses the selector to select a connection' do
46
+ expect(collection.get_connection).to eq('OK')
47
+ end
48
+ end
49
+
50
+ describe '#hosts' do
51
+
52
+ let(:collection) do
53
+ described_class.new(connections: [ double('connection', host: 'A'),
54
+ double('connection', host: 'B') ])
55
+ end
56
+
57
+ it 'returns a list of hosts' do
58
+ expect(collection.hosts).to eq([ 'A', 'B'])
59
+ end
60
+ end
61
+
62
+ describe 'enumerable' do
63
+
64
+ let(:collection) do
65
+ described_class.new(connections: [ double('connection', host: 'A', dead?: false),
66
+ double('connection', host: 'B', dead?: false) ])
67
+ end
68
+
69
+ describe '#map' do
70
+
71
+ it 'responds to the method' do
72
+ expect(collection.map { |c| c.host.downcase }).to eq(['a', 'b'])
73
+ end
74
+ end
75
+
76
+ describe '#[]' do
77
+
78
+ it 'responds to the method' do
79
+ expect(collection[0].host).to eq('A')
80
+ expect(collection[1].host).to eq('B')
81
+ end
82
+ end
83
+
84
+ describe '#size' do
85
+
86
+ it 'responds to the method' do
87
+ expect(collection.size).to eq(2)
88
+ end
89
+ end
90
+
91
+ context 'when a connection is marked as dead' do
92
+
93
+ let(:collection) do
94
+ described_class.new(connections: [ double('connection', host: 'A', dead?: true),
95
+ double('connection', host: 'B', dead?: false) ])
96
+ end
97
+
98
+ it 'does not enumerate the dead connections' do
99
+ expect(collection.size).to eq(1)
100
+ expect(collection.collect { |c| c.host }).to eq(['B'])
101
+ end
102
+
103
+ context '#alive' do
104
+
105
+ it 'enumerates the alive connections' do
106
+ expect(collection.alive.collect { |c| c.host }).to eq(['B'])
107
+ end
108
+ end
109
+
110
+ context '#dead' do
111
+
112
+ it 'enumerates the alive connections' do
113
+ expect(collection.dead.collect { |c| c.host }).to eq(['A'])
114
+ end
115
+ end
116
+ end
117
+ end
118
+
119
+ describe '#add' do
120
+
121
+ let(:collection) do
122
+ described_class.new(connections: [ double('connection', host: 'A', dead?: false),
123
+ double('connection', host: 'B', dead?: false) ])
124
+ end
125
+
126
+ context 'when an array is provided' do
127
+
128
+ before do
129
+ collection.add([double('connection', host: 'C', dead?: false),
130
+ double('connection', host: 'D', dead?: false)])
131
+ end
132
+
133
+ it 'adds the connections' do
134
+ expect(collection.size).to eq(4)
135
+ end
136
+ end
137
+
138
+ context 'when an element is provided' do
139
+
140
+ before do
141
+ collection.add(double('connection', host: 'C', dead?: false))
142
+ end
143
+
144
+ it 'adds the connection' do
145
+ expect(collection.size).to eq(3)
146
+ end
147
+ end
148
+ end
149
+
150
+ describe '#remove' do
151
+
152
+ let(:connections) do
153
+ [ double('connection', host: 'A', dead?: false),
154
+ double('connection', host: 'B', dead?: false) ]
155
+ end
156
+
157
+ let(:collection) do
158
+ described_class.new(connections: connections)
159
+ end
160
+
161
+ context 'when an array is provided' do
162
+
163
+ before do
164
+ collection.remove(connections)
165
+ end
166
+
167
+ it 'removes the connections' do
168
+ expect(collection.size).to eq(0)
169
+ end
170
+ end
171
+
172
+ context 'when an element is provided' do
173
+
174
+ let(:connections) do
175
+ [ double('connection', host: 'A', dead?: false),
176
+ double('connection', host: 'B', dead?: false) ]
177
+ end
178
+
179
+ before do
180
+ collection.remove(connections.first)
181
+ end
182
+
183
+ it 'removes the connection' do
184
+ expect(collection.size).to eq(1)
185
+ end
186
+ end
187
+ end
188
+
189
+ describe '#get_connection' do
190
+
191
+ context 'when all connections are dead' do
192
+
193
+ let(:connection_a) do
194
+ Elasticsearch::Transport::Transport::Connections::Connection.new(host: { host: 'A' })
195
+ end
196
+
197
+ let(:connection_b) do
198
+ Elasticsearch::Transport::Transport::Connections::Connection.new(host: { host: 'B' })
199
+ end
200
+
201
+ let(:collection) do
202
+ described_class.new(connections: [connection_a, connection_b])
203
+ end
204
+
205
+ before do
206
+ connection_a.dead!.dead!
207
+ connection_b.dead!
208
+ end
209
+
210
+ it 'returns the connection with the least failures' do
211
+ expect(collection.get_connection.host[:host]).to eq('B')
212
+ end
213
+ end
214
+
215
+ context 'when multiple threads are used' do
216
+
217
+ let(:connections) do
218
+ 20.times.collect do |i|
219
+ Elasticsearch::Transport::Transport::Connections::Connection.new(host: { host: i })
220
+ end
221
+ end
222
+
223
+ let(:collection) do
224
+ described_class.new(connections: connections)
225
+ end
226
+
227
+ it 'allows threads to select connections in parallel' do
228
+ expect(10.times.collect do
229
+ threads = []
230
+ 20.times do
231
+ threads << Thread.new do
232
+ collection.get_connection
233
+ end
234
+ end
235
+ threads.map { |t| t.join }
236
+ collection.get_connection.host[:host]
237
+ end).to eq((0..9).to_a)
238
+ end
239
+ end
240
+ end
241
+ end