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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9392ad85128da7b9c3e85dfe0311316174fe36b014d573784f0de7fd60e5e9f2
4
- data.tar.gz: dec15a5ce58afb3a5e9d21c374c0c089d8f7855d44df540dd94f2cf2fe7a34ac
3
+ metadata.gz: f91a5f7f465b546a3a49c78d2076097eac9b6af8fd9fbee7f8c1c45df06bea16
4
+ data.tar.gz: 0a04dd7945a401c01e107c95062b738ff987a23706338d7436e88ff8e50bec13
5
5
  SHA512:
6
- metadata.gz: 7eb05753626860442cc047f379a4a0a74306a72b9d3d15bdd8fcf0d4c8f743653dc8d6213883807ac9ffa7e23263581e0a32aeefe7c5fa5ece3fcd7f1298ace8
7
- data.tar.gz: f4b1d0c916e95e5c93c9792bc16cdedf85f90b582ca15c5c3c5fe6272253a24973e3bc629cb927501e7650b25eacba091218eb332bebbd22e028c08846217856
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, # Integer, default 0 (no retry)
27
- :retry_interval_ms # Integer ms base interval, default 200
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 || HttpClient.new(timeout: config.http_timeout, tls_config: config.tls_config)
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 || HttpClient.new(timeout: config.http_timeout, tls_config: config.tls_config)
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
@@ -6,14 +6,16 @@ require "openssl"
6
6
 
7
7
  module Pinot
8
8
  class HttpClient
9
- MAX_POOL_SIZE = 5
10
- KEEP_ALIVE_TIMEOUT = 30
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 < KEEP_ALIVE_TIMEOUT
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 < MAX_POOL_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 KEEP_ALIVE_TIMEOUT / 2.0
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 >= KEEP_ALIVE_TIMEOUT
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 == "503"
208
+ if (error_class = HTTP_ERROR_MAP[resp.code])
198
209
  logger.error "Pinot broker returned HTTP #{resp.code}"
199
- raise TransportError, "http exception with HTTP status code #{resp.code}"
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 TransportError, *RETRYABLE_ERRORS => e
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
@@ -1,3 +1,3 @@
1
1
  module Pinot
2
- VERSION = "1.20.0"
2
+ VERSION = "1.21.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pinot-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.20.0
4
+ version: 1.21.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Xiang Fu