pinot-client 1.0.2 → 1.2.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/README.md +69 -1
- data/lib/pinot/config.rb +6 -2
- data/lib/pinot/connection.rb +7 -1
- data/lib/pinot/connection_factory.rb +7 -5
- data/lib/pinot/controller_based_broker_selector.rb +11 -5
- data/lib/pinot/errors.rb +8 -0
- data/lib/pinot/logger.rb +17 -0
- data/lib/pinot/prepared_statement.rb +4 -4
- data/lib/pinot/simple_broker_selector.rb +2 -2
- data/lib/pinot/table_aware_broker_selector.rb +3 -3
- data/lib/pinot/tls_config.rb +15 -0
- data/lib/pinot/transport.rb +90 -16
- data/lib/pinot/version.rb +1 -1
- data/lib/pinot.rb +3 -0
- metadata +19 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8307d3d291be0af45b13a63512bd9f7d2f10d1c4db6383edd72b9ab5bb7c1e03
|
|
4
|
+
data.tar.gz: c74a5865da644b9edf2db66cfcc79eb6021e8ac06a872ab7e2d2528448ea1ea2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ae76f79806e4a0eef9f18f7ea7253a2ad4aba9f098bdf389e91b5cb1e2463d1e5637f158984523ceb9e99de0da293f541e37c36ac322d877d78c0be024ff6883
|
|
7
|
+
data.tar.gz: 034d035acefa3616c6f4892f22bd6cfc9569e43ffa5189930b96efc6a232a0e0fe11795a96e4546be6d7577425da3e590afab42223730ae682d86abb868d16e5
|
data/README.md
CHANGED
|
@@ -67,7 +67,7 @@ client = Pinot.from_controller("localhost:9000")
|
|
|
67
67
|
```ruby
|
|
68
68
|
config = Pinot::ClientConfig.new(
|
|
69
69
|
broker_list: ["localhost:8000"],
|
|
70
|
-
http_timeout: 10, # seconds
|
|
70
|
+
http_timeout: 10, # seconds — sets open_timeout, read_timeout, and write_timeout on the underlying Net::HTTP connection
|
|
71
71
|
extra_http_header: { "Authorization" => "Bearer <token>" },
|
|
72
72
|
use_multistage_engine: false,
|
|
73
73
|
controller_config: Pinot::ControllerConfig.new(
|
|
@@ -79,6 +79,38 @@ config = Pinot::ClientConfig.new(
|
|
|
79
79
|
client = Pinot.from_config(config)
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
+
### With TLS
|
|
83
|
+
|
|
84
|
+
```ruby
|
|
85
|
+
tls = Pinot::TlsConfig.new(
|
|
86
|
+
ca_cert_file: "/path/to/ca.pem",
|
|
87
|
+
client_cert_file: "/path/to/client.crt",
|
|
88
|
+
client_key_file: "/path/to/client.key",
|
|
89
|
+
insecure_skip_verify: false # set true to skip cert verification
|
|
90
|
+
)
|
|
91
|
+
config = Pinot::ClientConfig.new(
|
|
92
|
+
broker_list: ["https://pinot-broker.example.com:8000"],
|
|
93
|
+
tls_config: tls
|
|
94
|
+
)
|
|
95
|
+
client = Pinot.from_config(config)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Logging
|
|
99
|
+
|
|
100
|
+
By default the client logs warnings to stdout. Configure a custom logger globally or per-client:
|
|
101
|
+
|
|
102
|
+
```ruby
|
|
103
|
+
# Global logger
|
|
104
|
+
Pinot::Logging.logger = Logger.new("pinot.log", level: Logger::DEBUG)
|
|
105
|
+
|
|
106
|
+
# Per-client logger via ClientConfig
|
|
107
|
+
config = Pinot::ClientConfig.new(
|
|
108
|
+
broker_list: ["localhost:8000"],
|
|
109
|
+
logger: Logger.new($stdout, level: Logger::INFO)
|
|
110
|
+
)
|
|
111
|
+
client = Pinot.from_config(config)
|
|
112
|
+
```
|
|
113
|
+
|
|
82
114
|
## Executing Queries
|
|
83
115
|
|
|
84
116
|
### Simple SQL
|
|
@@ -106,6 +138,16 @@ client.use_multistage_engine = true
|
|
|
106
138
|
resp = client.execute_sql("baseballStats", "SELECT teamID, count(*) FROM baseballStats GROUP BY teamID")
|
|
107
139
|
```
|
|
108
140
|
|
|
141
|
+
Or enable it upfront via `ClientConfig`:
|
|
142
|
+
|
|
143
|
+
```ruby
|
|
144
|
+
config = Pinot::ClientConfig.new(
|
|
145
|
+
broker_list: ["localhost:8000"],
|
|
146
|
+
use_multistage_engine: true
|
|
147
|
+
)
|
|
148
|
+
client = Pinot.from_config(config)
|
|
149
|
+
```
|
|
150
|
+
|
|
109
151
|
### Trace
|
|
110
152
|
|
|
111
153
|
```ruby
|
|
@@ -114,6 +156,32 @@ resp = client.execute_sql("baseballStats", "SELECT count(*) FROM baseballStats")
|
|
|
114
156
|
client.close_trace
|
|
115
157
|
```
|
|
116
158
|
|
|
159
|
+
## Error Handling
|
|
160
|
+
|
|
161
|
+
All errors raised by the client inherit from `Pinot::Error < StandardError`:
|
|
162
|
+
|
|
163
|
+
| Class | Raised when |
|
|
164
|
+
|-------|-------------|
|
|
165
|
+
| `Pinot::BrokerNotFoundError` | No broker available (empty list or all offline) |
|
|
166
|
+
| `Pinot::TableNotFoundError` | Named table not found in broker map |
|
|
167
|
+
| `Pinot::TransportError` | Non-200 HTTP response from broker |
|
|
168
|
+
| `Pinot::PreparedStatementClosedError` | Operation on a closed prepared statement |
|
|
169
|
+
| `Pinot::ConfigurationError` | Invalid config (bad URL scheme, missing broker source) |
|
|
170
|
+
|
|
171
|
+
Example:
|
|
172
|
+
|
|
173
|
+
```ruby
|
|
174
|
+
begin
|
|
175
|
+
resp = client.execute_sql("myTable", "SELECT * FROM myTable")
|
|
176
|
+
rescue Pinot::TableNotFoundError => e
|
|
177
|
+
puts "Table missing: #{e.message}"
|
|
178
|
+
rescue Pinot::TransportError => e
|
|
179
|
+
puts "Broker error: #{e.message}"
|
|
180
|
+
rescue Pinot::Error => e
|
|
181
|
+
puts "Pinot error: #{e.message}"
|
|
182
|
+
end
|
|
183
|
+
```
|
|
184
|
+
|
|
117
185
|
## Reading Results
|
|
118
186
|
|
|
119
187
|
`execute_sql` returns a `Pinot::BrokerResponse`. Results are in `result_table`:
|
data/lib/pinot/config.rb
CHANGED
|
@@ -11,20 +11,24 @@ module Pinot
|
|
|
11
11
|
|
|
12
12
|
class ClientConfig
|
|
13
13
|
attr_accessor :broker_list, :http_timeout, :extra_http_header,
|
|
14
|
-
:use_multistage_engine, :controller_config
|
|
14
|
+
:use_multistage_engine, :controller_config, :logger, :tls_config
|
|
15
15
|
|
|
16
16
|
def initialize(
|
|
17
17
|
broker_list: [],
|
|
18
18
|
http_timeout: nil,
|
|
19
19
|
extra_http_header: {},
|
|
20
20
|
use_multistage_engine: false,
|
|
21
|
-
controller_config: nil
|
|
21
|
+
controller_config: nil,
|
|
22
|
+
logger: nil,
|
|
23
|
+
tls_config: nil
|
|
22
24
|
)
|
|
23
25
|
@broker_list = broker_list
|
|
24
26
|
@http_timeout = http_timeout
|
|
25
27
|
@extra_http_header = extra_http_header
|
|
26
28
|
@use_multistage_engine = use_multistage_engine
|
|
27
29
|
@controller_config = controller_config
|
|
30
|
+
@logger = logger
|
|
31
|
+
@tls_config = tls_config
|
|
28
32
|
end
|
|
29
33
|
end
|
|
30
34
|
end
|
data/lib/pinot/connection.rb
CHANGED
|
@@ -2,11 +2,12 @@ require "bigdecimal"
|
|
|
2
2
|
|
|
3
3
|
module Pinot
|
|
4
4
|
class Connection
|
|
5
|
-
def initialize(transport:, broker_selector:, use_multistage_engine: false)
|
|
5
|
+
def initialize(transport:, broker_selector:, use_multistage_engine: false, logger: nil)
|
|
6
6
|
@transport = transport
|
|
7
7
|
@broker_selector = broker_selector
|
|
8
8
|
@use_multistage_engine = use_multistage_engine
|
|
9
9
|
@trace = false
|
|
10
|
+
@logger = logger
|
|
10
11
|
end
|
|
11
12
|
|
|
12
13
|
def use_multistage_engine=(val)
|
|
@@ -22,6 +23,7 @@ module Pinot
|
|
|
22
23
|
end
|
|
23
24
|
|
|
24
25
|
def execute_sql(table, query)
|
|
26
|
+
logger.debug "Executing SQL on table=#{table}: #{query}"
|
|
25
27
|
broker = @broker_selector.select_broker(table)
|
|
26
28
|
@transport.execute(broker, build_request(query))
|
|
27
29
|
rescue => e
|
|
@@ -87,6 +89,10 @@ module Pinot
|
|
|
87
89
|
|
|
88
90
|
private
|
|
89
91
|
|
|
92
|
+
def logger
|
|
93
|
+
@logger || Pinot::Logging.logger
|
|
94
|
+
end
|
|
95
|
+
|
|
90
96
|
def build_request(query)
|
|
91
97
|
Request.new("sql", query, @trace, @use_multistage_engine)
|
|
92
98
|
end
|
|
@@ -12,20 +12,22 @@ module Pinot
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def self.from_config(config, http_client: nil)
|
|
15
|
-
inner = http_client || HttpClient.new(timeout: config.http_timeout)
|
|
15
|
+
inner = http_client || HttpClient.new(timeout: config.http_timeout, tls_config: config.tls_config)
|
|
16
16
|
|
|
17
17
|
transport = JsonHttpTransport.new(
|
|
18
18
|
http_client: inner,
|
|
19
|
-
extra_headers: config.extra_http_header || {}
|
|
19
|
+
extra_headers: config.extra_http_header || {},
|
|
20
|
+
logger: config.logger
|
|
20
21
|
)
|
|
21
22
|
|
|
22
23
|
selector = build_selector(config, inner)
|
|
23
|
-
raise
|
|
24
|
+
raise ConfigurationError, "must specify broker_list or controller_config" unless selector
|
|
24
25
|
|
|
25
26
|
conn = Connection.new(
|
|
26
27
|
transport: transport,
|
|
27
28
|
broker_selector: selector,
|
|
28
|
-
use_multistage_engine: config.use_multistage_engine || false
|
|
29
|
+
use_multistage_engine: config.use_multistage_engine || false,
|
|
30
|
+
logger: config.logger
|
|
29
31
|
)
|
|
30
32
|
|
|
31
33
|
selector.init
|
|
@@ -36,7 +38,7 @@ module Pinot
|
|
|
36
38
|
if config.broker_list && !config.broker_list.empty?
|
|
37
39
|
SimpleBrokerSelector.new(config.broker_list)
|
|
38
40
|
elsif config.controller_config
|
|
39
|
-
ControllerBasedBrokerSelector.new(config.controller_config, http_client)
|
|
41
|
+
ControllerBasedBrokerSelector.new(config.controller_config, http_client, logger: config.logger)
|
|
40
42
|
end
|
|
41
43
|
end
|
|
42
44
|
private_class_method :build_selector
|
|
@@ -7,16 +7,18 @@ module Pinot
|
|
|
7
7
|
CONTROLLER_API_PATH = "/v2/brokers/tables?state=ONLINE"
|
|
8
8
|
DEFAULT_UPDATE_FREQ_MS = 1000
|
|
9
9
|
|
|
10
|
-
def initialize(config, http_client = nil)
|
|
10
|
+
def initialize(config, http_client = nil, logger: nil)
|
|
11
11
|
super()
|
|
12
12
|
@config = config
|
|
13
13
|
@internal_http = http_client || HttpClient.new
|
|
14
|
+
@logger = logger
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
def init
|
|
17
18
|
@config.update_freq_ms ||= DEFAULT_UPDATE_FREQ_MS
|
|
18
19
|
@controller_url = build_controller_url(@config.controller_address)
|
|
19
20
|
fetch_and_update
|
|
21
|
+
logger.info "ControllerBasedBrokerSelector initialized with #{@all_broker_list.size} brokers"
|
|
20
22
|
start_background_refresh
|
|
21
23
|
nil
|
|
22
24
|
end
|
|
@@ -26,7 +28,7 @@ module Pinot
|
|
|
26
28
|
if addr.include?("://")
|
|
27
29
|
scheme = addr.split("://").first
|
|
28
30
|
unless %w[http https].include?(scheme)
|
|
29
|
-
raise "unsupported controller URL scheme: #{scheme}"
|
|
31
|
+
raise ConfigurationError, "unsupported controller URL scheme: #{scheme}"
|
|
30
32
|
end
|
|
31
33
|
addr.chomp("/") + CONTROLLER_API_PATH
|
|
32
34
|
else
|
|
@@ -43,20 +45,24 @@ module Pinot
|
|
|
43
45
|
resp = @internal_http.get(@controller_url, headers: headers)
|
|
44
46
|
|
|
45
47
|
unless resp.code.to_i == 200
|
|
46
|
-
raise "controller API returned HTTP status code #{resp.code}"
|
|
48
|
+
raise TransportError, "controller API returned HTTP status code #{resp.code}"
|
|
47
49
|
end
|
|
48
50
|
|
|
49
51
|
body = resp.body
|
|
50
52
|
begin
|
|
51
53
|
raw = JSON.parse(body)
|
|
52
54
|
rescue JSON::ParserError => e
|
|
53
|
-
raise "error decoding controller API response: #{e.message}"
|
|
55
|
+
raise ConfigurationError, "error decoding controller API response: #{e.message}"
|
|
54
56
|
end
|
|
55
57
|
|
|
56
58
|
cr = ControllerResponse.new(raw)
|
|
57
59
|
update_broker_data(cr.extract_broker_list, cr.extract_table_to_broker_map)
|
|
58
60
|
end
|
|
59
61
|
|
|
62
|
+
def logger
|
|
63
|
+
@logger || Pinot::Logging.logger
|
|
64
|
+
end
|
|
65
|
+
|
|
60
66
|
def start_background_refresh
|
|
61
67
|
interval = @config.update_freq_ms / 1000.0
|
|
62
68
|
Thread.new do
|
|
@@ -65,7 +71,7 @@ module Pinot
|
|
|
65
71
|
begin
|
|
66
72
|
fetch_and_update
|
|
67
73
|
rescue => e
|
|
68
|
-
warn "Pinot
|
|
74
|
+
logger.warn "Pinot controller refresh failed: #{e.message}"
|
|
69
75
|
end
|
|
70
76
|
end
|
|
71
77
|
end
|
data/lib/pinot/errors.rb
ADDED
data/lib/pinot/logger.rb
ADDED
|
@@ -51,7 +51,7 @@ module Pinot
|
|
|
51
51
|
|
|
52
52
|
def set(index, value)
|
|
53
53
|
@mutex.synchronize do
|
|
54
|
-
raise "prepared statement is closed" if @closed
|
|
54
|
+
raise PreparedStatementClosedError, "prepared statement is closed" if @closed
|
|
55
55
|
unless index >= 1 && index <= @param_count
|
|
56
56
|
raise "parameter index #{index} is out of range [1, #{@param_count}]"
|
|
57
57
|
end
|
|
@@ -62,7 +62,7 @@ module Pinot
|
|
|
62
62
|
|
|
63
63
|
def execute
|
|
64
64
|
@mutex.synchronize do
|
|
65
|
-
raise "prepared statement is closed" if @closed
|
|
65
|
+
raise PreparedStatementClosedError, "prepared statement is closed" if @closed
|
|
66
66
|
@parameters.each_with_index do |p, i|
|
|
67
67
|
raise "parameter at index #{i + 1} is not set" if p.nil?
|
|
68
68
|
end
|
|
@@ -76,7 +76,7 @@ module Pinot
|
|
|
76
76
|
end
|
|
77
77
|
|
|
78
78
|
def execute_with_params(*params)
|
|
79
|
-
@mutex.synchronize { raise "prepared statement is closed" if @closed }
|
|
79
|
+
@mutex.synchronize { raise PreparedStatementClosedError, "prepared statement is closed" if @closed }
|
|
80
80
|
if params.length != @param_count
|
|
81
81
|
raise "expected #{@param_count} parameters, got #{params.length}"
|
|
82
82
|
end
|
|
@@ -90,7 +90,7 @@ module Pinot
|
|
|
90
90
|
|
|
91
91
|
def clear_parameters
|
|
92
92
|
@mutex.synchronize do
|
|
93
|
-
raise "prepared statement is closed" if @closed
|
|
93
|
+
raise PreparedStatementClosedError, "prepared statement is closed" if @closed
|
|
94
94
|
@parameters.fill(nil)
|
|
95
95
|
end
|
|
96
96
|
nil
|
|
@@ -7,11 +7,11 @@ module Pinot
|
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
def init
|
|
10
|
-
raise "no pre-configured broker lists" if @broker_list.empty?
|
|
10
|
+
raise BrokerNotFoundError, "no pre-configured broker lists" if @broker_list.empty?
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def select_broker(_table)
|
|
14
|
-
raise "no pre-configured broker lists" if @broker_list.empty?
|
|
14
|
+
raise BrokerNotFoundError, "no pre-configured broker lists" if @broker_list.empty?
|
|
15
15
|
@broker_list.sample
|
|
16
16
|
end
|
|
17
17
|
end
|
|
@@ -19,12 +19,12 @@ module Pinot
|
|
|
19
19
|
table_name = extract_table_name(table.to_s)
|
|
20
20
|
@mutex.synchronize do
|
|
21
21
|
if table_name.empty?
|
|
22
|
-
raise "no available broker" if @all_broker_list.empty?
|
|
22
|
+
raise BrokerNotFoundError, "no available broker" if @all_broker_list.empty?
|
|
23
23
|
return @all_broker_list.sample
|
|
24
24
|
end
|
|
25
25
|
brokers = @table_broker_map[table_name]
|
|
26
|
-
raise "unable to find table: #{table}" unless brokers
|
|
27
|
-
raise "no available broker for table: #{table}" if brokers.empty?
|
|
26
|
+
raise TableNotFoundError, "unable to find table: #{table}" unless brokers
|
|
27
|
+
raise BrokerNotFoundError, "no available broker for table: #{table}" if brokers.empty?
|
|
28
28
|
brokers.sample
|
|
29
29
|
end
|
|
30
30
|
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Pinot
|
|
2
|
+
class TlsConfig
|
|
3
|
+
attr_accessor :ca_cert_file, # path to CA cert PEM file (String, optional)
|
|
4
|
+
:client_cert_file, # path to client cert PEM file (String, optional)
|
|
5
|
+
:client_key_file, # path to client key PEM file (String, optional)
|
|
6
|
+
:insecure_skip_verify # boolean, skip server cert verification (default: false)
|
|
7
|
+
|
|
8
|
+
def initialize(ca_cert_file: nil, client_cert_file: nil, client_key_file: nil, insecure_skip_verify: false)
|
|
9
|
+
@ca_cert_file = ca_cert_file
|
|
10
|
+
@client_cert_file = client_cert_file
|
|
11
|
+
@client_key_file = client_key_file
|
|
12
|
+
@insecure_skip_verify = insecure_skip_verify
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
data/lib/pinot/transport.rb
CHANGED
|
@@ -2,38 +2,104 @@ require "net/http"
|
|
|
2
2
|
require "uri"
|
|
3
3
|
require "json"
|
|
4
4
|
require "securerandom"
|
|
5
|
+
require "openssl"
|
|
5
6
|
|
|
6
7
|
module Pinot
|
|
7
8
|
class HttpClient
|
|
8
|
-
|
|
9
|
+
MAX_POOL_SIZE = 5
|
|
10
|
+
KEEP_ALIVE_TIMEOUT = 30
|
|
11
|
+
|
|
12
|
+
def initialize(timeout: nil, tls_config: nil)
|
|
9
13
|
@timeout = timeout
|
|
14
|
+
@tls_config = tls_config
|
|
15
|
+
@pool = {}
|
|
16
|
+
@pool_mutex = Mutex.new
|
|
10
17
|
end
|
|
11
18
|
|
|
12
19
|
def post(url, body:, headers: {})
|
|
13
20
|
uri = URI.parse(url)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
http.
|
|
21
|
+
with_connection(url) do |http|
|
|
22
|
+
req = Net::HTTP::Post.new(uri.request_uri)
|
|
23
|
+
headers.each { |k, v| req[k] = v }
|
|
24
|
+
req.body = body
|
|
25
|
+
http.request(req)
|
|
19
26
|
end
|
|
20
|
-
req = Net::HTTP::Post.new(uri.request_uri)
|
|
21
|
-
headers.each { |k, v| req[k] = v }
|
|
22
|
-
req.body = body
|
|
23
|
-
http.request(req)
|
|
24
27
|
end
|
|
25
28
|
|
|
26
29
|
def get(url, headers: {})
|
|
27
30
|
uri = URI.parse(url)
|
|
31
|
+
with_connection(url) do |http|
|
|
32
|
+
req = Net::HTTP::Get.new(uri.request_uri)
|
|
33
|
+
headers.each { |k, v| req[k] = v }
|
|
34
|
+
http.request(req)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def with_connection(url)
|
|
41
|
+
uri = URI.parse(url)
|
|
42
|
+
key = "#{uri.host}:#{uri.port}"
|
|
43
|
+
http = checkout(key, uri)
|
|
44
|
+
begin
|
|
45
|
+
result = yield http
|
|
46
|
+
checkin(key, http)
|
|
47
|
+
result
|
|
48
|
+
rescue => e
|
|
49
|
+
http.finish rescue nil
|
|
50
|
+
raise e
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def checkout(key, uri)
|
|
55
|
+
@pool_mutex.synchronize { @pool[key]&.pop } || new_connection(uri)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def checkin(key, http)
|
|
59
|
+
@pool_mutex.synchronize do
|
|
60
|
+
pool_for_key = @pool[key] ||= []
|
|
61
|
+
if pool_for_key.size < MAX_POOL_SIZE
|
|
62
|
+
pool_for_key.push(http)
|
|
63
|
+
else
|
|
64
|
+
http.finish rescue nil
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def new_connection(uri)
|
|
28
70
|
http = Net::HTTP.new(uri.host, uri.port)
|
|
29
|
-
http
|
|
71
|
+
configure_ssl(http, uri)
|
|
30
72
|
if @timeout
|
|
31
73
|
http.open_timeout = @timeout
|
|
32
74
|
http.read_timeout = @timeout
|
|
75
|
+
http.write_timeout = @timeout
|
|
76
|
+
end
|
|
77
|
+
http.start
|
|
78
|
+
http
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def configure_ssl(http, uri)
|
|
82
|
+
if uri.scheme == "https"
|
|
83
|
+
http.use_ssl = true
|
|
84
|
+
if @tls_config
|
|
85
|
+
if @tls_config.ca_cert_file
|
|
86
|
+
store = OpenSSL::X509::Store.new
|
|
87
|
+
store.add_file(@tls_config.ca_cert_file)
|
|
88
|
+
http.cert_store = store
|
|
89
|
+
end
|
|
90
|
+
if @tls_config.client_cert_file && @tls_config.client_key_file
|
|
91
|
+
http.cert = OpenSSL::X509::Certificate.new(File.read(@tls_config.client_cert_file))
|
|
92
|
+
http.key = OpenSSL::PKey.read(File.read(@tls_config.client_key_file))
|
|
93
|
+
end
|
|
94
|
+
if @tls_config.insecure_skip_verify
|
|
95
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
96
|
+
else
|
|
97
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
else
|
|
101
|
+
http.use_ssl = false
|
|
33
102
|
end
|
|
34
|
-
req = Net::HTTP::Get.new(uri.request_uri)
|
|
35
|
-
headers.each { |k, v| req[k] = v }
|
|
36
|
-
http.request(req)
|
|
37
103
|
end
|
|
38
104
|
end
|
|
39
105
|
|
|
@@ -42,13 +108,16 @@ module Pinot
|
|
|
42
108
|
"Content-Type" => "application/json; charset=utf-8"
|
|
43
109
|
}.freeze
|
|
44
110
|
|
|
45
|
-
def initialize(http_client:, extra_headers: {}, timeout_ms: nil)
|
|
111
|
+
def initialize(http_client:, extra_headers: {}, timeout_ms: nil, logger: nil)
|
|
46
112
|
@http_client = http_client
|
|
47
113
|
@extra_headers = extra_headers
|
|
48
114
|
@timeout_ms = timeout_ms
|
|
115
|
+
@logger = logger
|
|
49
116
|
end
|
|
50
117
|
|
|
51
118
|
def execute(broker_address, request)
|
|
119
|
+
logger.debug "Pinot query to #{broker_address}: #{request.query}"
|
|
120
|
+
|
|
52
121
|
url = build_url(broker_address, request.query_format)
|
|
53
122
|
body = build_body(request)
|
|
54
123
|
headers = DEFAULT_HEADERS
|
|
@@ -58,7 +127,8 @@ module Pinot
|
|
|
58
127
|
resp = @http_client.post(url, body: body, headers: headers)
|
|
59
128
|
|
|
60
129
|
unless resp.code.to_i == 200
|
|
61
|
-
|
|
130
|
+
logger.error "Pinot broker returned HTTP #{resp.code}"
|
|
131
|
+
raise TransportError, "http exception with HTTP status code #{resp.code}"
|
|
62
132
|
end
|
|
63
133
|
|
|
64
134
|
begin
|
|
@@ -70,6 +140,10 @@ module Pinot
|
|
|
70
140
|
|
|
71
141
|
private
|
|
72
142
|
|
|
143
|
+
def logger
|
|
144
|
+
@logger || Pinot::Logging.logger
|
|
145
|
+
end
|
|
146
|
+
|
|
73
147
|
def build_url(broker_address, query_format)
|
|
74
148
|
base = if broker_address.start_with?("http://", "https://")
|
|
75
149
|
broker_address
|
data/lib/pinot/version.rb
CHANGED
data/lib/pinot.rb
CHANGED
|
@@ -4,8 +4,11 @@ require "json"
|
|
|
4
4
|
require "bigdecimal"
|
|
5
5
|
require "securerandom"
|
|
6
6
|
|
|
7
|
+
require_relative "pinot/errors"
|
|
7
8
|
require_relative "pinot/version"
|
|
9
|
+
require_relative "pinot/logger"
|
|
8
10
|
require_relative "pinot/config"
|
|
11
|
+
require_relative "pinot/tls_config"
|
|
9
12
|
require_relative "pinot/request"
|
|
10
13
|
require_relative "pinot/response"
|
|
11
14
|
require_relative "pinot/broker_selector"
|
metadata
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pinot-client
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Xiang Fu
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-04-
|
|
11
|
+
date: 2026-04-19 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: logger
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0'
|
|
13
27
|
- !ruby/object:Gem::Dependency
|
|
14
28
|
name: rspec
|
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -67,11 +81,14 @@ files:
|
|
|
67
81
|
- lib/pinot/connection_factory.rb
|
|
68
82
|
- lib/pinot/controller_based_broker_selector.rb
|
|
69
83
|
- lib/pinot/controller_response.rb
|
|
84
|
+
- lib/pinot/errors.rb
|
|
85
|
+
- lib/pinot/logger.rb
|
|
70
86
|
- lib/pinot/prepared_statement.rb
|
|
71
87
|
- lib/pinot/request.rb
|
|
72
88
|
- lib/pinot/response.rb
|
|
73
89
|
- lib/pinot/simple_broker_selector.rb
|
|
74
90
|
- lib/pinot/table_aware_broker_selector.rb
|
|
91
|
+
- lib/pinot/tls_config.rb
|
|
75
92
|
- lib/pinot/transport.rb
|
|
76
93
|
- lib/pinot/version.rb
|
|
77
94
|
homepage: https://github.com/startreedata/ruby-pinot-client
|