olaf 1.0.0 → 1.0.1
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 +15 -2
- data/lib/olaf/drivers/big_query.rb +52 -10
- data/olaf.gemspec +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dd476121e33e97e071c41dc3e75a8128eddee118c93f0ec93acd61fa167b0523
|
|
4
|
+
data.tar.gz: 843da2abba59ade1dc644b47a4575abf77102ed7a58dd0ce1c45e6f7ba6f7968
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 577d617e9e02f3bbaa858425e70e6d87803af2a9d2764626d30e6d0fda0bce130171b33566e642354e30990ffe19d7e9b4b8c55f48f29d63712a1e81743eb31c
|
|
7
|
+
data.tar.gz: 39da006e922a15ad3fa88a7c7c45979aff8eeb8e13b6e4c5340ee52608f54cf4930b4314e4925c2103ed7559604f664fbb18f2dd8084903605075e1d9f908293
|
data/README.md
CHANGED
|
@@ -88,7 +88,8 @@ Olaf.instance.register_result(FetchUsers, [{ id: 42 }], with: { department_id: 1
|
|
|
88
88
|
```
|
|
89
89
|
|
|
90
90
|
Whichever driver runs the query, a failure it reports is wrapped into
|
|
91
|
-
`Olaf::QueryExecutionError`, carrying the query metadata.
|
|
91
|
+
`Olaf::QueryExecutionError`, carrying the query metadata. A BigQuery connection
|
|
92
|
+
that keeps dropping can be the exception: see below.
|
|
92
93
|
|
|
93
94
|
## BigQuery
|
|
94
95
|
|
|
@@ -97,7 +98,8 @@ Olaf::BigQuery.new(
|
|
|
97
98
|
project: 'carwow',
|
|
98
99
|
credentials: JSON.parse(ENV['BIGQUERY_CREDENTIALS']), # ambient Google credentials when omitted
|
|
99
100
|
maximum_bytes_billed: 5 * Olaf::BigQuery::GIGABYTE, # default: 1 GiB
|
|
100
|
-
labels: { service: 'flatmin', country: 'uk' }
|
|
101
|
+
labels: { service: 'flatmin', country: 'uk' },
|
|
102
|
+
retry_delays: [1, 2] # default: retry twice, after 1s then 2s
|
|
101
103
|
)
|
|
102
104
|
```
|
|
103
105
|
|
|
@@ -114,6 +116,17 @@ The query runs as a job, so that it can be capped and labelled:
|
|
|
114
116
|
```ruby
|
|
115
117
|
driver :big_query, maximum_bytes_billed: 10 * Olaf::BigQuery::GIGABYTE
|
|
116
118
|
```
|
|
119
|
+
* A call whose connection drops mid-request is retried once after each of the
|
|
120
|
+
`retry_delays`. Waiting for the job is retried on its own, so a dropped status
|
|
121
|
+
check doesn't start a second job. A drop arrives in one of two forms, and both
|
|
122
|
+
are retried:
|
|
123
|
+
* A bare `OpenSSL::SSL::SSLError`, which is how google-apis-core 0.x reports
|
|
124
|
+
an SSL read that ends early. Once the retries run out it is raised as it is,
|
|
125
|
+
not wrapped, so a job runner such as Sidekiq can retry it in turn.
|
|
126
|
+
* A `Google::Cloud::Error` caused by a `Google::Apis::TransmissionError`.
|
|
127
|
+
google-apis-core 1.x reports every dropped connection this way, and 0.x
|
|
128
|
+
does the same for a reset connection. Once the retries run out it is wrapped
|
|
129
|
+
into `Olaf::QueryExecutionError`, like any other error the API reports.
|
|
117
130
|
|
|
118
131
|
`Olaf.execute` returns the rows. `Olaf.instance(:big_query).run(query)` returns
|
|
119
132
|
the finished job instead, for its statistics:
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# Not a dependency of the gem: consumers of this driver add it themselves.
|
|
2
2
|
require 'google/cloud/bigquery'
|
|
3
|
+
require 'securerandom'
|
|
3
4
|
|
|
4
5
|
# The API client debug-logs whole response bodies, which for a warehouse query
|
|
5
6
|
# is the entire result set.
|
|
@@ -17,14 +18,17 @@ module Olaf
|
|
|
17
18
|
LABEL_FORMAT = /[^a-z0-9_-]/.freeze
|
|
18
19
|
LABEL_MAX_LENGTH = 63
|
|
19
20
|
|
|
21
|
+
RETRY_DELAYS = [1, 2].freeze
|
|
22
|
+
|
|
20
23
|
# Credentials default to the ambient Google ones, and the client is built
|
|
21
24
|
# from the arguments unless one is given.
|
|
22
25
|
def initialize(project: nil, credentials: nil, maximum_bytes_billed: DEFAULT_MAXIMUM_BYTES_BILLED,
|
|
23
|
-
labels: {}, client: nil)
|
|
26
|
+
labels: {}, retry_delays: RETRY_DELAYS, client: nil)
|
|
24
27
|
@project = project
|
|
25
28
|
@credentials = credentials
|
|
26
29
|
@maximum_bytes_billed = maximum_bytes_billed
|
|
27
30
|
@labels = labels
|
|
31
|
+
@retry_delays = retry_delays
|
|
28
32
|
@client = client
|
|
29
33
|
end
|
|
30
34
|
|
|
@@ -35,15 +39,11 @@ module Olaf
|
|
|
35
39
|
# Returns the finished job, so callers can read its stats (bytes billed,
|
|
36
40
|
# cache hit) on top of its rows.
|
|
37
41
|
#
|
|
38
|
-
# @raises Olaf::QueryExecutionError
|
|
42
|
+
# @raises Olaf::QueryExecutionError, or the OpenSSL::SSL::SSLError of a
|
|
43
|
+
# connection that keeps dropping, which google-apis-core 0.x doesn't wrap
|
|
39
44
|
def run(olaf_query)
|
|
40
|
-
job =
|
|
41
|
-
|
|
42
|
-
params: params(olaf_query),
|
|
43
|
-
maximum_bytes_billed: maximum_bytes_billed(olaf_query),
|
|
44
|
-
labels: labels(olaf_query)
|
|
45
|
-
)
|
|
46
|
-
job.wait_until_done!
|
|
45
|
+
job = start_job(olaf_query)
|
|
46
|
+
retrying_dropped_connections { job.wait_until_done! }
|
|
47
47
|
|
|
48
48
|
raise QueryExecutionError.new(job.error.to_h['message'].to_s, olaf_query) if job.failed?
|
|
49
49
|
|
|
@@ -58,9 +58,51 @@ module Olaf
|
|
|
58
58
|
@client ||= Google::Cloud::Bigquery.new(project: @project, credentials: @credentials)
|
|
59
59
|
end
|
|
60
60
|
|
|
61
|
+
# A connection can drop after BigQuery has started the job, so every attempt
|
|
62
|
+
# reuses one job ID: BigQuery refuses the retry as a duplicate, and the job
|
|
63
|
+
# already started is loaded rather than running and billing the query twice.
|
|
64
|
+
# If the lookup can't find the job, as happens for one in a single region,
|
|
65
|
+
# the refusal is re-raised.
|
|
66
|
+
def start_job(olaf_query)
|
|
67
|
+
job_id = "job_#{SecureRandom.urlsafe_base64(21)}"
|
|
68
|
+
|
|
69
|
+
retrying_dropped_connections do
|
|
70
|
+
client.query_job(
|
|
71
|
+
sql(olaf_query),
|
|
72
|
+
params: params(olaf_query),
|
|
73
|
+
maximum_bytes_billed: maximum_bytes_billed(olaf_query),
|
|
74
|
+
labels: labels(olaf_query),
|
|
75
|
+
job_id:
|
|
76
|
+
)
|
|
77
|
+
rescue Google::Cloud::AlreadyExistsError
|
|
78
|
+
client.job(job_id) || raise
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# google-cloud-bigquery switches off google-apis-core's retries.
|
|
83
|
+
def retrying_dropped_connections
|
|
84
|
+
@retry_delays.each do |delay|
|
|
85
|
+
return yield
|
|
86
|
+
rescue StandardError => error
|
|
87
|
+
raise unless dropped_connection?(error)
|
|
88
|
+
|
|
89
|
+
sleep delay
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
yield
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# google-apis-core 1.x reports every dropped connection as a TransmissionError,
|
|
96
|
+
# and 0.x does the same for a reset connection; google-cloud-bigquery wraps it.
|
|
97
|
+
def dropped_connection?(error)
|
|
98
|
+
error.is_a?(OpenSSL::SSL::SSLError) ||
|
|
99
|
+
(error.is_a?(Google::Cloud::Error) && error.cause.is_a?(Google::Apis::TransmissionError))
|
|
100
|
+
end
|
|
101
|
+
|
|
61
102
|
# Google::Cloud::Error covers everything the API reports, Google::Auth::Error
|
|
62
103
|
# credentials missing or expired before a call is made. The second one only
|
|
63
|
-
# exists in googleauth 1.14 and later.
|
|
104
|
+
# exists in googleauth 1.14 and later. A bare OpenSSL::SSL::SSLError is left
|
|
105
|
+
# out, so once its retries run out the caller's job runner can still retry it.
|
|
64
106
|
def wrapped_errors
|
|
65
107
|
@wrapped_errors ||= [
|
|
66
108
|
Google::Cloud::Error,
|
data/olaf.gemspec
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: olaf
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Emiliano Mancuso
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-10-
|
|
10
|
+
date: 2026-10-20 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: google-cloud-bigquery
|
|
@@ -75,7 +75,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
75
75
|
- !ruby/object:Gem::Version
|
|
76
76
|
version: '0'
|
|
77
77
|
requirements: []
|
|
78
|
-
rubygems_version:
|
|
78
|
+
rubygems_version: 3.6.9
|
|
79
79
|
specification_version: 4
|
|
80
80
|
summary: Ruby wrapper for warehouse queries.
|
|
81
81
|
test_files:
|