google-gax 0.4.0 → 0.4.2
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/google/gax/api_callable.rb +7 -5
- data/lib/google/gax/bundling.rb +445 -0
- data/lib/google/gax/settings.rb +1 -1
- data/lib/google/gax/version.rb +1 -1
- data/spec/google/gax/api_callable_spec.rb +76 -29
- data/spec/google/gax/bundling_spec.rb +627 -0
- data/spec/google/gax/settings_spec.rb +2 -3
- metadata +7 -5
data/lib/google/gax/settings.rb
CHANGED
data/lib/google/gax/version.rb
CHANGED
@@ -48,19 +48,19 @@ describe Google::Gax do
|
|
48
48
|
describe 'create_api_call' do
|
49
49
|
it 'calls api call' do
|
50
50
|
settings = CallSettings.new
|
51
|
-
|
52
|
-
func = proc do |
|
53
|
-
|
51
|
+
deadline_arg = nil
|
52
|
+
func = proc do |deadline: nil, **_kwargs|
|
53
|
+
deadline_arg = deadline
|
54
54
|
42
|
55
55
|
end
|
56
56
|
my_callable = Google::Gax.create_api_call(func, settings)
|
57
57
|
expect(my_callable.call(nil)).to eq(42)
|
58
|
-
expect(
|
58
|
+
expect(deadline_arg).to be_a(Time)
|
59
59
|
|
60
|
-
|
61
|
-
options = Google::Gax::CallOptions.new(timeout:
|
60
|
+
new_deadline = Time.now + 20
|
61
|
+
options = Google::Gax::CallOptions.new(timeout: 20)
|
62
62
|
expect(my_callable.call(nil, options)).to eq(42)
|
63
|
-
expect(
|
63
|
+
expect(deadline_arg).to be_within(0.9).of(new_deadline)
|
64
64
|
end
|
65
65
|
end
|
66
66
|
|
@@ -71,9 +71,9 @@ describe Google::Gax do
|
|
71
71
|
page_descriptor = Google::Gax::PageDescriptor.new('page_token',
|
72
72
|
'next_page_token', 'nums')
|
73
73
|
settings = CallSettings.new(page_descriptor: page_descriptor)
|
74
|
-
|
75
|
-
func = proc do |request,
|
76
|
-
|
74
|
+
deadline_arg = nil
|
75
|
+
func = proc do |request, deadline: nil, **_kwargs|
|
76
|
+
deadline_arg = deadline
|
77
77
|
page_token = request['page_token']
|
78
78
|
if page_token > 0 && page_token < page_size * pages_to_stream
|
79
79
|
{ 'nums' => (page_token...(page_token + page_size)),
|
@@ -90,7 +90,7 @@ describe Google::Gax do
|
|
90
90
|
expect(my_callable.call('page_token' => 0).to_a).to match_array(
|
91
91
|
(0...(page_size * pages_to_stream))
|
92
92
|
)
|
93
|
-
expect(
|
93
|
+
expect(deadline_arg).to be_a(Time)
|
94
94
|
end
|
95
95
|
|
96
96
|
it 'offers interface for pages' do
|
@@ -118,10 +118,10 @@ describe Google::Gax do
|
|
118
118
|
describe 'failures without retry' do
|
119
119
|
it 'simply fails' do
|
120
120
|
settings = CallSettings.new(errors: [GRPC::Cancelled])
|
121
|
-
|
121
|
+
deadline_arg = nil
|
122
122
|
call_count = 0
|
123
|
-
func = proc do |
|
124
|
-
|
123
|
+
func = proc do |deadline: nil, **_kwargs|
|
124
|
+
deadline_arg = deadline
|
125
125
|
call_count += 1
|
126
126
|
raise GRPC::Cancelled, ''
|
127
127
|
end
|
@@ -132,26 +132,66 @@ describe Google::Gax do
|
|
132
132
|
rescue Google::Gax::GaxError => exc
|
133
133
|
expect(exc.cause).to be_a(GRPC::Cancelled)
|
134
134
|
end
|
135
|
-
expect(
|
135
|
+
expect(deadline_arg).to be_a(Time)
|
136
136
|
expect(call_count).to eq(1)
|
137
137
|
end
|
138
138
|
|
139
139
|
it 'does not wrap unknown errors' do
|
140
140
|
settings = CallSettings.new
|
141
|
-
|
141
|
+
deadline_arg = nil
|
142
142
|
call_count = 0
|
143
|
-
func = proc do |
|
144
|
-
|
143
|
+
func = proc do |deadline: nil, **_kwargs|
|
144
|
+
deadline_arg = deadline
|
145
145
|
call_count += 1
|
146
146
|
raise CustomException.new('', FAKE_STATUS_CODE_1)
|
147
147
|
end
|
148
148
|
my_callable = Google::Gax.create_api_call(func, settings)
|
149
149
|
expect { my_callable.call }.to raise_error(CustomException)
|
150
|
-
expect(
|
150
|
+
expect(deadline_arg).to be_a(Time)
|
151
151
|
expect(call_count).to eq(1)
|
152
152
|
end
|
153
153
|
end
|
154
154
|
|
155
|
+
describe 'bundleable' do
|
156
|
+
it 'bundling page streaming error' do
|
157
|
+
settings = CallSettings.new(
|
158
|
+
page_descriptor: Object.new,
|
159
|
+
bundle_descriptor: Object.new,
|
160
|
+
bundler: Object.new
|
161
|
+
)
|
162
|
+
expect do
|
163
|
+
Google::Gax.create_api_call(proc {}, settings)
|
164
|
+
end.to raise_error(RuntimeError)
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'bundles the API call' do
|
168
|
+
BundleOptions = Google::Gax::BundleOptions
|
169
|
+
BundleDescriptor = Google::Gax::BundleDescriptor
|
170
|
+
|
171
|
+
bundler = Google::Gax::Executor.new(
|
172
|
+
BundleOptions.new(element_count_threshold: 8)
|
173
|
+
)
|
174
|
+
fake_descriptor = BundleDescriptor.new('elements', [])
|
175
|
+
settings = CallSettings.new(
|
176
|
+
bundler: bundler,
|
177
|
+
bundle_descriptor: fake_descriptor,
|
178
|
+
timeout: 0
|
179
|
+
)
|
180
|
+
|
181
|
+
func = proc do |request, _|
|
182
|
+
request['elements'].count
|
183
|
+
end
|
184
|
+
|
185
|
+
callable = Google::Gax.create_api_call(func, settings)
|
186
|
+
|
187
|
+
first = callable.call('elements' => [0] * 5)
|
188
|
+
expect(first).to be_an_instance_of Google::Gax::Event
|
189
|
+
expect(first.result).to be_nil
|
190
|
+
second = callable.call('elements' => [0] * 3)
|
191
|
+
expect(second.result).to be 8
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
155
195
|
describe 'retryable' do
|
156
196
|
RetryOptions = Google::Gax::RetryOptions
|
157
197
|
BackoffSettings = Google::Gax::BackoffSettings
|
@@ -166,9 +206,9 @@ describe Google::Gax do
|
|
166
206
|
|
167
207
|
to_attempt = 3
|
168
208
|
|
169
|
-
|
170
|
-
func = proc do |
|
171
|
-
|
209
|
+
deadline_arg = nil
|
210
|
+
func = proc do |deadline: nil, **_kwargs|
|
211
|
+
deadline_arg = deadline
|
172
212
|
to_attempt -= 1
|
173
213
|
raise CustomException.new('', FAKE_STATUS_CODE_1) if to_attempt > 0
|
174
214
|
1729
|
@@ -176,7 +216,7 @@ describe Google::Gax do
|
|
176
216
|
my_callable = Google::Gax.create_api_call(func, settings)
|
177
217
|
expect(my_callable.call).to eq(1729)
|
178
218
|
expect(to_attempt).to eq(0)
|
179
|
-
expect(
|
219
|
+
expect(deadline_arg).to be_a(Time)
|
180
220
|
end
|
181
221
|
|
182
222
|
it 'doesn\'t retry if no codes' do
|
@@ -211,11 +251,17 @@ describe Google::Gax do
|
|
211
251
|
call_count = 0
|
212
252
|
|
213
253
|
time_now = Time.now
|
214
|
-
|
215
|
-
|
254
|
+
# Time.now will be called twice for each API call (one in set_timeout_arg
|
255
|
+
# and the other in retryable). It returns time_now for to_attempt * 2
|
256
|
+
# times (which allows retrying), and then finally returns time_now + 2
|
257
|
+
# to exceed the deadline.
|
258
|
+
allow(Time).to receive(:now).exactly(to_attempt * 2 + 1).times.and_return(
|
259
|
+
*([time_now] * to_attempt * 2 + [time_now + 2])
|
216
260
|
)
|
217
261
|
|
218
|
-
|
262
|
+
deadline_arg = nil
|
263
|
+
func = proc do |deadline: nil, **_kwargs|
|
264
|
+
deadline_arg = deadline
|
219
265
|
call_count += 1
|
220
266
|
raise CustomException.new('', FAKE_STATUS_CODE_1)
|
221
267
|
end
|
@@ -227,6 +273,7 @@ describe Google::Gax do
|
|
227
273
|
rescue Google::Gax::RetryError => exc
|
228
274
|
expect(exc.cause).to be_a(CustomException)
|
229
275
|
end
|
276
|
+
expect(deadline_arg).to eq(time_now)
|
230
277
|
expect(call_count).to eq(to_attempt)
|
231
278
|
end
|
232
279
|
|
@@ -252,10 +299,10 @@ describe Google::Gax do
|
|
252
299
|
start_time = time_now
|
253
300
|
incr_time = proc { |secs| time_now += secs }
|
254
301
|
call_count = 0
|
255
|
-
func = proc do |_,
|
302
|
+
func = proc do |_, deadline: nil, **_kwargs|
|
256
303
|
call_count += 1
|
257
|
-
incr_time.call(
|
258
|
-
raise CustomException.new(
|
304
|
+
incr_time.call(deadline - time_now)
|
305
|
+
raise CustomException.new(deadline.to_s, FAKE_STATUS_CODE_1)
|
259
306
|
end
|
260
307
|
|
261
308
|
allow(Time).to receive(:now) { time_now }
|
@@ -0,0 +1,627 @@
|
|
1
|
+
# Copyright 2016, Google Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions are
|
6
|
+
# met:
|
7
|
+
#
|
8
|
+
# * Redistributions of source code must retain the above copyright
|
9
|
+
# notice, this list of conditions and the following disclaimer.
|
10
|
+
# * Redistributions in binary form must reproduce the above
|
11
|
+
# copyright notice, this list of conditions and the following disclaimer
|
12
|
+
# in the documentation and/or other materials provided with the
|
13
|
+
# distribution.
|
14
|
+
# * Neither the name of Google Inc. nor the names of its
|
15
|
+
# contributors may be used to endorse or promote products derived from
|
16
|
+
# this software without specific prior written permission.
|
17
|
+
#
|
18
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
19
|
+
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
20
|
+
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
21
|
+
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
22
|
+
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
23
|
+
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
24
|
+
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
25
|
+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
26
|
+
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
27
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
28
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
29
|
+
|
30
|
+
require 'google/gax/bundling'
|
31
|
+
require 'google/gax'
|
32
|
+
require 'google/protobuf'
|
33
|
+
|
34
|
+
describe Google::Gax do
|
35
|
+
Pool = Google::Protobuf::DescriptorPool.new
|
36
|
+
Pool.build do
|
37
|
+
add_message 'google.protobuf.Simple' do
|
38
|
+
optional :field1, :string, 1
|
39
|
+
optional :field2, :string, 2
|
40
|
+
end
|
41
|
+
add_message 'google.protobuf.Outer' do
|
42
|
+
optional :inner, :message, 1, 'google.protobuf.Simple'
|
43
|
+
optional :field1, :string, 2
|
44
|
+
end
|
45
|
+
add_message 'google.protobuf.Bundled' do
|
46
|
+
repeated :field1, :string, 1
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
Simple = Pool.lookup('google.protobuf.Simple').msgclass
|
51
|
+
Outer = Pool.lookup('google.protobuf.Outer').msgclass
|
52
|
+
Bundled = Pool.lookup('google.protobuf.Bundled').msgclass
|
53
|
+
|
54
|
+
def simple_builder(value, other_value: nil)
|
55
|
+
return Simple.new(field1: value) if other_value.nil?
|
56
|
+
Simple.new(field1: value, field2: other_value)
|
57
|
+
end
|
58
|
+
|
59
|
+
def outer_builder(value)
|
60
|
+
Outer.new(inner: simple_builder(value, other_value: value), field1: value)
|
61
|
+
end
|
62
|
+
|
63
|
+
def bundled_builder(value)
|
64
|
+
Bundled.new(field1: value)
|
65
|
+
end
|
66
|
+
|
67
|
+
describe 'method `compute_bundle_id`' do
|
68
|
+
context 'should work for' do
|
69
|
+
it 'single field value' do
|
70
|
+
actual =
|
71
|
+
Google::Gax.compute_bundle_id(
|
72
|
+
simple_builder('dummy_value'), ['field1']
|
73
|
+
)
|
74
|
+
expect(actual).to eq(['dummy_value'])
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'composite value with nil' do
|
78
|
+
actual =
|
79
|
+
Google::Gax.compute_bundle_id(
|
80
|
+
simple_builder('dummy_value'), %w(field1 field2)
|
81
|
+
)
|
82
|
+
expect(actual).to eq(['dummy_value', ''])
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'composite value' do
|
86
|
+
actual =
|
87
|
+
Google::Gax.compute_bundle_id(
|
88
|
+
simple_builder('dummy_value', other_value: 'other_value'),
|
89
|
+
%w(field1 field2)
|
90
|
+
)
|
91
|
+
expect(actual).to eq(%w(dummy_value other_value))
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'simple dotted value' do
|
95
|
+
actual =
|
96
|
+
Google::Gax.compute_bundle_id(
|
97
|
+
outer_builder('dummy_value'),
|
98
|
+
%w(inner.field1)
|
99
|
+
)
|
100
|
+
expect(actual).to eq(['dummy_value'])
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'complex case' do
|
104
|
+
actual =
|
105
|
+
Google::Gax.compute_bundle_id(
|
106
|
+
outer_builder('dummy_value'),
|
107
|
+
%w(inner.field1 inner.field2 field1)
|
108
|
+
)
|
109
|
+
expect(actual).to eq(%w(dummy_value dummy_value dummy_value))
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'should return false for a single missing field value' do
|
113
|
+
expect(
|
114
|
+
Google::Gax.compute_bundle_id(
|
115
|
+
simple_builder('dummy_value'),
|
116
|
+
['field3']
|
117
|
+
)
|
118
|
+
).to eq([nil])
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'should return false for a composite value' do
|
122
|
+
expect(
|
123
|
+
Google::Gax.compute_bundle_id(
|
124
|
+
simple_builder('dummy_value'),
|
125
|
+
%w(field1 field3)
|
126
|
+
)
|
127
|
+
).to eq(['dummy_value', nil])
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'should return false for a simple dotted value' do
|
131
|
+
expect(
|
132
|
+
Google::Gax.compute_bundle_id(
|
133
|
+
outer_builder('dotted this'),
|
134
|
+
['inner.field3']
|
135
|
+
)
|
136
|
+
).to eq([nil])
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# A dummy api call that simply returns the request.
|
142
|
+
def return_request
|
143
|
+
proc do |req|
|
144
|
+
req
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def create_a_test_task(api_call: return_request)
|
149
|
+
Google::Gax::Task.new(
|
150
|
+
api_call,
|
151
|
+
'an_id',
|
152
|
+
'field1',
|
153
|
+
bundled_builder([]),
|
154
|
+
{}
|
155
|
+
)
|
156
|
+
end
|
157
|
+
|
158
|
+
# A dummy api call that raises an exception
|
159
|
+
def raise_exc
|
160
|
+
proc do |_|
|
161
|
+
raise Google::Gax::GaxError, 'Raised in a test'
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe Google::Gax::Task do
|
166
|
+
test_message = 'a simple msg'.freeze
|
167
|
+
context 'increase in element count' do
|
168
|
+
it 'no messages added' do
|
169
|
+
test_task = create_a_test_task
|
170
|
+
actual = test_task.element_count
|
171
|
+
expect(actual).to eq(0)
|
172
|
+
end
|
173
|
+
|
174
|
+
it '1 message added' do
|
175
|
+
test_task = create_a_test_task
|
176
|
+
test_task.extend([test_message])
|
177
|
+
actual = test_task.element_count
|
178
|
+
expect(actual).to eq(1)
|
179
|
+
end
|
180
|
+
|
181
|
+
it '5 messages added' do
|
182
|
+
test_task = create_a_test_task
|
183
|
+
test_task.extend([test_message] * 5)
|
184
|
+
actual = test_task.element_count
|
185
|
+
expect(actual).to eq(5)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
context 'increase in request byte count' do
|
190
|
+
it 'no messages added' do
|
191
|
+
test_task = create_a_test_task
|
192
|
+
actual = test_task.request_bytesize
|
193
|
+
expect(actual).to eq(0)
|
194
|
+
end
|
195
|
+
|
196
|
+
it '1 message added' do
|
197
|
+
test_task = create_a_test_task
|
198
|
+
test_task.extend([test_message])
|
199
|
+
actual = test_task.request_bytesize
|
200
|
+
expect(actual).to eq(test_message.bytesize)
|
201
|
+
end
|
202
|
+
|
203
|
+
it '5 messages added' do
|
204
|
+
test_task = create_a_test_task
|
205
|
+
test_task.extend([test_message] * 5)
|
206
|
+
actual = test_task.request_bytesize
|
207
|
+
expect(actual).to eq(5 * test_message.bytesize)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
context 'sends bundle elements' do
|
212
|
+
it 'no messages added' do
|
213
|
+
test_task = create_a_test_task
|
214
|
+
expect(test_task.element_count).to eq(0)
|
215
|
+
test_task.run
|
216
|
+
expect(test_task.element_count).to eq(0)
|
217
|
+
expect(test_task.request_bytesize).to eq(0)
|
218
|
+
end
|
219
|
+
|
220
|
+
it '1 message added' do
|
221
|
+
test_task = create_a_test_task
|
222
|
+
event = test_task.extend([test_message])
|
223
|
+
expect(test_task.element_count).to eq(1)
|
224
|
+
test_task.run
|
225
|
+
expect(test_task.element_count).to eq(0)
|
226
|
+
expect(test_task.request_bytesize).to eq(0)
|
227
|
+
expect(event).to_not be_nil
|
228
|
+
expect(event.result).to eq(bundled_builder([test_message]))
|
229
|
+
end
|
230
|
+
|
231
|
+
it '5 messages added' do
|
232
|
+
test_task = create_a_test_task
|
233
|
+
event = test_task.extend([test_message] * 5)
|
234
|
+
test_task.run
|
235
|
+
expect(test_task.element_count).to eq(0)
|
236
|
+
expect(test_task.request_bytesize).to eq(0)
|
237
|
+
expect(event).to_not be_nil
|
238
|
+
expect(event.result).to eq(bundled_builder([test_message] * 5))
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
context 'api call execution fails' do
|
243
|
+
it 'adds an error if execution fails' do
|
244
|
+
test_task = create_a_test_task(api_call: raise_exc)
|
245
|
+
event = test_task.extend([test_message])
|
246
|
+
expect(test_task.element_count).to eq(1)
|
247
|
+
test_task.run
|
248
|
+
expect(test_task.element_count).to eq(0)
|
249
|
+
expect(test_task.request_bytesize).to eq(0)
|
250
|
+
expect(event.result).to be_a(Google::Gax::GaxError)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
context 'calling the canceller' do
|
255
|
+
it 'stops the element from getting sent' do
|
256
|
+
another_message = 'another msg'
|
257
|
+
test_task = create_a_test_task
|
258
|
+
an_event = test_task.extend([test_message])
|
259
|
+
another_event = test_task.extend([another_message])
|
260
|
+
expect(test_task.element_count).to eq(2)
|
261
|
+
expect(an_event.cancel).to eq(true)
|
262
|
+
expect(test_task.element_count).to eq(1)
|
263
|
+
expect(an_event.cancel).to eq(false)
|
264
|
+
expect(test_task.element_count).to eq(1)
|
265
|
+
test_task.run
|
266
|
+
expect(test_task.element_count).to eq(0)
|
267
|
+
expect(another_event.result).to eq(bundled_builder([another_message]))
|
268
|
+
expect(an_event.set?).to eq(false)
|
269
|
+
expect(an_event.result).to be_nil
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
describe Google::Gax::Executor do
|
275
|
+
SIMPLE_DESCRIPTOR = Google::Gax::BundleDescriptor.new('field1', [])
|
276
|
+
DEMUX_DESCRIPTOR =
|
277
|
+
Google::Gax::BundleDescriptor.new(
|
278
|
+
'field1', [], subresponse_field: 'field1'
|
279
|
+
)
|
280
|
+
bundler = nil
|
281
|
+
|
282
|
+
after(:each) do
|
283
|
+
bundler.close if bundler.instance_of? Google::Gax::Executor
|
284
|
+
end
|
285
|
+
|
286
|
+
context 'grouped by bundle id' do
|
287
|
+
it 'should group the api_calls by bundle_id' do
|
288
|
+
an_elt = 'dummy_message'
|
289
|
+
api_call = return_request
|
290
|
+
bundle_ids = %w(id1 id2)
|
291
|
+
threshold = 5
|
292
|
+
options =
|
293
|
+
Google::Gax::BundleOptions.new(element_count_threshold: threshold)
|
294
|
+
bundler = Google::Gax::Executor.new(options)
|
295
|
+
bundle_ids.each do |id|
|
296
|
+
(threshold - 1).times do
|
297
|
+
event = bundler.schedule(
|
298
|
+
api_call,
|
299
|
+
id,
|
300
|
+
SIMPLE_DESCRIPTOR,
|
301
|
+
bundled_builder([an_elt])
|
302
|
+
)
|
303
|
+
expect(event.canceller).to_not be_nil
|
304
|
+
expect(event.set?).to eq(false)
|
305
|
+
expect(event.result).to be_nil
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
bundle_ids.each do |id|
|
310
|
+
event = bundler.schedule(
|
311
|
+
api_call,
|
312
|
+
id,
|
313
|
+
SIMPLE_DESCRIPTOR,
|
314
|
+
bundled_builder([an_elt])
|
315
|
+
)
|
316
|
+
expect(event.canceller).to_not be_nil
|
317
|
+
expect(event.set?).to eq(true)
|
318
|
+
expect(event.result).to eq(
|
319
|
+
bundled_builder([an_elt] * threshold)
|
320
|
+
)
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
context 'bundling with subresponses' do
|
326
|
+
it 'are demuxed correctly' do
|
327
|
+
an_elt = 'dummy_message'
|
328
|
+
api_call = return_request
|
329
|
+
bundle_id = 'an_id'
|
330
|
+
threshold = 5
|
331
|
+
options =
|
332
|
+
Google::Gax::BundleOptions.new(element_count_threshold: threshold)
|
333
|
+
bundler = Google::Gax::Executor.new(options)
|
334
|
+
events = []
|
335
|
+
|
336
|
+
# send 3 groups of elements of different sizes in the bundle
|
337
|
+
1.upto(3) do |i|
|
338
|
+
event = bundler.schedule(
|
339
|
+
api_call,
|
340
|
+
bundle_id,
|
341
|
+
DEMUX_DESCRIPTOR,
|
342
|
+
bundled_builder(["#{an_elt}#{i}"] * i)
|
343
|
+
)
|
344
|
+
events.push(event)
|
345
|
+
end
|
346
|
+
previous_event = nil
|
347
|
+
events.each_with_index do |current_event, i|
|
348
|
+
index = i + 1
|
349
|
+
expect(current_event).to_not eq(previous_event)
|
350
|
+
expect(current_event.set?).to eq(true)
|
351
|
+
expect(current_event.result).to eq(
|
352
|
+
bundled_builder(["#{an_elt}#{index}"] * index)
|
353
|
+
)
|
354
|
+
previous_event = current_event
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
it 'each have an exception when demuxed call fails' do
|
359
|
+
an_elt = 'dummy_message'
|
360
|
+
api_call = raise_exc
|
361
|
+
bundle_id = 'an_id'
|
362
|
+
threshold = 5
|
363
|
+
options =
|
364
|
+
Google::Gax::BundleOptions.new(element_count_threshold: threshold)
|
365
|
+
bundler = Google::Gax::Executor.new(options)
|
366
|
+
events = []
|
367
|
+
|
368
|
+
0.upto(threshold - 2) do |i|
|
369
|
+
event = bundler.schedule(
|
370
|
+
api_call,
|
371
|
+
bundle_id,
|
372
|
+
DEMUX_DESCRIPTOR,
|
373
|
+
bundled_builder(["#{an_elt}#{i}"])
|
374
|
+
)
|
375
|
+
expect(event.set?).to be(false)
|
376
|
+
expect(event.result).to be_nil
|
377
|
+
events.push(event)
|
378
|
+
end
|
379
|
+
last_event = bundler.schedule(
|
380
|
+
api_call,
|
381
|
+
bundle_id,
|
382
|
+
DEMUX_DESCRIPTOR,
|
383
|
+
bundled_builder(["#{an_elt}#{threshold - 1}"])
|
384
|
+
)
|
385
|
+
events.push(last_event)
|
386
|
+
|
387
|
+
previous_event = nil
|
388
|
+
events.each do |event|
|
389
|
+
expect(event).to_not eq(previous_event)
|
390
|
+
expect(event.set?).to eq(true)
|
391
|
+
expect(event.result).to be_a(Google::Gax::GaxError)
|
392
|
+
previous_event = event
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
it 'each event has same result from mismatched demuxed api call' do
|
397
|
+
an_elt = 'dummy_message'
|
398
|
+
mismatched_result = bundled_builder([an_elt, an_elt])
|
399
|
+
bundle_id = 'an_id'
|
400
|
+
threshold = 5
|
401
|
+
options =
|
402
|
+
Google::Gax::BundleOptions.new(element_count_threshold: threshold)
|
403
|
+
bundler = Google::Gax::Executor.new(options)
|
404
|
+
events = []
|
405
|
+
# send 3 groups of elements of different sizes in the bundle
|
406
|
+
1.upto(3) do |i|
|
407
|
+
event = bundler.schedule(
|
408
|
+
proc { |_| mismatched_result },
|
409
|
+
bundle_id,
|
410
|
+
DEMUX_DESCRIPTOR,
|
411
|
+
bundled_builder(["#{an_elt}#{i}"] * i)
|
412
|
+
)
|
413
|
+
events.push(event)
|
414
|
+
end
|
415
|
+
previous_event = nil
|
416
|
+
events.each do |event|
|
417
|
+
expect(event).to_not eq(previous_event)
|
418
|
+
expect(event.set?).to eq(true)
|
419
|
+
expect(event.result).to eq(
|
420
|
+
mismatched_result
|
421
|
+
)
|
422
|
+
previous_event = event
|
423
|
+
end
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
context 'bundles are triggered to run correctly' do
|
428
|
+
it 'api call not invoked until element threshold' do
|
429
|
+
an_elt = 'dummy_msg'
|
430
|
+
an_id = 'bundle_id'
|
431
|
+
api_call = return_request
|
432
|
+
threshold = 3
|
433
|
+
options =
|
434
|
+
Google::Gax::BundleOptions.new(element_count_threshold: threshold)
|
435
|
+
bundler = Google::Gax::Executor.new(options)
|
436
|
+
1.upto(3) do |i|
|
437
|
+
event = bundler.schedule(
|
438
|
+
api_call,
|
439
|
+
an_id,
|
440
|
+
SIMPLE_DESCRIPTOR,
|
441
|
+
bundled_builder([an_elt])
|
442
|
+
)
|
443
|
+
|
444
|
+
expect(event.canceller).to_not be_nil
|
445
|
+
if i < threshold
|
446
|
+
expect(event.set?).to eq(false)
|
447
|
+
expect(event.result).to be_nil
|
448
|
+
else
|
449
|
+
expect(event.set?).to eq(true)
|
450
|
+
expect(event.result).to eq(
|
451
|
+
bundled_builder([an_elt] * threshold)
|
452
|
+
)
|
453
|
+
end
|
454
|
+
end
|
455
|
+
end
|
456
|
+
|
457
|
+
it 'api call not invoked until byte threshold' do
|
458
|
+
an_elt = 'dummy_msg'
|
459
|
+
an_id = 'bundle_id'
|
460
|
+
api_call = return_request
|
461
|
+
elts_for_threshold = 3
|
462
|
+
threshold = elts_for_threshold * an_elt.bytesize
|
463
|
+
options =
|
464
|
+
Google::Gax::BundleOptions.new(request_byte_threshold: threshold)
|
465
|
+
bundler = Google::Gax::Executor.new(options)
|
466
|
+
1.upto(elts_for_threshold) do |i|
|
467
|
+
event = bundler.schedule(
|
468
|
+
api_call,
|
469
|
+
an_id,
|
470
|
+
SIMPLE_DESCRIPTOR,
|
471
|
+
bundled_builder([an_elt])
|
472
|
+
)
|
473
|
+
|
474
|
+
expect(event.canceller).to_not be_nil
|
475
|
+
if i < elts_for_threshold
|
476
|
+
expect(event.set?).to eq(false)
|
477
|
+
expect(event.result).to be_nil
|
478
|
+
else
|
479
|
+
expect(event.set?).to eq(true)
|
480
|
+
expect(event.result).to eq(
|
481
|
+
bundled_builder([an_elt] * elts_for_threshold)
|
482
|
+
)
|
483
|
+
end
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
487
|
+
class MockTimer
|
488
|
+
attr_accessor :asleep
|
489
|
+
|
490
|
+
def initialize
|
491
|
+
@asleep = true
|
492
|
+
end
|
493
|
+
|
494
|
+
def run_after(_)
|
495
|
+
loop do
|
496
|
+
break unless @asleep
|
497
|
+
sleep(1 / Google::Gax::MILLIS_PER_SECOND)
|
498
|
+
end
|
499
|
+
yield
|
500
|
+
end
|
501
|
+
end
|
502
|
+
|
503
|
+
it 'api call not invoked until time threshold' do
|
504
|
+
test_timer = MockTimer.new
|
505
|
+
an_elt = 'dummy_msg'
|
506
|
+
an_id = 'bundle_id'
|
507
|
+
api_call = return_request
|
508
|
+
delay = 10_000
|
509
|
+
options =
|
510
|
+
Google::Gax::BundleOptions.new(delay_threshold_millis: delay)
|
511
|
+
bundler = Google::Gax::Executor.new(options, timer: test_timer)
|
512
|
+
event = bundler.schedule(
|
513
|
+
api_call,
|
514
|
+
an_id,
|
515
|
+
SIMPLE_DESCRIPTOR,
|
516
|
+
bundled_builder([an_elt])
|
517
|
+
)
|
518
|
+
expect(event.canceller).to_not be_nil
|
519
|
+
expect(event.set?).to eq(false)
|
520
|
+
|
521
|
+
test_timer.asleep = false
|
522
|
+
|
523
|
+
# Wait until the event is set because the timer flag will need time
|
524
|
+
# to propogate through to the thread.
|
525
|
+
event.wait
|
526
|
+
expect(event.result).to eq(bundled_builder([an_elt]))
|
527
|
+
end
|
528
|
+
end
|
529
|
+
|
530
|
+
context 'method `close` works' do
|
531
|
+
it 'non timed bundles are sent when the executor is closed' do
|
532
|
+
an_elt = 'dummy_message'
|
533
|
+
api_call = return_request
|
534
|
+
bundle_id = 'an_id'
|
535
|
+
threshold = 10 # arbitrary, greater than the number of elts sent
|
536
|
+
options =
|
537
|
+
Google::Gax::BundleOptions.new(element_count_threshold: threshold)
|
538
|
+
bundler = Google::Gax::Executor.new(options)
|
539
|
+
events = []
|
540
|
+
|
541
|
+
# send 3 groups of elements of different sizes in the bundle
|
542
|
+
1.upto(3) do |i|
|
543
|
+
event = bundler.schedule(
|
544
|
+
api_call,
|
545
|
+
bundle_id,
|
546
|
+
DEMUX_DESCRIPTOR,
|
547
|
+
bundled_builder(["#{an_elt}#{i}"] * i)
|
548
|
+
)
|
549
|
+
events.push(event)
|
550
|
+
end
|
551
|
+
|
552
|
+
bundler.close
|
553
|
+
|
554
|
+
previous_event = nil
|
555
|
+
events.each_with_index do |current_event, i|
|
556
|
+
index = i + 1
|
557
|
+
expect(current_event).to_not eq(previous_event)
|
558
|
+
expect(current_event.set?).to eq(true)
|
559
|
+
expect(current_event.result).to eq(
|
560
|
+
bundled_builder(["#{an_elt}#{index}"] * index)
|
561
|
+
)
|
562
|
+
previous_event = current_event
|
563
|
+
end
|
564
|
+
end
|
565
|
+
|
566
|
+
it 'timed bundles are sent when the executor is closed' do
|
567
|
+
an_elt = 'dummy_msg'
|
568
|
+
an_id = 'bundle_id'
|
569
|
+
api_call = return_request
|
570
|
+
delay_threshold_millis = 100_000 # arbitrary, very high.
|
571
|
+
options =
|
572
|
+
Google::Gax::BundleOptions.new(
|
573
|
+
delay_threshold_millis: delay_threshold_millis
|
574
|
+
)
|
575
|
+
bundler = Google::Gax::Executor.new(options)
|
576
|
+
event = bundler.schedule(
|
577
|
+
api_call,
|
578
|
+
an_id,
|
579
|
+
SIMPLE_DESCRIPTOR,
|
580
|
+
bundled_builder([an_elt])
|
581
|
+
)
|
582
|
+
expect(event.canceller).to_not be_nil
|
583
|
+
expect(event.set?).to eq(false)
|
584
|
+
|
585
|
+
bundler.close
|
586
|
+
expect(event.canceller).to_not be_nil
|
587
|
+
expect(event.set?).to eq(true)
|
588
|
+
expect(event.result).to eq(bundled_builder([an_elt]))
|
589
|
+
end
|
590
|
+
end
|
591
|
+
end
|
592
|
+
describe Google::Gax::Event do
|
593
|
+
context 'event is working correctly' do
|
594
|
+
it 'can be set' do
|
595
|
+
event = Google::Gax::Event.new
|
596
|
+
expect(event.set?).to eq(false)
|
597
|
+
event.result = Object.new
|
598
|
+
expect(event.set?).to eq(true)
|
599
|
+
end
|
600
|
+
|
601
|
+
it 'returns false when #cancel is called with no canceller' do
|
602
|
+
event = Google::Gax::Event.new
|
603
|
+
expect(event.canceller).to be_nil
|
604
|
+
expect(event.cancel).to eq(false)
|
605
|
+
end
|
606
|
+
|
607
|
+
it 'returns cancellers result when # cancel is called' do
|
608
|
+
event = Google::Gax::Event.new
|
609
|
+
event.canceller = proc { true }
|
610
|
+
expect(event.cancel).to eq(true)
|
611
|
+
event.canceller = proc { false }
|
612
|
+
expect(event.cancel).to eq(false)
|
613
|
+
end
|
614
|
+
|
615
|
+
it 'wait does not block if event is set' do
|
616
|
+
event = Google::Gax::Event.new
|
617
|
+
event.result = Object.new
|
618
|
+
expect(event.wait).to eq(true)
|
619
|
+
end
|
620
|
+
|
621
|
+
it 'waits returns false after the timeout period' do
|
622
|
+
event = Google::Gax::Event.new
|
623
|
+
expect(event.wait(timeout_millis: 1)).to eq(false)
|
624
|
+
end
|
625
|
+
end
|
626
|
+
end
|
627
|
+
end
|