pinot-client 1.2.0 → 1.4.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: 8307d3d291be0af45b13a63512bd9f7d2f10d1c4db6383edd72b9ab5bb7c1e03
4
- data.tar.gz: c74a5865da644b9edf2db66cfcc79eb6021e8ac06a872ab7e2d2528448ea1ea2
3
+ metadata.gz: 828dd3e3c441143fb0754adbcb1b50de4a070e777ad3a158c51dfabf91a3f877
4
+ data.tar.gz: 6c4a3f0ae7e703ae0d3ff18fd473030710e5a1954b9624207e0b2c6e944d2365
5
5
  SHA512:
6
- metadata.gz: ae76f79806e4a0eef9f18f7ea7253a2ad4aba9f098bdf389e91b5cb1e2463d1e5637f158984523ceb9e99de0da293f541e37c36ac322d877d78c0be024ff6883
7
- data.tar.gz: 034d035acefa3616c6f4892f22bd6cfc9569e43ffa5189930b96efc6a232a0e0fe11795a96e4546be6d7577425da3e590afab42223730ae682d86abb868d16e5
6
+ metadata.gz: ca183a5d10a9b87a18cb61725e6810f1446cd70b627e6a6a8320a3db9b2ec27981bf7d282e684f2c2cca94c2a87be2fb1595c2c178d378465bb77628cf501bb7
7
+ data.tar.gz: 715e0acb71c456409c999da0207c4c2c1aac310d27024dae6d9154996811d51b1f4c665686b609d598128b031f766ae06ee18c111c6ef0f6801b69cd523b5c82
data/lib/pinot/config.rb CHANGED
@@ -1,4 +1,14 @@
1
1
  module Pinot
2
+ class ZookeeperConfig
3
+ attr_accessor :zk_path, # "host:port" or "host1:port,host2:port/chroot"
4
+ :session_timeout_ms # default 30000
5
+
6
+ def initialize(zk_path:, session_timeout_ms: 30_000)
7
+ @zk_path = zk_path
8
+ @session_timeout_ms = session_timeout_ms
9
+ end
10
+ end
11
+
2
12
  class ControllerConfig
3
13
  attr_accessor :controller_address, :update_freq_ms, :extra_controller_api_headers
4
14
 
@@ -11,7 +21,8 @@ module Pinot
11
21
 
12
22
  class ClientConfig
13
23
  attr_accessor :broker_list, :http_timeout, :extra_http_header,
14
- :use_multistage_engine, :controller_config, :logger, :tls_config
24
+ :use_multistage_engine, :controller_config, :logger, :tls_config,
25
+ :grpc_config, :zookeeper_config
15
26
 
16
27
  def initialize(
17
28
  broker_list: [],
@@ -20,7 +31,9 @@ module Pinot
20
31
  use_multistage_engine: false,
21
32
  controller_config: nil,
22
33
  logger: nil,
23
- tls_config: nil
34
+ tls_config: nil,
35
+ grpc_config: nil,
36
+ zookeeper_config: nil
24
37
  )
25
38
  @broker_list = broker_list
26
39
  @http_timeout = http_timeout
@@ -29,6 +42,8 @@ module Pinot
29
42
  @controller_config = controller_config
30
43
  @logger = logger
31
44
  @tls_config = tls_config
45
+ @grpc_config = grpc_config
46
+ @zookeeper_config = zookeeper_config
32
47
  end
33
48
  end
34
49
  end
@@ -12,6 +12,40 @@ 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
+
30
+ if config.zookeeper_config
31
+ selector = ZookeeperBrokerSelector.new(zk_path: config.zookeeper_config.zk_path)
32
+ selector.init
33
+
34
+ inner = http_client || HttpClient.new(timeout: config.http_timeout, tls_config: config.tls_config)
35
+ transport = JsonHttpTransport.new(
36
+ http_client: inner,
37
+ extra_headers: config.extra_http_header || {},
38
+ logger: config.logger
39
+ )
40
+
41
+ return Connection.new(
42
+ transport: transport,
43
+ broker_selector: selector,
44
+ use_multistage_engine: config.use_multistage_engine || false,
45
+ logger: config.logger
46
+ )
47
+ end
48
+
15
49
  inner = http_client || HttpClient.new(timeout: config.http_timeout, tls_config: config.tls_config)
