pinot-client 1.20.0 → 1.21.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/lib/pinot/config.rb +17 -4
- data/lib/pinot/connection_factory.rb +12 -2
- data/lib/pinot/errors.rb +3 -0
- data/lib/pinot/transport.rb +21 -10
- data/lib/pinot/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f91a5f7f465b546a3a49c78d2076097eac9b6af8fd9fbee7f8c1c45df06bea16
|
|
4
|
+
data.tar.gz: 0a04dd7945a401c01e107c95062b738ff987a23706338d7436e88ff8e50bec13
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: aeee0c948cca37c67a7b8bb103201ea4a4ad1dbe9e08476ca679efb10c4ea9951ff4ade65af6e8f7cc11e3c55b33ca66f969b0e4271fe97a8bd62bc7265c24aa
|
|
7
|
+
data.tar.gz: 8701f59b1adc42307e0774484a24d5c0e0cd33e8da378d9d2ccc65ad0240befcd094d58a136dc5031150fa687e88bbf87cbdde29e02d69e96462eedaf31dcbdc
|
data/lib/pinot/config.rb
CHANGED
|
@@ -23,8 +23,10 @@ module Pinot
|
|
|
23
23
|
attr_accessor :broker_list, :http_timeout, :query_timeout_ms, :extra_http_header,
|
|
24
24
|
:use_multistage_engine, :controller_config, :logger, :tls_config,
|
|
25
25
|
:grpc_config, :zookeeper_config,
|
|
26
|
-
:max_retries,
|
|
27
|
-
:retry_interval_ms
|
|
26
|
+
:max_retries, # Integer, default 0 (no retry)
|
|
27
|
+
:retry_interval_ms, # Integer ms base interval, default 200
|
|
28
|
+
:pool_size, # max idle connections per broker, default 5
|
|
29
|
+
:keep_alive_timeout # seconds before idle connection is reaped, default 30
|
|
28
30
|
|
|
29
31
|
def initialize(
|
|
30
32
|
broker_list: [],
|
|
@@ -38,7 +40,9 @@ module Pinot
|
|
|
38
40
|
grpc_config: nil,
|
|
39
41
|
zookeeper_config: nil,
|
|
40
42
|
max_retries: 0,
|
|
41
|
-
retry_interval_ms: 200
|
|
43
|
+
retry_interval_ms: 200,
|
|
44
|
+
pool_size: nil,
|
|
45
|
+
keep_alive_timeout: nil
|
|
42
46
|
)
|
|
43
47
|
@broker_list = broker_list
|
|
44
48
|
@http_timeout = http_timeout
|
|
@@ -50,9 +54,10 @@ module Pinot
|
|
|
50
54
|
@tls_config = tls_config
|
|
51
55
|
@grpc_config = grpc_config
|
|
52
56
|
@zookeeper_config = zookeeper_config
|
|
53
|
-
@query_timeout_ms = query_timeout_ms
|
|
54
57
|
@max_retries = max_retries
|
|
55
58
|
@retry_interval_ms = retry_interval_ms
|
|
59
|
+
@pool_size = pool_size
|
|
60
|
+
@keep_alive_timeout = keep_alive_timeout
|
|
56
61
|
end
|
|
57
62
|
|
|
58
63
|
def validate!
|
|
@@ -75,6 +80,14 @@ module Pinot
|
|
|
75
80
|
raise ConfigurationError, "query_timeout_ms must be positive, got: #{query_timeout_ms}"
|
|
76
81
|
end
|
|
77
82
|
|
|
83
|
+
if !pool_size.nil? && pool_size < 1
|
|
84
|
+
raise ConfigurationError, "pool_size must be at least 1, got: #{pool_size}"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
if !keep_alive_timeout.nil? && keep_alive_timeout <= 0
|
|
88
|
+
raise ConfigurationError, "keep_alive_timeout must be positive, got: #{keep_alive_timeout}"
|
|
89
|
+
end
|
|
90
|
+
|
|
78
91
|
self
|
|
79
92
|
end
|
|
80
93
|
end
|
|
@@ -34,7 +34,7 @@ module Pinot
|
|
|
34
34
|
selector = ZookeeperBrokerSelector.new(zk_path: config.zookeeper_config.zk_path)
|
|
35
35
|
selector.init
|
|
36
36
|
|
|
37
|
-
inner = http_client ||
|
|
37
|
+
inner = http_client || build_http_client(config)
|
|
38
38
|
transport = JsonHttpTransport.new(
|
|
39
39
|
http_client: inner,
|
|
40
40
|
extra_headers: config.extra_http_header || {},
|
|
@@ -53,7 +53,7 @@ module Pinot
|
|
|
53
53
|
)
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
-
inner = http_client ||
|
|
56
|
+
inner = http_client || build_http_client(config)
|
|
57
57
|
|
|
58
58
|
transport = JsonHttpTransport.new(
|
|
59
59
|
http_client: inner,
|
|
@@ -78,6 +78,16 @@ module Pinot
|
|
|
78
78
|
conn
|
|
79
79
|
end
|
|
80
80
|
|
|
81
|
+
def self.build_http_client(config)
|
|
82
|
+
HttpClient.new(
|
|
83
|
+
timeout: config.http_timeout,
|
|
84
|
+
tls_config: config.tls_config,
|
|
85
|
+
pool_size: config.pool_size,
|
|
86
|
+
keep_alive_timeout: config.keep_alive_timeout
|
|
87
|
+
)
|
|
88
|
+
end
|
|
89
|
+
private_class_method :build_http_client
|
|
90
|
+
|
|
81
91
|
def self.build_selector(config, http_client)
|
|
82
92
|
if config.broker_list && !config.broker_list.empty?
|
|
83
93
|
SimpleBrokerSelector.new(config.broker_list)
|
data/lib/pinot/errors.rb
CHANGED
|
@@ -3,6 +3,9 @@ module Pinot
|
|
|
3
3
|
class BrokerNotFoundError < Error; end
|
|
4
4
|
class TableNotFoundError < Error; end
|
|
5
5
|
class TransportError < Error; end
|
|
6
|
+
class BrokerUnavailableError < TransportError; end
|
|
7
|
+
class QueryTimeoutError < TransportError; end
|
|
8
|
+
class RateLimitError < TransportError; end
|
|
6
9
|
class PreparedStatementClosedError < Error; end
|
|
7
10
|
class ConfigurationError < Error; end
|
|
8
11
|
end
|
data/lib/pinot/transport.rb
CHANGED
|
@@ -6,14 +6,16 @@ require "openssl"
|
|
|
6
6
|
|
|
7
7
|
module Pinot
|
|
8
8
|
class HttpClient
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
DEFAULT_POOL_SIZE = 5
|
|
10
|
+
DEFAULT_KEEP_ALIVE_TIMEOUT = 30
|
|
11
11
|
|
|
12
12
|
PoolEntry = Struct.new(:http, :checked_in_at)
|
|
13
13
|
|
|
14
|
-
def initialize(timeout: nil, tls_config: nil)
|
|
14
|
+
def initialize(timeout: nil, tls_config: nil, pool_size: nil, keep_alive_timeout: nil)
|
|
15
15
|
@timeout = timeout
|
|
16
16
|
@tls_config = tls_config
|
|
17
|
+
@max_pool_size = pool_size || DEFAULT_POOL_SIZE
|
|
18
|
+
@keep_alive_timeout = keep_alive_timeout || DEFAULT_KEEP_ALIVE_TIMEOUT
|
|
17
19
|
@pool = {}
|
|
18
20
|
@pool_mutex = Mutex.new
|
|
19
21
|
@reaper = start_reaper
|
|
@@ -70,7 +72,7 @@ module Pinot
|
|
|
70
72
|
entries = @pool[key] ||= []
|
|
71
73
|
fresh = nil
|
|
72
74
|
while (entry = entries.pop)
|
|
73
|
-
if now - entry.checked_in_at <
|
|
75
|
+
if now - entry.checked_in_at < @keep_alive_timeout
|
|
74
76
|
fresh = entry.http
|
|
75
77
|
break
|
|
76
78
|
else
|
|
@@ -85,7 +87,7 @@ module Pinot
|
|
|
85
87
|
def checkin(key, http)
|
|
86
88
|
@pool_mutex.synchronize do
|
|
87
89
|
pool_for_key = @pool[key] ||= []
|
|
88
|
-
if pool_for_key.size <
|
|
90
|
+
if pool_for_key.size < @max_pool_size
|
|
89
91
|
pool_for_key.push(PoolEntry.new(http, Process.clock_gettime(Process::CLOCK_MONOTONIC)))
|
|
90
92
|
else
|
|
91
93
|
http.finish rescue nil
|
|
@@ -96,7 +98,7 @@ module Pinot
|
|
|
96
98
|
def start_reaper
|
|
97
99
|
t = Thread.new do
|
|
98
100
|
loop do
|
|
99
|
-
sleep
|
|
101
|
+
sleep @keep_alive_timeout / 2.0
|
|
100
102
|
reap_stale_connections
|
|
101
103
|
end
|
|
102
104
|
end
|
|
@@ -109,7 +111,7 @@ module Pinot
|
|
|
109
111
|
@pool_mutex.synchronize do
|
|
110
112
|
@pool.each_value do |entries|
|
|
111
113
|
entries.reject! do |entry|
|
|
112
|
-
if now - entry.checked_in_at >=
|
|
114
|
+
if now - entry.checked_in_at >= @keep_alive_timeout
|
|
113
115
|
entry.http.finish rescue nil
|
|
114
116
|
true
|
|
115
117
|
else
|
|
@@ -167,6 +169,15 @@ module Pinot
|
|
|
167
169
|
Net::OpenTimeout, Net::ReadTimeout, Net::WriteTimeout
|
|
168
170
|
].freeze
|
|
169
171
|
|
|
172
|
+
# HTTP status codes that map to specific error classes and are safe to retry
|
|
173
|
+
HTTP_ERROR_MAP = {
|
|
174
|
+
"429" => RateLimitError,
|
|
175
|
+
"503" => BrokerUnavailableError,
|
|
176
|
+
"504" => BrokerUnavailableError
|
|
177
|
+
}.freeze
|
|
178
|
+
|
|
179
|
+
RETRYABLE_HTTP_ERRORS = [RateLimitError, BrokerUnavailableError].freeze
|
|
180
|
+
|
|
170
181
|
def initialize(http_client:, extra_headers: {}, timeout_ms: nil, logger: nil,
|
|
171
182
|
max_retries: 0, retry_interval_ms: 200)
|
|
172
183
|
@http_client = http_client
|
|
@@ -194,9 +205,9 @@ module Pinot
|
|
|
194
205
|
|
|
195
206
|
resp = @http_client.post(url, body: body, headers: headers)
|
|
196
207
|
|
|
197
|
-
if resp.code
|
|
208
|
+
if (error_class = HTTP_ERROR_MAP[resp.code])
|
|
198
209
|
logger.error "Pinot broker returned HTTP #{resp.code}"
|
|
199
|
-
raise
|
|
210
|
+
raise error_class, "http exception with HTTP status code #{resp.code}"
|
|
200
211
|
end
|
|
201
212
|
|
|
202
213
|
unless resp.code.to_i == 200
|
|
@@ -209,7 +220,7 @@ module Pinot
|
|
|
209
220
|
rescue JSON::ParserError => e
|
|
210
221
|
raise e.message
|
|
211
222
|
end
|
|
212
|
-
rescue
|
|
223
|
+
rescue *RETRYABLE_HTTP_ERRORS, *RETRYABLE_ERRORS => e
|
|
213
224
|
if attempts < max_attempts
|
|
214
225
|
sleep_ms = (@retry_interval_ms || 200) * (2 ** (attempts - 1))
|
|
215
226
|
sleep(sleep_ms / 1000.0)
|
data/lib/pinot/version.rb
CHANGED