pinot-client 1.25.0 → 1.26.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/connection.rb +11 -0
- data/lib/pinot/paginator.rb +123 -0
- data/lib/pinot/response.rb +18 -1
- data/lib/pinot/transport.rb +11 -0
- data/lib/pinot/version.rb +1 -1
- data/lib/pinot.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0c6d47938639a210b77881492e1155d26728be12f987b9721712f16503183ec3
|
|
4
|
+
data.tar.gz: 9e14c467edbba3ad7fff1e20f6b288ac810895c5069549cd33f08e2972b8d739
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 73ec7a9ae84e96f71618f430ed542527fb9f9e16f64b1d0d78a1a8bcb0ba1950f6d7117072f0318ffcc08fdb0df2143be268667d6b96e4914d52fc4f0448c0fa
|
|
7
|
+
data.tar.gz: 9fe83c991b5ef549ce915dbe4af55b50143d17d6d9660915d5fd6308798541f81068e557317b8b88af167b55a63ea7781ef14dfff428cd51b671df7d2a0267b2
|
data/lib/pinot/connection.rb
CHANGED
|
@@ -79,6 +79,17 @@ module Pinot
|
|
|
79
79
|
results
|
|
80
80
|
end
|
|
81
81
|
|
|
82
|
+
def paginate(query, page_size: Paginator::DEFAULT_PAGE_SIZE, table: nil, extra_headers: {})
|
|
83
|
+
broker = @broker_selector.select_broker(table || "")
|
|
84
|
+
Paginator.new(
|
|
85
|
+
@transport.http_client,
|
|
86
|
+
broker,
|
|
87
|
+
query,
|
|
88
|
+
page_size: page_size,
|
|
89
|
+
extra_headers: extra_headers
|
|
90
|
+
)
|
|
91
|
+
end
|
|
92
|
+
|
|
82
93
|
def prepare(table, query_template)
|
|
83
94
|
raise ArgumentError, "table name cannot be empty" if table.nil? || table.strip.empty?
|
|
84
95
|
raise ArgumentError, "query template cannot be empty" if query_template.nil? || query_template.strip.empty?
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
module Pinot
|
|
2
|
+
# Implements Pinot's server-side cursor API.
|
|
3
|
+
#
|
|
4
|
+
# The broker stores the full result set and returns slices on demand.
|
|
5
|
+
# All fetch requests after the first must go to the same broker that
|
|
6
|
+
# owns the cursor state (brokerHost:brokerPort from the initial response).
|
|
7
|
+
#
|
|
8
|
+
# Usage:
|
|
9
|
+
# paginator = conn.paginate("SELECT * FROM t LIMIT 10000", page_size: 100)
|
|
10
|
+
# paginator.each_page { |resp| process(resp.result_table) }
|
|
11
|
+
# paginator.each_row { |row| puts row.map(&:to_s).join(", ") }
|
|
12
|
+
# paginator.delete # optional early cleanup; also called automatically on exhaustion
|
|
13
|
+
class Paginator
|
|
14
|
+
include Enumerable
|
|
15
|
+
|
|
16
|
+
DEFAULT_PAGE_SIZE = 1000
|
|
17
|
+
|
|
18
|
+
def initialize(http_client, broker_address, query, page_size:, extra_headers: {})
|
|
19
|
+
raise ArgumentError, "page_size must be a positive integer" unless page_size.is_a?(Integer) && page_size > 0
|
|
20
|
+
|
|
21
|
+
@http_client = http_client
|
|
22
|
+
@broker_address = broker_address
|
|
23
|
+
@query = query
|
|
24
|
+
@page_size = page_size
|
|
25
|
+
@extra_headers = extra_headers
|
|
26
|
+
|
|
27
|
+
@request_id = nil
|
|
28
|
+
@cursor_base = nil # "http://host:port" — set after first response
|
|
29
|
+
@exhausted = false
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Yields each page as a BrokerResponse. Returns an Enumerator without a block.
|
|
33
|
+
def each_page
|
|
34
|
+
return enum_for(:each_page) unless block_given?
|
|
35
|
+
|
|
36
|
+
# Submit the query and get the first page + cursor metadata
|
|
37
|
+
first = submit_cursor
|
|
38
|
+
return if first.result_table.nil? || first.result_table.rows.empty?
|
|
39
|
+
|
|
40
|
+
yield first
|
|
41
|
+
|
|
42
|
+
fetched = first.num_rows || first.result_table.rows.size
|
|
43
|
+
total = first.num_rows_result_set || 0
|
|
44
|
+
|
|
45
|
+
while fetched < total
|
|
46
|
+
page = fetch_page(fetched)
|
|
47
|
+
rows = page.result_table&.rows || []
|
|
48
|
+
break if rows.empty?
|
|
49
|
+
|
|
50
|
+
yield page
|
|
51
|
+
|
|
52
|
+
fetched += rows.size
|
|
53
|
+
break if rows.size < @page_size
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
delete
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Yields each row Array across all pages. Returns an Enumerator without a block.
|
|
60
|
+
# Aliased as #each so Enumerable methods (.map, .select, .to_a, etc.) work.
|
|
61
|
+
def each(&block)
|
|
62
|
+
return enum_for(:each) unless block_given?
|
|
63
|
+
|
|
64
|
+
each_page do |response|
|
|
65
|
+
response.result_table.rows.each(&block)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
alias each_row each
|
|
70
|
+
|
|
71
|
+
# Delete the cursor from the broker early (also called automatically after exhaustion).
|
|
72
|
+
def delete
|
|
73
|
+
return unless @request_id && @cursor_base
|
|
74
|
+
|
|
75
|
+
url = "#{@cursor_base}/responseStore/#{@request_id}"
|
|
76
|
+
@http_client.delete(url, headers: json_headers)
|
|
77
|
+
@request_id = nil
|
|
78
|
+
rescue StandardError
|
|
79
|
+
# best-effort; cursor will expire naturally
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
private
|
|
83
|
+
|
|
84
|
+
def submit_cursor
|
|
85
|
+
base = broker_base(@broker_address)
|
|
86
|
+
url = "#{base}/query/sql?getCursor=true&numRows=#{@page_size}"
|
|
87
|
+
body = JSON.generate("sql" => @query)
|
|
88
|
+
resp = @http_client.post(url, body: body, headers: json_headers)
|
|
89
|
+
|
|
90
|
+
raise TransportError, "cursor submit returned HTTP #{resp.code}" unless resp.code.to_i == 200
|
|
91
|
+
|
|
92
|
+
parsed = BrokerResponse.from_json(resp.body)
|
|
93
|
+
|
|
94
|
+
@request_id = parsed.request_id
|
|
95
|
+
@cursor_base = broker_base_from_response(parsed) || base
|
|
96
|
+
|
|
97
|
+
parsed
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def fetch_page(offset)
|
|
101
|
+
url = "#{@cursor_base}/responseStore/#{@request_id}/results?offset=#{offset}&numRows=#{@page_size}"
|
|
102
|
+
resp = @http_client.get(url, headers: json_headers)
|
|
103
|
+
|
|
104
|
+
raise TransportError, "cursor fetch returned HTTP #{resp.code}" unless resp.code.to_i == 200
|
|
105
|
+
|
|
106
|
+
BrokerResponse.from_json(resp.body)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def broker_base(address)
|
|
110
|
+
return address if address.start_with?("http://", "https://")
|
|
111
|
+
"http://#{address}"
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def broker_base_from_response(resp)
|
|
115
|
+
return nil unless resp.broker_host && resp.broker_port
|
|
116
|
+
"http://#{resp.broker_host}:#{resp.broker_port}"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def json_headers
|
|
120
|
+
{ "Content-Type" => "application/json; charset=utf-8" }.merge(@extra_headers)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
data/lib/pinot/response.rb
CHANGED
|
@@ -188,7 +188,11 @@ module Pinot
|
|
|
188
188
|
:num_docs_scanned, :num_entries_scanned_in_filter,
|
|
189
189
|
:num_entries_scanned_post_filter, :total_docs,
|
|
190
190
|
:time_used_ms, :min_consuming_freshness_time_ms,
|
|
191
|
-
:num_groups_limit_reached
|
|
191
|
+
:num_groups_limit_reached,
|
|
192
|
+
# cursor fields — only present when getCursor=true
|
|
193
|
+
:request_id, :num_rows_result_set, :offset, :num_rows,
|
|
194
|
+
:broker_host, :broker_port,
|
|
195
|
+
:submission_time_ms, :expiration_time_ms
|
|
192
196
|
|
|
193
197
|
def self.from_json(json_str)
|
|
194
198
|
hash = JSON.parse(json_str)
|
|
@@ -215,6 +219,19 @@ module Pinot
|
|
|
215
219
|
@total_docs = hash["totalDocs"] || 0
|
|
216
220
|
@time_used_ms = hash["timeUsedMs"] || 0
|
|
217
221
|
@min_consuming_freshness_time_ms = hash["minConsumingFreshnessTimeMs"] || 0
|
|
222
|
+
|
|
223
|
+
@request_id = hash["requestId"]
|
|
224
|
+
@num_rows_result_set = hash["numRowsResultSet"]
|
|
225
|
+
@offset = hash["offset"]
|
|
226
|
+
@num_rows = hash["numRows"]
|
|
227
|
+
@broker_host = hash["brokerHost"]
|
|
228
|
+
@broker_port = hash["brokerPort"]
|
|
229
|
+
@submission_time_ms = hash["submissionTimeMs"]
|
|
230
|
+
@expiration_time_ms = hash["expirationTimeMs"]
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def cursor?
|
|
234
|
+
!@request_id.nil?
|
|
218
235
|
end
|
|
219
236
|
end
|
|
220
237
|
end
|
data/lib/pinot/transport.rb
CHANGED
|
@@ -40,6 +40,15 @@ module Pinot
|
|
|
40
40
|
end
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
+
def delete(url, headers: {})
|
|
44
|
+
uri = URI.parse(url)
|
|
45
|
+
with_connection(url) do |http|
|
|
46
|
+
req = Net::HTTP::Delete.new(uri.request_uri)
|
|
47
|
+
headers.each { |k, v| req[k] = v }
|
|
48
|
+
http.request(req)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
43
52
|
def close
|
|
44
53
|
@reaper.kill rescue nil
|
|
45
54
|
@pool_mutex.synchronize do
|
|
@@ -183,6 +192,8 @@ module Pinot
|
|
|
183
192
|
# 250 = ExecutionTimeoutError (server-side), 400 = BrokerTimeoutError.
|
|
184
193
|
TIMEOUT_ERROR_CODES = [250, 400].freeze
|
|
185
194
|
|
|
195
|
+
attr_reader :http_client
|
|
196
|
+
|
|
186
197
|
def initialize(http_client:, extra_headers: {}, timeout_ms: nil, logger: nil,
|
|
187
198
|
max_retries: 0, retry_interval_ms: 200)
|
|
188
199
|
@http_client = http_client
|
data/lib/pinot/version.rb
CHANGED
data/lib/pinot.rb
CHANGED
|
@@ -20,6 +20,7 @@ require_relative "pinot/controller_based_broker_selector"
|
|
|
20
20
|
require_relative "pinot/transport"
|
|
21
21
|
require_relative "pinot/circuit_breaker"
|
|
22
22
|
require_relative "pinot/query_result"
|
|
23
|
+
require_relative "pinot/paginator"
|
|
23
24
|
require_relative "pinot/connection"
|
|
24
25
|
require_relative "pinot/prepared_statement"
|
|
25
26
|
require_relative "pinot/connection_factory"
|
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.26.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Xiang Fu
|
|
@@ -115,6 +115,7 @@ files:
|
|
|
115
115
|
- lib/pinot/grpc_transport.rb
|
|
116
116
|
- lib/pinot/instrumentation.rb
|
|
117
117
|
- lib/pinot/logger.rb
|
|
118
|
+
- lib/pinot/paginator.rb
|
|
118
119
|
- lib/pinot/prepared_statement.rb
|
|
119
120
|
- lib/pinot/proto/broker_service.proto
|
|
120
121
|
- lib/pinot/proto/broker_service_pb.rb
|