pinot-client 1.1.0 → 1.3.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 +59 -1
- data/lib/pinot/config.rb +5 -2
- data/lib/pinot/connection_factory.rb +15 -0
- data/lib/pinot/grpc_config.rb +15 -0
- data/lib/pinot/grpc_transport.rb +76 -0
- data/lib/pinot/proto/broker_service.proto +16 -0
- data/lib/pinot/proto/broker_service_pb.rb +20 -0
- data/lib/pinot/proto/broker_service_services_pb.rb +26 -0
- data/lib/pinot/version.rb +1 -1
- data/lib/pinot.rb +7 -0
- metadata +6 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f6d0692d6f1de1178eb0c27cb2f69e3f6575362b1163e58032538b2b003e13cb
|
|
4
|
+
data.tar.gz: d9ee2eb20b541cfe54ad640b223425ed16697e869b031349d021ef89c8bab5af
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d66f070cd740df59a8351724b954b902633052e0a94c0cffcc377cba887329709955b0c93075f5f90ea820abe453aa716b4f6eae80600ce90015eda669c33dd0
|
|
7
|
+
data.tar.gz: '03345279b3e4ae8621fa75a4222dce2988cf61255663b659c7307296a9506680f29f804938ec1b5d28326bf15e4e5c4cf5984e71233b2e0fcf255f6123f0fa36'
|
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
|
|
@@ -124,6 +156,32 @@ resp = client.execute_sql("baseballStats", "SELECT count(*) FROM baseballStats")
|
|
|
124
156
|
client.close_trace
|
|
125
157
|
```
|
|
126
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
|
+
|
|
127
185
|
## Reading Results
|
|
128
186
|
|
|
129
187
|
`execute_sql` returns a `Pinot::BrokerResponse`. Results are in `result_table`:
|
data/lib/pinot/config.rb
CHANGED
|
@@ -11,7 +11,8 @@ 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, :logger, :tls_config
|
|
14
|
+
:use_multistage_engine, :controller_config, :logger, :tls_config,
|
|
15
|
+
:grpc_config
|
|
15
16
|
|
|
16
17
|
def initialize(
|
|
17
18
|
broker_list: [],
|
|
@@ -20,7 +21,8 @@ module Pinot
|
|
|
20
21
|
use_multistage_engine: false,
|
|
21
22
|
controller_config: nil,
|
|
22
23
|
logger: nil,
|
|
23
|
-
tls_config: nil
|
|
24
|
+
tls_config: nil,
|
|
25
|
+
grpc_config: nil
|
|
24
26
|
)
|
|
25
27
|
@broker_list = broker_list
|
|
26
28
|
@http_timeout = http_timeout
|
|
@@ -29,6 +31,7 @@ module Pinot
|
|
|
29
31
|
@controller_config = controller_config
|
|
30
32
|
@logger = logger
|
|
31
33
|
@tls_config = tls_config
|
|
34
|
+
@grpc_config = grpc_config
|
|
32
35
|
end
|
|
33
36
|
end
|
|
34
37
|
end
|
|
@@ -12,6 +12,21 @@ module Pinot
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def self.from_config(config, http_client: nil)
|
|
15
|
+
if config.grpc_config
|
|
16
|
+
transport = GrpcTransport.new(config.grpc_config)
|
|
17
|
+
selector = SimpleBrokerSelector.new(config.grpc_config.broker_list)
|
|
18
|
+
|
|
19
|
+
conn = Connection.new(
|
|
20
|
+
transport: transport,
|
|
21
|
+
broker_selector: selector,
|
|
22
|
+
use_multistage_engine: config.use_multistage_engine || false,
|
|
23
|
+
logger: config.logger
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
selector.init
|
|
27
|
+
return conn
|
|
28
|
+
end
|
|
29
|
+
|
|
15
30
|
inner = http_client || HttpClient.new(timeout: config.http_timeout, tls_config: config.tls_config)
|
|
16
31
|
|
|
17
32
|
transport = JsonHttpTransport.new(
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Pinot
|
|
2
|
+
class GrpcConfig
|
|
3
|
+
attr_accessor :broker_list, # Array<String> of "host:port" (no scheme)
|
|
4
|
+
:tls_config, # Pinot::TlsConfig or nil
|
|
5
|
+
:extra_metadata, # Hash — extra gRPC metadata headers
|
|
6
|
+
:timeout # seconds (Integer/Float), nil = no timeout
|
|
7
|
+
|
|
8
|
+
def initialize(broker_list: [], tls_config: nil, extra_metadata: {}, timeout: nil)
|
|
9
|
+
@broker_list = broker_list
|
|
10
|
+
@tls_config = tls_config
|
|
11
|
+
@extra_metadata = extra_metadata
|
|
12
|
+
@timeout = timeout
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
begin
|
|
2
|
+
require "grpc"
|
|
3
|
+
require_relative "proto/broker_service_pb"
|
|
4
|
+
require_relative "proto/broker_service_services_pb"
|
|
5
|
+
rescue LoadError
|
|
6
|
+
raise LoadError, "The 'grpc' gem is required to use Pinot::GrpcTransport. Add it to your Gemfile: gem \"grpc\""
|
|
7
|
+
end
|
|
8
|
+
require_relative "grpc_config"
|
|
9
|
+
require_relative "response"
|
|
10
|
+
|
|
11
|
+
module Pinot
|
|
12
|
+
class GrpcTransport
|
|
13
|
+
def initialize(grpc_config)
|
|
14
|
+
@config = grpc_config
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Returns Pinot::BrokerResponse
|
|
18
|
+
def execute(broker_address, request)
|
|
19
|
+
stub = build_stub(broker_address)
|
|
20
|
+
grpc_request = build_request(request)
|
|
21
|
+
call_opts = build_call_opts
|
|
22
|
+
|
|
23
|
+
grpc_response = stub.submit(grpc_request, **call_opts)
|
|
24
|
+
BrokerResponse.from_json(grpc_response.payload)
|
|
25
|
+
rescue GRPC::BadStatus => e
|
|
26
|
+
raise TransportError, "gRPC error #{e.code}: #{e.details}"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def build_stub(broker_address)
|
|
32
|
+
creds = if @config.tls_config
|
|
33
|
+
build_ssl_credentials
|
|
34
|
+
else
|
|
35
|
+
:this_channel_is_insecure
|
|
36
|
+
end
|
|
37
|
+
Pinot::Broker::Grpc::PinotClientGrpcBrokerService::Stub.new(
|
|
38
|
+
broker_address, creds
|
|
39
|
+
)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def build_request(request)
|
|
43
|
+
meta = {}
|
|
44
|
+
query_opts = build_query_options(request)
|
|
45
|
+
meta["queryOptions"] = query_opts unless query_opts.empty?
|
|
46
|
+
meta["traceEnabled"] = "true" if request.trace
|
|
47
|
+
meta.merge!(@config.extra_metadata)
|
|
48
|
+
|
|
49
|
+
Pinot::Broker::Grpc::BrokerRequest.new(
|
|
50
|
+
sql: request.query,
|
|
51
|
+
metadata: meta
|
|
52
|
+
)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def build_query_options(request)
|
|
56
|
+
parts = ["groupByMode=sql", "responseFormat=sql"]
|
|
57
|
+
parts << "useMultistageEngine=true" if request.use_multistage_engine
|
|
58
|
+
parts.join(";")
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def build_call_opts
|
|
62
|
+
opts = {}
|
|
63
|
+
opts[:deadline] = Time.now + @config.timeout if @config.timeout
|
|
64
|
+
opts[:metadata] = @config.extra_metadata unless @config.extra_metadata.empty?
|
|
65
|
+
opts
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def build_ssl_credentials
|
|
69
|
+
tls = @config.tls_config
|
|
70
|
+
root_certs = tls.ca_cert_file ? File.read(tls.ca_cert_file) : nil
|
|
71
|
+
private_key = tls.client_key_file ? File.read(tls.client_key_file) : nil
|
|
72
|
+
cert_chain = tls.client_cert_file ? File.read(tls.client_cert_file) : nil
|
|
73
|
+
GRPC::Core::ChannelCredentials.new(root_certs, private_key, cert_chain)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
syntax = "proto3";
|
|
2
|
+
package pinot.broker.grpc;
|
|
3
|
+
|
|
4
|
+
message BrokerRequest {
|
|
5
|
+
string sql = 1;
|
|
6
|
+
map<string, string> metadata = 2;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
message BrokerResponse {
|
|
10
|
+
int32 result_row_size = 1;
|
|
11
|
+
bytes payload = 2;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
service PinotClientGrpcBrokerService {
|
|
15
|
+
rpc Submit(BrokerRequest) returns (BrokerResponse);
|
|
16
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# source: broker_service.proto
|
|
4
|
+
|
|
5
|
+
require 'google/protobuf'
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
descriptor_data = "\n\x14\x62roker_service.proto\x12\x11pinot.broker.grpc\"\x8f\x01\n\rBrokerRequest\x12\x0b\n\x03sql\x18\x01 \x01(\t\x12@\n\x08metadata\x18\x02 \x03(\x0b\x32..pinot.broker.grpc.BrokerRequest.MetadataEntry\x1a/\n\rMetadataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\":\n\x0e\x42rokerResponse\x12\x17\n\x0fresult_row_size\x18\x01 \x01(\x05\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\x32m\n\x1cPinotClientGrpcBrokerService\x12M\n\x06Submit\x12 .pinot.broker.grpc.BrokerRequest\x1a!.pinot.broker.grpc.BrokerResponseb\x06proto3"
|
|
9
|
+
|
|
10
|
+
pool = ::Google::Protobuf::DescriptorPool.generated_pool
|
|
11
|
+
pool.add_serialized_file(descriptor_data)
|
|
12
|
+
|
|
13
|
+
module Pinot
|
|
14
|
+
module Broker
|
|
15
|
+
module Grpc
|
|
16
|
+
BrokerRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("pinot.broker.grpc.BrokerRequest").msgclass
|
|
17
|
+
BrokerResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("pinot.broker.grpc.BrokerResponse").msgclass
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
2
|
+
# Source: broker_service.proto for package 'pinot.broker.grpc'
|
|
3
|
+
|
|
4
|
+
require 'grpc'
|
|
5
|
+
require_relative 'broker_service_pb'
|
|
6
|
+
|
|
7
|
+
module Pinot
|
|
8
|
+
module Broker
|
|
9
|
+
module Grpc
|
|
10
|
+
module PinotClientGrpcBrokerService
|
|
11
|
+
class Service
|
|
12
|
+
|
|
13
|
+
include ::GRPC::GenericService
|
|
14
|
+
|
|
15
|
+
self.marshal_class_method = :encode
|
|
16
|
+
self.unmarshal_class_method = :decode
|
|
17
|
+
self.service_name = 'pinot.broker.grpc.PinotClientGrpcBrokerService'
|
|
18
|
+
|
|
19
|
+
rpc :Submit, ::Pinot::Broker::Grpc::BrokerRequest, ::Pinot::Broker::Grpc::BrokerResponse
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
Stub = Service.rpc_stub_class
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
data/lib/pinot/version.rb
CHANGED
data/lib/pinot.rb
CHANGED
|
@@ -20,3 +20,10 @@ require_relative "pinot/transport"
|
|
|
20
20
|
require_relative "pinot/connection"
|
|
21
21
|
require_relative "pinot/prepared_statement"
|
|
22
22
|
require_relative "pinot/connection_factory"
|
|
23
|
+
|
|
24
|
+
begin
|
|
25
|
+
require_relative "pinot/grpc_transport"
|
|
26
|
+
require_relative "pinot/grpc_config"
|
|
27
|
+
rescue LoadError
|
|
28
|
+
# grpc gem not available; GrpcTransport disabled
|
|
29
|
+
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.
|
|
4
|
+
version: 1.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Xiang Fu
|
|
@@ -82,8 +82,13 @@ files:
|
|
|
82
82
|
- lib/pinot/controller_based_broker_selector.rb
|
|
83
83
|
- lib/pinot/controller_response.rb
|
|
84
84
|
- lib/pinot/errors.rb
|
|
85
|
+
- lib/pinot/grpc_config.rb
|
|
86
|
+
- lib/pinot/grpc_transport.rb
|
|
85
87
|
- lib/pinot/logger.rb
|
|
86
88
|
- lib/pinot/prepared_statement.rb
|
|
89
|
+
- lib/pinot/proto/broker_service.proto
|
|
90
|
+
- lib/pinot/proto/broker_service_pb.rb
|
|
91
|
+
- lib/pinot/proto/broker_service_services_pb.rb
|
|
87
92
|
- lib/pinot/request.rb
|
|
88
93
|
- lib/pinot/response.rb
|
|
89
94
|
- lib/pinot/simple_broker_selector.rb
|