elastic-transport 8.0.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.github/check_license_headers.rb +33 -0
  3. data/.github/license-header.txt +16 -0
  4. data/.github/workflows/license.yml +13 -0
  5. data/.github/workflows/tests.yml +45 -0
  6. data/.gitignore +19 -0
  7. data/CHANGELOG.md +224 -0
  8. data/Gemfile +38 -0
  9. data/LICENSE +202 -0
  10. data/README.md +552 -0
  11. data/Rakefile +87 -0
  12. data/elastic-transport.gemspec +74 -0
  13. data/lib/elastic/transport/client.rb +276 -0
  14. data/lib/elastic/transport/meta_header.rb +135 -0
  15. data/lib/elastic/transport/redacted.rb +73 -0
  16. data/lib/elastic/transport/transport/base.rb +450 -0
  17. data/lib/elastic/transport/transport/connections/collection.rb +126 -0
  18. data/lib/elastic/transport/transport/connections/connection.rb +160 -0
  19. data/lib/elastic/transport/transport/connections/selector.rb +91 -0
  20. data/lib/elastic/transport/transport/errors.rb +91 -0
  21. data/lib/elastic/transport/transport/http/curb.rb +120 -0
  22. data/lib/elastic/transport/transport/http/faraday.rb +95 -0
  23. data/lib/elastic/transport/transport/http/manticore.rb +179 -0
  24. data/lib/elastic/transport/transport/loggable.rb +83 -0
  25. data/lib/elastic/transport/transport/response.rb +36 -0
  26. data/lib/elastic/transport/transport/serializer/multi_json.rb +52 -0
  27. data/lib/elastic/transport/transport/sniffer.rb +101 -0
  28. data/lib/elastic/transport/version.rb +22 -0
  29. data/lib/elastic/transport.rb +37 -0
  30. data/lib/elastic-transport.rb +18 -0
  31. data/spec/elasticsearch/connections/collection_spec.rb +266 -0
  32. data/spec/elasticsearch/connections/selector_spec.rb +166 -0
  33. data/spec/elasticsearch/transport/base_spec.rb +264 -0
  34. data/spec/elasticsearch/transport/client_spec.rb +1651 -0
  35. data/spec/elasticsearch/transport/meta_header_spec.rb +274 -0
  36. data/spec/elasticsearch/transport/sniffer_spec.rb +275 -0
  37. data/spec/spec_helper.rb +90 -0
  38. data/test/integration/transport_test.rb +98 -0
  39. data/test/profile/client_benchmark_test.rb +132 -0
  40. data/test/test_helper.rb +83 -0
  41. data/test/unit/connection_test.rb +135 -0
  42. data/test/unit/response_test.rb +30 -0
  43. data/test/unit/serializer_test.rb +33 -0
  44. data/test/unit/transport_base_test.rb +664 -0
  45. data/test/unit/transport_curb_test.rb +135 -0
  46. data/test/unit/transport_faraday_test.rb +228 -0
  47. data/test/unit/transport_manticore_test.rb +251 -0
  48. metadata +412 -0