16
50
 
17
51
  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
@@ -1,3 +1,3 @@
1
1
  module Pinot
2
- VERSION = "1.2.0"
2
+ VERSION = "1.4.0"
3
3
  end
@@ -0,0 +1,80 @@
1
+ require "json"
2
+ require "set"
3
+ require_relative "table_aware_broker_selector"
4
+ require_relative "errors"
5
+
6
+ module Pinot
7
+ class ZookeeperBrokerSelector < TableAwareBrokerSelector
8
+ # ZK path where Pinot stores broker external view
9
+ BROKER_EXTERNAL_VIEW_PATH = "/EXTERNALVIEW/brokerResource"
10
+
11
+ def initialize(zk_path:, zk_client: nil)
12
+ super()
13
+ @zk_path = zk_path # e.g. "localhost:2181"
14
+ @zk_client = zk_client # injectable for testing
15
+ end
16
+
17
+ def init
18
+ @zk = @zk_client || build_zk_client
19
+ fetch_and_update
20
+ setup_watcher
21
+ end
22
+
23
+ private
24
+
25
+ def build_zk_client
26
+ begin
27
+ require "zk"
28
+ rescue LoadError
29
+ raise ConfigurationError, "The 'zk' gem is required to use ZookeeperBrokerSelector. Add it to your Gemfile: gem \"zk\""
30
+ end
31
+ ZK.new(@zk_path)
32
+ end
33
+
34
+ def fetch_and_update
35
+ data, _stat = @zk.get(BROKER_EXTERNAL_VIEW_PATH)
36
+ parsed = JSON.parse(data)
37
+ all_brokers, table_map = parse_external_view(parsed)
38
+ update_broker_data(all_brokers, table_map)
39
+ end
40
+
41
+ def setup_watcher
42
+ @zk.register(BROKER_EXTERNAL_VIEW_PATH) do |event|
43
+ fetch_and_update rescue nil
44
+ setup_watcher # re-register watch after each trigger
45
+ end
46
+ # Set initial watch
47
+ @zk.exists?(BROKER_EXTERNAL_VIEW_PATH, watch: true)
48
+ end
49
+
50
+ def parse_external_view(data)
51
+ # Pinot external view format:
52
+ # { "mapFields": { "tableName_OFFLINE": { "Broker_host_port": "ONLINE" } } }
53
+ map_fields = data["mapFields"] || {}
54
+
55
+ all_brokers = Set.new
56
+ table_map = {}
57
+
58
+ map_fields.each do |raw_table, broker_map|
59
+ # Strip _OFFLINE / _REALTIME suffix from the table key, matching Go's extractTableName
60
+ table_name = raw_table.sub(/_OFFLINE\z/, "").sub(/_REALTIME\z/, "")
61
+ brokers = []
62
+ broker_map.each do |broker_key, state|
63
+ next unless state == "ONLINE"
64
+ # Broker key format: Broker_<hostname>_<port>
65
+ # Use the last segment as port and the second-to-last as host
66
+ parts = broker_key.split("_")
67
+ next if parts.length < 2
68
+ port = parts.last
69
+ next unless port =~ /\A\d+\z/
70
+ host = parts[-2]
71
+ brokers << "#{host}:#{port}"
72
+ end
73
+ table_map[table_name] = brokers
74
+ all_brokers.merge(brokers)
75
+ end
76
+
77
+ [all_brokers.to_a, table_map]
78
+ end
79
+ end
80
+ end
data/lib/pinot.rb CHANGED
@@ -20,3 +20,15 @@ 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
30
+
31
+ begin
32
+ require_relative "pinot/zookeeper_broker_selector"
33
+ rescue LoadError
34
+ 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.2.0
4
+ version: 1.4.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
@@ -91,6 +96,7 @@ files:
91
96
  - lib/pinot/tls_config.rb
92
97
  - lib/pinot/transport.rb
93
98
  - lib/pinot/version.rb
99
+ - lib/pinot/zookeeper_broker_selector.rb
94
100
  homepage: https://github.com/startreedata/ruby-pinot-client
95
101
  licenses:
96
102
  - Apache-2.0