@@ -0,0 +1,126 @@
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
+ module Elastic
19
+ module Transport
20
+ module Transport
21
+ module Connections
22
+ # Wraps the collection of connections for the transport object as an Enumerable object.
23
+ #
24
+ # @see Base#connections
25
+ # @see Selector::Base#select
26
+ # @see Connection
27
+ #
28
+ class Collection
29
+ include Enumerable
30
+
31
+ DEFAULT_SELECTOR = Selector::RoundRobin
32
+
33
+ attr_reader :selector
34
+
35
+ # @option arguments [Array] :connections An array of {Connection} objects.
36
+ # @option arguments [Constant] :selector_class The class to be used as a connection selector strategy.
37
+ # @option arguments [Object] :selector The selector strategy object.
38
+ #
39
+ def initialize(arguments={})
40
+ selector_class = arguments[:selector_class] || DEFAULT_SELECTOR
41
+ @connections = arguments[:connections] || []
42
+ @selector = arguments[:selector] || selector_class.new(arguments.merge(:connections => self))
43
+ end
44
+
45
+ # Returns an Array of hosts information in this collection as Hashes.
46
+ #
47
+ # @return [Array]
48
+ #
49
+ def hosts
50
+ @connections.to_a.map { |c| c.host }
51
+ end
52
+
53
+ # Returns an Array of alive connections.
54
+ #
55
+ # @return [Array]
56
+ #
57
+ def connections
58
+ @connections.reject { |c| c.dead? }
59
+ end
60
+ alias :alive :connections
61
+
62
+ # Returns an Array of dead connections.
63
+ #
64
+ # @return [Array]
65
+ #
66
+ def dead
67
+ @connections.select { |c| c.dead? }
68
+ end
69
+
70
+ # Returns an Array of all connections, both dead and alive
71
+ #
72
+ # @return [Array]
73
+ #
74
+ def all
75
+ @connections
76
+ end
77
+
78
+ # Returns a connection.
79
+ #
80
+ # If there are no alive connections, returns a connection with least failures.
81
+ # Delegates to selector's `#select` method to get the connection.
82
+ #
83
+ # @return [Connection]
84
+ #
85
+ def get_connection(options={})
86
+ selector.select(options) || @connections.min_by(&:failures)
87
+ end
88
+
89
+ def each(&block)
90
+ connections.each(&block)
91
+ end
92
+
93
+ def slice(*args)
94
+ connections.slice(*args)
95
+ end
96
+ alias :[] :slice
97
+
98
+ def size
99
+ connections.size
100
+ end
101
+
102
+ # Add connection(s) to the collection
103
+ #
104
+ # @param connections [Connection,Array] A connection or an array of connections to add
105
+ # @return [self]
106
+ #
107
+ def add(connections)
108
+ @connections += Array(connections).to_a
109
+ self
110
+ end
111
+
112
+ # Remove connection(s) from the collection
113
+ #
114
+ # @param connections [Connection,Array] A connection or an array of connections to remove
115
+ # @return [self]
116
+ #
117
+ def remove(connections)
118
+ @connections -= Array(connections).to_a
119
+ self
120
+ end
121
+ end
122
+
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,160 @@
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
+ module Elastic
19
+ module Transport
20
+ module Transport
21
+ module Connections
22
+ # Wraps the connection information and logic.
23
+ #
24
+ # The Connection instance wraps the host information (hostname, port, attributes, etc),
25
+ # as well as the "session" (a transport client object, such as a {HTTP::Faraday} instance).
26
+ #
27
+ # It provides methods to construct and properly encode the URLs and paths for passing them
28
+ # to the transport client object.
29
+ #
30
+ # It provides methods to handle connection livecycle (dead, alive, healthy).
31
+ #
32
+ class Connection
33
+ DEFAULT_RESURRECT_TIMEOUT = 60
34
+
35
+ attr_reader :host, :connection, :options, :failures, :dead_since
36
+
37
+ # @option arguments [Hash] :host Host information (example: `{host: 'localhost', port: 9200}`)
38
+ # @option arguments [Object] :connection The transport-specific physical connection or "session"
39
+ # @option arguments [Hash] :options Options (usually passed in from transport)
40
+ #
41
+ def initialize(arguments={})
42
+ @host = arguments[:host].is_a?(Hash) ? Redacted.new(arguments[:host]) : arguments[:host]
43
+ @connection = arguments[:connection]
44
+ @options = arguments[:options] || {}
45
+ @state_mutex = Mutex.new
46
+
47
+ @options[:resurrect_timeout] ||= DEFAULT_RESURRECT_TIMEOUT
48
+ @dead = false
49
+ @failures = 0
50
+ end
51
+
52
+ # Returns the complete endpoint URL with host, port, path and serialized parameters.
53
+ #
54
+ # @return [String]
55
+ #
56
+ def full_url(path, params = {})
57
+ url = "#{host[:protocol]}://"
58
+ url += "#{CGI.escape(host[:user])}:#{CGI.escape(host[:password])}@" if host[:user]
59
+ url += "#{host[:host]}:#{host[:port]}"
60
+ url += "#{host[:path]}" if host[:path]
61
+ full_path = full_path(path, params)
62
+ url += '/' unless full_path.match?(/^\//)
63
+ url += full_path
64
+ end
65
+
66
+ # Returns the complete endpoint path with serialized parameters.
67
+ #
68
+ # @return [String]
69
+ #
70
+ def full_path(path, params={})
71
+ path + (params.empty? ? '' : "?#{::Faraday::Utils::ParamsHash[params].to_query}")
72
+ end
73
+
74
+ # Returns true when this connection has been marked dead, false otherwise.
75
+ #
76
+ # @return [Boolean]
77
+ #
78
+ def dead?
79
+ @dead || false
80
+ end
81
+
82
+ # Marks this connection as dead, incrementing the `failures` counter and
83
+ # storing the current time as `dead_since`.
84
+ #
85
+ # @return [self]
86
+ #
87
+ def dead!
88
+ @state_mutex.synchronize do
89
+ @dead = true
90
+ @failures += 1
91
+ @dead_since = Time.now
92
+ end
93
+ self
94
+ end
95
+
96
+ # Marks this connection as alive, ie. it is eligible to be returned from the pool by the selector.
97
+ #
98
+ # @return [self]
99
+ #
100
+ def alive!
101
+ @state_mutex.synchronize do
102
+ @dead = false
103
+ end
104
+ self
105
+ end
106
+
107
+ # Marks this connection as healthy, ie. a request has been successfully performed with it.
108
+ #
109
+ # @return [self]
110
+ #
111
+ def healthy!
112
+ @state_mutex.synchronize do
113
+ @dead = false
114
+ @failures = 0
115
+ end
116
+ self
117
+ end
118
+
119
+ # Marks this connection as alive, if the required timeout has passed.
120
+ #
121
+ # @return [self,nil]
122
+ # @see DEFAULT_RESURRECT_TIMEOUT
123
+ # @see #resurrectable?
124
+ #
125
+ def resurrect!
126
+ alive! if resurrectable?
127
+ end
128
+
129
+ # Returns true if the connection is eligible to be resurrected as alive, false otherwise.
130
+ #
131
+ # @return [Boolean]
132
+ #
133
+ def resurrectable?
134
+ @state_mutex.synchronize {
135
+ Time.now > @dead_since + ( @options[:resurrect_timeout] * 2 ** (@failures-1) )
136
+ }
137
+ end
138
+
139
+ # Equality operator based on connection protocol, host, port and attributes
140
+ #
141
+ # @return [Boolean]
142
+ #
143
+ def ==(other)
144
+ self.host[:protocol] == other.host[:protocol] && \
145
+ self.host[:host] == other.host[:host] && \
146
+ self.host[:port].to_i == other.host[:port].to_i && \
147
+ self.host[:attributes] == other.host[:attributes]
148
+ end
149
+
150
+ # @return [String]
151
+ #
152
+ def to_s
153
+ "<#{self.class.name} host: #{host} (#{dead? ? 'dead since ' + dead_since.to_s : 'alive'})>"
154
+ end
155
+ end
156
+
157
+ end
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,91 @@
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
+ module Elastic
19
+ module Transport
20
+ module Transport
21
+ module Connections
22
+ module Selector
23
+ # @abstract Common functionality for connection selector implementations.
24
+ #
25
+ module Base
26
+ attr_reader :connections
27
+
28
+ # @option arguments [Connections::Collection] :connections Collection with connections.
29
+ #
30
+ def initialize(arguments={})
31
+ @connections = arguments[:connections]
32
+ end
33
+
34
+ # @abstract Selector strategies implement this method to
35
+ # select and return a connection from the pool.
36
+ #
37
+ # @return [Connection]
38
+ #
39
+ def select(options={})
40
+ raise NoMethodError, "Implement this method in the selector implementation."
41
+ end
42
+ end
43
+
44
+ # "Random connection" selector strategy.
45
+ #
46
+ class Random
47
+ include Base
48
+
49
+ # Returns a random connection from the collection.
50
+ #
51
+ # @return [Connections::Connection]
52
+ #
53
+ def select(options={})
54
+ connections.to_a.sample
55
+ end
56
+ end
57
+
58
+ # "Round-robin" selector strategy (default).
59
+ #
60
+ class RoundRobin
61
+ include Base
62
+
63
+ # @option arguments [Connections::Collection] :connections Collection with connections.
64
+ #
65
+ def initialize(arguments = {})
66
+ super
67
+ @mutex = Mutex.new
68
+ @current = nil
69
+ end
70
+
71
+ # Returns the next connection from the collection, rotating them in round-robin fashion.
72
+ #
73
+ # @return [Connections::Connection]
74
+ #
75
+ def select(options={})
76
+ @mutex.synchronize do
77
+ conns = connections
78
+ if @current && (@current < conns.size-1)
79
+ @current += 1
80
+ else
81
+ @current = 0
82
+ end
83
+ conns[@current]
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,91 @@
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
+ module Elastic
19
+ module Transport
20
+ module Transport
21
+
22
+ # Generic client error
23
+ #
24
+ class Error < StandardError; end
25
+
26
+ # Reloading connections timeout (1 sec by default)
27
+ #
28
+ class SnifferTimeoutError < Timeout::Error; end
29
+
30
+ # Elastic server error (HTTP status 5xx)
31
+ #
32
+ class ServerError < Error; end
33
+
34
+ module Errors; end
35
+
36
+ HTTP_STATUSES = {
37
+ 300 => 'MultipleChoices',
38
+ 301 => 'MovedPermanently',
39
+ 302 => 'Found',
40
+ 303 => 'SeeOther',
41
+ 304 => 'NotModified',
42
+ 305 => 'UseProxy',
43
+ 307 => 'TemporaryRedirect',
44
+ 308 => 'PermanentRedirect',
45
+
46
+ 400 => 'BadRequest',
47
+ 401 => 'Unauthorized',
48
+ 402 => 'PaymentRequired',
49
+ 403 => 'Forbidden',
50
+ 404 => 'NotFound',
51
+ 405 => 'MethodNotAllowed',
52
+ 406 => 'NotAcceptable',
53
+ 407 => 'ProxyAuthenticationRequired',
54
+ 408 => 'RequestTimeout',
55
+ 409 => 'Conflict',
56
+ 410 => 'Gone',
57
+ 411 => 'LengthRequired',
58
+ 412 => 'PreconditionFailed',
59
+ 413 => 'RequestEntityTooLarge',
60
+ 414 => 'RequestURITooLong',
61
+ 415 => 'UnsupportedMediaType',
62
+ 416 => 'RequestedRangeNotSatisfiable',
63
+ 417 => 'ExpectationFailed',
64
+ 418 => 'ImATeapot',
65
+ 421 => 'TooManyConnectionsFromThisIP',
66
+ 426 => 'UpgradeRequired',
67
+ 429 => 'TooManyRequests',
68
+ 450 => 'BlockedByWindowsParentalControls',
69
+ 494 => 'RequestHeaderTooLarge',
70
+ 497 => 'HTTPToHTTPS',
71
+ 499 => 'ClientClosedRequest',
72
+
73
+ 500 => 'InternalServerError',
74
+ 501 => 'NotImplemented',
75
+ 502 => 'BadGateway',
76
+ 503 => 'ServiceUnavailable',
77
+ 504 => 'GatewayTimeout',
78
+ 505 => 'HTTPVersionNotSupported',
79
+ 506 => 'VariantAlsoNegotiates',
80
+ 510 => 'NotExtended'
81
+ }
82
+
83
+ ERRORS = HTTP_STATUSES.inject({}) do |sum, error|
84
+ status, name = error
85
+ sum[status] = Errors.const_set name, Class.new(ServerError)
86
+ sum
87
+ end
88
+
89
+ end
90
+ end
91
+ end