yugabyte-ycql-driver 3.2.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (145) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +13 -0
  3. data/README.md +242 -0
  4. data/ext/cassandra_murmur3/cassandra_murmur3.c +178 -0
  5. data/ext/cassandra_murmur3/extconf.rb +2 -0
  6. data/lib/cassandra/address_resolution.rb +36 -0
  7. data/lib/cassandra/address_resolution/policies.rb +2 -0
  8. data/lib/cassandra/address_resolution/policies/ec2_multi_region.rb +56 -0
  9. data/lib/cassandra/address_resolution/policies/none.rb +35 -0
  10. data/lib/cassandra/aggregate.rb +123 -0
  11. data/lib/cassandra/argument.rb +51 -0
  12. data/lib/cassandra/attr_boolean.rb +33 -0
  13. data/lib/cassandra/auth.rb +100 -0
  14. data/lib/cassandra/auth/providers.rb +17 -0
  15. data/lib/cassandra/auth/providers/password.rb +65 -0
  16. data/lib/cassandra/cassandra_logger.rb +80 -0
  17. data/lib/cassandra/cluster.rb +331 -0
  18. data/lib/cassandra/cluster/client.rb +1612 -0
  19. data/lib/cassandra/cluster/connection_pool.rb +78 -0
  20. data/lib/cassandra/cluster/connector.rb +372 -0
  21. data/lib/cassandra/cluster/control_connection.rb +962 -0
  22. data/lib/cassandra/cluster/failed_connection.rb +35 -0
  23. data/lib/cassandra/cluster/metadata.rb +142 -0
  24. data/lib/cassandra/cluster/options.rb +145 -0
  25. data/lib/cassandra/cluster/registry.rb +284 -0
  26. data/lib/cassandra/cluster/schema.rb +405 -0
  27. data/lib/cassandra/cluster/schema/cql_type_parser.rb +112 -0
  28. data/lib/cassandra/cluster/schema/fetchers.rb +1627 -0
  29. data/lib/cassandra/cluster/schema/fqcn_type_parser.rb +175 -0
  30. data/lib/cassandra/cluster/schema/partitioners.rb +21 -0
  31. data/lib/cassandra/cluster/schema/partitioners/murmur3.rb +45 -0
  32. data/lib/cassandra/cluster/schema/partitioners/ordered.rb +37 -0
  33. data/lib/cassandra/cluster/schema/partitioners/random.rb +37 -0
  34. data/lib/cassandra/cluster/schema/replication_strategies.rb +21 -0
  35. data/lib/cassandra/cluster/schema/replication_strategies/network_topology.rb +102 -0
  36. data/lib/cassandra/cluster/schema/replication_strategies/none.rb +39 -0
  37. data/lib/cassandra/cluster/schema/replication_strategies/simple.rb +44 -0
  38. data/lib/cassandra/column.rb +66 -0
  39. data/lib/cassandra/column_container.rb +326 -0
  40. data/lib/cassandra/compression.rb +69 -0
  41. data/lib/cassandra/compression/compressors/lz4.rb +73 -0
  42. data/lib/cassandra/compression/compressors/snappy.rb +69 -0
  43. data/lib/cassandra/custom_data.rb +53 -0
  44. data/lib/cassandra/driver.rb +260 -0
  45. data/lib/cassandra/errors.rb +784 -0
  46. data/lib/cassandra/execution/info.rb +69 -0
  47. data/lib/cassandra/execution/options.rb +267 -0
  48. data/lib/cassandra/execution/profile.rb +153 -0
  49. data/lib/cassandra/execution/profile_manager.rb +71 -0
  50. data/lib/cassandra/execution/trace.rb +192 -0
  51. data/lib/cassandra/executors.rb +113 -0
  52. data/lib/cassandra/function.rb +156 -0
  53. data/lib/cassandra/function_collection.rb +85 -0
  54. data/lib/cassandra/future.rb +794 -0
  55. data/lib/cassandra/host.rb +102 -0
  56. data/lib/cassandra/index.rb +118 -0
  57. data/lib/cassandra/keyspace.rb +473 -0
  58. data/lib/cassandra/listener.rb +87 -0
  59. data/lib/cassandra/load_balancing.rb +121 -0
  60. data/lib/cassandra/load_balancing/policies.rb +20 -0
  61. data/lib/cassandra/load_balancing/policies/dc_aware_round_robin.rb +172 -0
  62. data/lib/cassandra/load_balancing/policies/round_robin.rb +141 -0
  63. data/lib/cassandra/load_balancing/policies/token_aware.rb +149 -0
  64. data/lib/cassandra/load_balancing/policies/white_list.rb +100 -0
  65. data/lib/cassandra/materialized_view.rb +92 -0
  66. data/lib/cassandra/null_logger.rb +56 -0
  67. data/lib/cassandra/protocol.rb +102 -0
  68. data/lib/cassandra/protocol/coder.rb +1085 -0
  69. data/lib/cassandra/protocol/cql_byte_buffer.rb +418 -0
  70. data/lib/cassandra/protocol/cql_protocol_handler.rb +448 -0
  71. data/lib/cassandra/protocol/request.rb +41 -0
  72. data/lib/cassandra/protocol/requests/auth_response_request.rb +51 -0
  73. data/lib/cassandra/protocol/requests/batch_request.rb +117 -0
  74. data/lib/cassandra/protocol/requests/credentials_request.rb +51 -0
  75. data/lib/cassandra/protocol/requests/execute_request.rb +122 -0
  76. data/lib/cassandra/protocol/requests/options_request.rb +39 -0
  77. data/lib/cassandra/protocol/requests/prepare_request.rb +59 -0
  78. data/lib/cassandra/protocol/requests/query_request.rb +112 -0
  79. data/lib/cassandra/protocol/requests/register_request.rb +38 -0
  80. data/lib/cassandra/protocol/requests/startup_request.rb +49 -0
  81. data/lib/cassandra/protocol/requests/void_query_request.rb +24 -0
  82. data/lib/cassandra/protocol/response.rb +28 -0
  83. data/lib/cassandra/protocol/responses/already_exists_error_response.rb +50 -0
  84. data/lib/cassandra/protocol/responses/auth_challenge_response.rb +36 -0
  85. data/lib/cassandra/protocol/responses/auth_success_response.rb +36 -0
  86. data/lib/cassandra/protocol/responses/authenticate_response.rb +36 -0
  87. data/lib/cassandra/protocol/responses/error_response.rb +142 -0
  88. data/lib/cassandra/protocol/responses/event_response.rb +30 -0
  89. data/lib/cassandra/protocol/responses/function_failure_error_response.rb +52 -0
  90. data/lib/cassandra/protocol/responses/prepared_result_response.rb +62 -0
  91. data/lib/cassandra/protocol/responses/raw_rows_result_response.rb +59 -0
  92. data/lib/cassandra/protocol/responses/read_failure_error_response.rb +71 -0
  93. data/lib/cassandra/protocol/responses/read_timeout_error_response.rb +61 -0
  94. data/lib/cassandra/protocol/responses/ready_response.rb +43 -0
  95. data/lib/cassandra/protocol/responses/result_response.rb +42 -0
  96. data/lib/cassandra/protocol/responses/rows_result_response.rb +39 -0
  97. data/lib/cassandra/protocol/responses/schema_change_event_response.rb +73 -0
  98. data/lib/cassandra/protocol/responses/schema_change_result_response.rb +70 -0
  99. data/lib/cassandra/protocol/responses/set_keyspace_result_response.rb +37 -0
  100. data/lib/cassandra/protocol/responses/status_change_event_response.rb +39 -0
  101. data/lib/cassandra/protocol/responses/supported_response.rb +36 -0
  102. data/lib/cassandra/protocol/responses/topology_change_event_response.rb +33 -0
  103. data/lib/cassandra/protocol/responses/unavailable_error_response.rb +58 -0
  104. data/lib/cassandra/protocol/responses/unprepared_error_response.rb +48 -0
  105. data/lib/cassandra/protocol/responses/void_result_response.rb +34 -0
  106. data/lib/cassandra/protocol/responses/write_failure_error_response.rb +73 -0
  107. data/lib/cassandra/protocol/responses/write_timeout_error_response.rb +63 -0
  108. data/lib/cassandra/protocol/v1.rb +326 -0
  109. data/lib/cassandra/protocol/v3.rb +358 -0
  110. data/lib/cassandra/protocol/v4.rb +478 -0
  111. data/lib/cassandra/reconnection.rb +49 -0
  112. data/lib/cassandra/reconnection/policies.rb +20 -0
  113. data/lib/cassandra/reconnection/policies/constant.rb +46 -0
  114. data/lib/cassandra/reconnection/policies/exponential.rb +79 -0
  115. data/lib/cassandra/result.rb +276 -0
  116. data/lib/cassandra/retry.rb +154 -0
  117. data/lib/cassandra/retry/policies.rb +21 -0
  118. data/lib/cassandra/retry/policies/default.rb +53 -0
  119. data/lib/cassandra/retry/policies/downgrading_consistency.rb +73 -0
  120. data/lib/cassandra/retry/policies/fallthrough.rb +39 -0
  121. data/lib/cassandra/session.rb +270 -0
  122. data/lib/cassandra/statement.rb +32 -0
  123. data/lib/cassandra/statements.rb +23 -0
  124. data/lib/cassandra/statements/batch.rb +146 -0
  125. data/lib/cassandra/statements/bound.rb +65 -0
  126. data/lib/cassandra/statements/prepared.rb +235 -0
  127. data/lib/cassandra/statements/simple.rb +118 -0
  128. data/lib/cassandra/statements/void.rb +38 -0
  129. data/lib/cassandra/table.rb +240 -0
  130. data/lib/cassandra/time.rb +103 -0
  131. data/lib/cassandra/time_uuid.rb +78 -0
  132. data/lib/cassandra/timestamp_generator.rb +37 -0
  133. data/lib/cassandra/timestamp_generator/simple.rb +38 -0
  134. data/lib/cassandra/timestamp_generator/ticking_on_duplicate.rb +58 -0
  135. data/lib/cassandra/trigger.rb +67 -0
  136. data/lib/cassandra/tuple.rb +131 -0
  137. data/lib/cassandra/types.rb +1704 -0
  138. data/lib/cassandra/udt.rb +443 -0
  139. data/lib/cassandra/util.rb +464 -0
  140. data/lib/cassandra/uuid.rb +110 -0
  141. data/lib/cassandra/uuid/generator.rb +212 -0
  142. data/lib/cassandra/version.rb +21 -0
  143. data/lib/datastax/cassandra.rb +47 -0
  144. data/lib/ycql.rb +842 -0
  145. metadata +243 -0
@@ -0,0 +1,85 @@
1
+ # encoding: utf-8
2
+
3
+ #--
4
+ # Copyright DataStax, Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #++
18
+
19
+ module Cassandra
20
+ # @private
21
+ # This class encapsulates a collection of functions or aggregates.
22
+ # Really used internally, so it should not be documented.
23
+ class FunctionCollection
24
+ def initialize
25
+ @function_hash = {}
26
+ end
27
+
28
+ # Get the Function or Aggregate with the given name and argument-types.
29
+ # @param name [String] the name of the function/aggregate.
30
+ # @param argument_types [Array<Cassandra::Type>] list of argument-types.
31
+ # @return [Cassandra::Function] or [Cassandra::Aggregate] if found;
32
+ # nil otherwise.
33
+ def get(name, argument_types)
34
+ @function_hash[[name, argument_types]]
35
+ end
36
+
37
+ def add_or_update(function)
38
+ @function_hash[[function.name, function.argument_types]] = function
39
+ end
40
+
41
+ def delete(name, argument_types)
42
+ @function_hash.delete([name, argument_types])
43
+ end
44
+
45
+ # @return [Boolean] whether this FunctionCollection is equal to the other
46
+ def eql?(other)
47
+ other.is_a?(FunctionCollection) &&
48
+ @function_hash == other.raw_functions
49
+ end
50
+ alias == eql?
51
+
52
+ def hash
53
+ @function_hash.hash
54
+ end
55
+
56
+ # Yield or enumerate each function defined in this collection
57
+ # @overload each_function
58
+ # @yieldparam function [Cassandra::Function or Cassandra::Aggregate]
59
+ # current function or aggregate
60
+ # @return [Cassandra::FunctionCollection] self
61
+ # @overload each_function
62
+ # @return [Array<Cassandra::Function> or Array<Cassandra::Aggregate>]
63
+ # a list of functions or aggregates.
64
+ def each_function(&block)
65
+ if block_given?
66
+ @function_hash.each_value(&block)
67
+ self
68
+ else
69
+ @function_hash.values
70
+ end
71
+ end
72
+ alias functions each_function
73
+
74
+ def inspect
75
+ "#<Cassandra::FunctionCollection:0x#{object_id.to_s(16)} " \
76
+ "@function_hash=#{@function_hash.inspect}>"
77
+ end
78
+
79
+ protected
80
+
81
+ def raw_functions
82
+ @function_hash
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,794 @@
1
+ # encoding: utf-8
2
+
3
+ #--
4
+ # Copyright DataStax, Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #++
18
+
19
+ module Cassandra
20
+ # A Future represents a result of asynchronous execution. It can be used to
21
+ # block until a value is available or an error has happened, or register a
22
+ # listener to be notified whenever the execution is complete.
23
+ class Future
24
+ # a Future listener to be passed to {Cassandra::Future#add_listener}
25
+ #
26
+ # @note Listener methods can be called from application if a future has
27
+ # been resolved or failed by the time the listener is registered; or from
28
+ # background thread if it is resolved/failed after the listener has been
29
+ # registered.
30
+ #
31
+ # @abstract Actual listeners passed to {Cassandra::Future#add_listener} don't
32
+ # need to extend this class as long as they implement `#success` and
33
+ # `#failure` methods
34
+ class Listener
35
+ # @param value [Object] actual value the future has been resolved with
36
+ # @return [void]
37
+ def success(value)
38
+ end
39
+
40
+ # @param error [Exception] an exception used to fail the future
41
+ # @return [void]
42
+ def failure(error)
43
+ end
44
+ end
45
+
46
+ # @private
47
+ class Error < Future
48
+ def initialize(error)
49
+ raise ::ArgumentError, "error must be an exception, #{error.inspect} given" unless error.is_a?(::Exception)
50
+
51
+ @error = error
52
+ end
53
+
54
+ def get(timeout = nil)
55
+ raise(@error, @error.message, @error.backtrace)
56
+ end
57
+
58
+ alias join get
59
+
60
+ def on_success
61
+ raise ::ArgumentError, 'no block given' unless block_given?
62
+ self
63
+ end
64
+
65
+ def on_failure
66
+ raise ::ArgumentError, 'no block given' unless block_given?
67
+ begin
68
+ yield(@error)
69
+ rescue
70
+ nil
71
+ end
72
+ self
73
+ end
74
+
75
+ def on_complete
76
+ raise ::ArgumentError, 'no block given' unless block_given?
77
+ begin
78
+ yield(nil, @error)
79
+ rescue
80
+ nil
81
+ end
82
+ self
83
+ end
84
+
85
+ def add_listener(listener)
86
+ unless listener.respond_to?(:success) && listener.respond_to?(:failure)
87
+ raise ::ArgumentError, 'listener must respond to both #success and #failure'
88
+ end
89
+
90
+ begin
91
+ listener.failure(@error)
92
+ rescue
93
+ nil
94
+ end
95
+ self
96
+ end
97
+
98
+ def then
99
+ raise ::ArgumentError, 'no block given' unless block_given?
100
+ self
101
+ end
102
+
103
+ def fallback
104
+ raise ::ArgumentError, 'no block given' unless block_given?
105
+
106
+ begin
107
+ result = yield(@error)
108
+ result = Value.new(result) unless result.is_a?(Future)
109
+ result
110
+ rescue => e
111
+ Error.new(e)
112
+ end
113
+ end
114
+ end
115
+
116
+ # @private
117
+ class Value < Future
118
+ def initialize(value)
119
+ @value = value
120
+ end
121
+
122
+ def get(timeout = nil)
123
+ @value
124
+ end
125
+
126
+ alias join get
127
+
128
+ def on_success
129
+ raise ::ArgumentError, 'no block given' unless block_given?
130
+ begin
131
+ yield(@value)
132
+ rescue
133
+ nil
134
+ end
135
+ self
136
+ end
137
+
138
+ def on_failure
139
+ raise ::ArgumentError, 'no block given' unless block_given?
140
+ self
141
+ end
142
+
143
+ def on_complete
144
+ raise ::ArgumentError, 'no block given' unless block_given?
145
+ begin
146
+ yield(@value, nil)
147
+ rescue
148
+ nil
149
+ end
150
+ self
151
+ end
152
+
153
+ def add_listener(listener)
154
+ unless listener.respond_to?(:success) && listener.respond_to?(:failure)
155
+ raise ::ArgumentError, 'listener must respond to both #success and #failure'
156
+ end
157
+
158
+ begin
159
+ listener.success(@value)
160
+ rescue
161
+ nil
162
+ end
163
+ self
164
+ end
165
+
166
+ def join
167
+ self
168
+ end
169
+
170
+ def then
171
+ raise ::ArgumentError, 'no block given' unless block_given?
172
+
173
+ begin
174
+ result = yield(@value)
175
+ result = Value.new(result) unless result.is_a?(Future)
176
+ result
177
+ rescue => e
178
+ Error.new(e)
179
+ end
180
+ end
181
+
182
+ def fallback
183
+ raise ::ArgumentError, 'no block given' unless block_given?
184
+ self
185
+ end
186
+ end
187
+
188
+ # @private
189
+ class Factory
190
+ def initialize(executor)
191
+ @executor = executor
192
+ end
193
+
194
+ def value(value)
195
+ Value.new(value)
196
+ end
197
+
198
+ def error(error)
199
+ Error.new(error)
200
+ end
201
+
202
+ def promise
203
+ Promise.new(@executor)
204
+ end
205
+
206
+ def all(*futures)
207
+ # May get called with varargs or an array of futures. In the latter case,
208
+ # the first element in futures is the array of futures. Promote it.
209
+ futures = Array(futures.first) if futures.one?
210
+
211
+ # Special case where there are no futures to aggregate.
212
+ return Value.new([]) if futures.empty?
213
+
214
+ monitor = Monitor.new
215
+ promise = Promise.new(@executor)
216
+ remaining = futures.length
217
+ values = Array.new(remaining)
218
+
219
+ futures.each_with_index do |future, i|
220
+ future.on_complete do |v, e|
221
+ if e
222
+ promise.break(e)
223
+ else
224
+ done = false
225
+ monitor.synchronize do
226
+ remaining -= 1
227
+ done = (remaining == 0)
228
+ values[i] = v
229
+ end
230
+ promise.fulfill(values) if done
231
+ end
232
+ end
233
+ end
234
+ promise.future
235
+ end
236
+ end
237
+
238
+ # @private
239
+ @@factory = Factory.new(Executors::SameThread.new)
240
+
241
+ # Returns a future resolved to a given value
242
+ # @param value [Object] value for the future
243
+ # @return [Cassandra::Future<Object>] a future value
244
+ def self.value(value)
245
+ @@factory.value(value)
246
+ end
247
+
248
+ # Returns a future resolved to a given error
249
+ # @param error [Exception] error for the future
250
+ # @return [Cassandra::Future<Exception>] a future error
251
+ def self.error(error)
252
+ @@factory.error(error)
253
+ end
254
+
255
+ # Returns a future that resolves with values of all futures
256
+ # @overload all(*futures)
257
+ # @param *futures [Cassandra::Future] futures to combine
258
+ # @return [Cassandra::Future<Array<Object>>] a combined future
259
+ # @overload all(futures)
260
+ # @param futures [Enumerable<Cassandra::Future>] list of futures to
261
+ # combine
262
+ # @return [Cassandra::Future<Array<Object>>] a combined future
263
+ def self.all(*futures)
264
+ @@factory.all(*futures)
265
+ end
266
+
267
+ # Returns a new promise instance
268
+ def self.promise
269
+ @@factory.promise
270
+ end
271
+
272
+ # @private
273
+ def initialize(signal)
274
+ @signal = signal
275
+ end
276
+
277
+ # Run block when future resolves to a value
278
+ # @note The block can be called synchronously from current thread if the
279
+ # future has already been resolved, or, asynchronously, from background
280
+ # thread upon resolution.
281
+ # @yieldparam value [Object] a value
282
+ # @raise [ArgumentError] if no block given
283
+ # @return [self]
284
+ def on_success(&block)
285
+ raise ::ArgumentError, 'no block given' unless block_given?
286
+ @signal.on_success(&block)
287
+ self
288
+ end
289
+
290
+ # Run block when future resolves to error
291
+ # @note The block can be called synchronously from current thread if the
292
+ # future has already been resolved, or, asynchronously, from background
293
+ # thread upon resolution.
294
+ # @yieldparam error [Exception] an error
295
+ # @raise [ArgumentError] if no block given
296
+ # @return [self]
297
+ def on_failure(&block)
298
+ raise ::ArgumentError, 'no block given' unless block_given?
299
+ @signal.on_failure(&block)
300
+ self
301
+ end
302
+
303
+ # Run block when future resolves. The block will always be called with 2
304
+ # arguments - value and error. In case a future resolves to an error, the
305
+ # error argument will be non-nil.
306
+ # @note The block can be called synchronously from current thread if the
307
+ # future has already been resolved, or, asynchronously, from background
308
+ # thread upon resolution.
309
+ # @yieldparam value [Object, nil] a value or nil
310
+ # @yieldparam error [Exception, nil] an error or nil
311
+ # @raise [ArgumentError] if no block given
312
+ # @return [self]
313
+ def on_complete(&block)
314
+ raise ::ArgumentError, 'no block given' unless block_given?
315
+ @signal.on_complete(&block)
316
+ self
317
+ end
318
+
319
+ # Add future listener
320
+ # @note The listener can be notified synchronously, from current thread, if
321
+ # the future has already been resolved, or, asynchronously, from
322
+ # background thread upon resolution.
323
+ # @note that provided listener doesn't have to extend
324
+ # {Cassandra::Future::Listener}, only conform to the same interface
325
+ # @param listener [Cassandra::Future::Listener] an object that responds to
326
+ # `#success` and `#failure`
327
+ # @return [self]
328
+ def add_listener(listener)
329
+ unless listener.respond_to?(:success) && listener.respond_to?(:failure)
330
+ raise ::ArgumentError, 'listener must respond to both #success and #failure'
331
+ end
332
+
333
+ @signal.add_listener(listener)
334
+ self
335
+ end
336
+
337
+ # Returns a new future that will resolve to the result of the block.
338
+ # Besides regular values, block can return other futures, which will be
339
+ # transparently unwrapped before resolving the future from this method.
340
+ #
341
+ # @example Block returns a value
342
+ # future_users = session.execute_async('SELECT * FROM users WHERE user_name = ?', 'Sam')
343
+ # future_user = future_users.then {|users| users.first}
344
+ #
345
+ # @example Block returns a future
346
+ # future_statement = session.prepare_async('SELECT * FROM users WHERE user_name = ?')
347
+ # future_users = future_statement.then {|statement| session.execute_async(statement, 'Sam')}
348
+ #
349
+ # @note The block can be called synchronously from current thread if the
350
+ # future has already been resolved, or, asynchronously, from background
351
+ # thread upon resolution.
352
+ # @yieldparam value [Object] a value
353
+ # @yieldreturn [Cassandra::Future, Object] a future or a value to be
354
+ # wrapped in a future
355
+ # @raise [ArgumentError] if no block given
356
+ # @return [Cassandra::Future] a new future
357
+ def then(&block)
358
+ raise ::ArgumentError, 'no block given' unless block_given?
359
+ @signal.then(&block)
360
+ end
361
+
362
+ # Returns a new future that will resolve to the result of the block in case
363
+ # of an error. Besides regular values, block can return other futures,
364
+ # which will be transparently unwrapped before resolving the future from
365
+ # this method.
366
+ #
367
+ # @example Recovering from errors
368
+ # future_error = session.execute_async('SELECT * FROM invalid-table')
369
+ # future = future_error.fallback {|error| "Execution failed with #{error.class.name}: #{error.message}"}
370
+ #
371
+ # @example Executing something else on error
372
+ # future_error = session.execute_async('SELECT * FROM invalid-table')
373
+ # future = future_error.fallback {|e| session.execute_async('SELECT * FROM another-table')}
374
+ #
375
+ # @note The block can be called synchronously from current thread if the
376
+ # future has already been resolved, or, asynchronously, from background
377
+ # thread upon resolution.
378
+ # @yieldparam error [Exception] an error
379
+ # @yieldreturn [Cassandra::Future, Object] a future or a value to be
380
+ # wrapped in a future
381
+ # @raise [ArgumentError] if no block given
382
+ # @return [Cassandra::Future] a new future
383
+ def fallback(&block)
384
+ raise ::ArgumentError, 'no block given' unless block_given?
385
+ @signal.fallback(&block)
386
+ end
387
+
388
+ # Returns future value or raises future error
389
+ #
390
+ # @note This method blocks until a future is resolved or a times out
391
+ #
392
+ # @param timeout [nil, Numeric] a maximum number of seconds to block
393
+ # current thread for while waiting for this future to resolve. Will
394
+ # wait indefinitely if passed `nil`.
395
+ #
396
+ # @raise [Errors::TimeoutError] raised when wait time exceeds the timeout
397
+ # @raise [Exception] raises when the future has been resolved with an
398
+ # error. The original exception will be raised.
399
+ #
400
+ # @return [Object] the value that the future has been resolved with
401
+ def get(timeout = nil)
402
+ @signal.get(timeout)
403
+ end
404
+
405
+ alias join get
406
+ end
407
+
408
+ # @private
409
+ class Promise
410
+ # @private
411
+ class Signal
412
+ # @private
413
+ module Listeners
414
+ class Success < Future::Listener
415
+ def initialize(&block)
416
+ @block = block
417
+ end
418
+
419
+ def success(value)
420
+ @block.call(value)
421
+ end
422
+
423
+ def failure(error)
424
+ nil
425
+ end
426
+ end
427
+
428
+ class Failure < Future::Listener
429
+ def initialize(&block)
430
+ @block = block
431
+ end
432
+
433
+ def success(value)
434
+ nil
435
+ end
436
+
437
+ def failure(error)
438
+ @block.call(error)
439
+ end
440
+ end
441
+
442
+ class Complete < Future::Listener
443
+ def initialize(&block)
444
+ @block = block
445
+ end
446
+
447
+ def success(value)
448
+ @block.call(value, nil)
449
+ end
450
+
451
+ def failure(error)
452
+ @block.call(nil, error)
453
+ end
454
+ end
455
+
456
+ class Then < Future::Listener
457
+ def initialize(promise, &block)
458
+ @promise = promise
459
+ @block = block
460
+ end
461
+
462
+ def success(value)
463
+ result = @block.call(value)
464
+
465
+ if result.is_a?(Future)
466
+ @promise.observe(result)
467
+ else
468
+ @promise.fulfill(result)
469
+ end
470
+ rescue => e
471
+ @promise.break(e)
472
+ ensure
473
+ @promise = @block = nil
474
+ end
475
+
476
+ def failure(error)
477
+ @promise.break(error)
478
+ ensure
479
+ @promise = @block = nil
480
+ end
481
+ end
482
+
483
+ class Fallback < Future::Listener
484
+ def initialize(promise, &block)
485
+ @promise = promise
486
+ @block = block
487
+ end
488
+
489
+ def success(value)
490
+ @promise.fulfill(value)
491
+ ensure
492
+ @promise = @block = nil
493
+ end
494
+
495
+ def failure(error)
496
+ result = @block.call(error)
497
+
498
+ if result.is_a?(Future)
499
+ @promise.observe(result)
500
+ else
501
+ @promise.fulfill(result)
502
+ end
503
+ rescue => e
504
+ @promise.break(e)
505
+ ensure
506
+ @promise = @block = nil
507
+ end
508
+ end
509
+ end
510
+
511
+ include MonitorMixin
512
+
513
+ def initialize(executor)
514
+ mon_initialize
515
+
516
+ @cond = new_cond
517
+ @executor = executor
518
+ @state = :pending
519
+ @waiting = 0
520
+ @error = nil
521
+ @value = nil
522
+ @listeners = []
523
+ end
524
+
525
+ def failure(error)
526
+ raise ::ArgumentError, "error must be an exception, #{error.inspect} given" unless error.is_a?(::Exception)
527
+
528
+ return unless @state == :pending
529
+
530
+ listeners = nil
531
+
532
+ synchronize do
533
+ return unless @state == :pending
534
+
535
+ @error = error
536
+ @state = :broken
537
+
538
+ listeners = @listeners
539
+ @listeners = nil
540
+ end
541
+
542
+ @executor.execute do
543
+ listeners.each do |listener|
544
+ begin
545
+ listener.failure(error)
546
+ rescue
547
+ nil
548
+ end
549
+ end
550
+
551
+ synchronize do
552
+ @cond.broadcast if @waiting > 0
553
+ end
554
+ end
555
+
556
+ self
557
+ end
558
+
559
+ def success(value)
560
+ return unless @state == :pending
561
+
562
+ listeners = nil
563
+
564
+ synchronize do
565
+ return unless @state == :pending
566
+
567
+ @value = value
568
+ @state = :fulfilled
569
+
570
+ listeners = @listeners
571
+ @listeners = nil
572
+ end
573
+
574
+ @executor.execute do
575
+ listeners.each do |listener|
576
+ begin
577
+ listener.success(value)
578
+ rescue
579
+ nil
580
+ end
581
+ end
582
+
583
+ synchronize do
584
+ @cond.broadcast if @waiting > 0
585
+ end
586
+ end
587
+
588
+ self
589
+ end
590
+
591
+ # @param timeout [nil, Numeric] a maximum number of seconds to block
592
+ # current thread for while waiting for this future to resolve. Will
593
+ # wait indefinitely if passed `nil`.
594
+ #
595
+ # @raise [ArgumentError] raised when a negative timeout is given
596
+ # @raise [Errors::TimeoutError] raised when wait time exceeds the timeout
597
+ # @raise [Exception] raises when the future has been resolved with an
598
+ # error. The original exception will be raised.
599
+ #
600
+ # @return [Object] the value that the future has been resolved with
601
+ def get(timeout = nil)
602
+ timeout &&= Float(timeout)
603
+
604
+ if timeout
605
+ raise ::ArgumentError, "timeout cannot be negative, #{timeout.inspect} given" if timeout < 0
606
+
607
+ start = ::Time.now
608
+ now = start
609
+ deadline = start + timeout
610
+ end
611
+
612
+ if @state == :pending
613
+ synchronize do
614
+ if @state == :pending
615
+ @waiting += 1
616
+ while @state == :pending
617
+ if deadline
618
+ @cond.wait(deadline - now)
619
+ now = ::Time.now
620
+ break if now >= deadline
621
+ else
622
+ @cond.wait
623
+ end
624
+ end
625
+ @waiting -= 1
626
+ end
627
+ end
628
+
629
+ if @state == :pending
630
+ total_wait = deadline - start
631
+ raise Errors::TimeoutError,
632
+ "Future did not complete within #{timeout.inspect} seconds. " \
633
+ "Wait time: #{total_wait.inspect}"
634
+ end
635
+ end
636
+
637
+ raise(@error, @error.message, @error.backtrace) if @state == :broken
638
+
639
+ @value
640
+ end
641
+
642
+ alias join get
643
+
644
+ def add_listener(listener)
645
+ if @state == :pending
646
+ synchronize do
647
+ if @state == :pending
648
+ @listeners << listener
649
+
650
+ return self
651
+ end
652
+ end
653
+ end
654
+
655
+ begin
656
+ listener.success(@value)
657
+ rescue
658
+ nil
659
+ end if @state == :fulfilled
660
+ begin
661
+ listener.failure(@error)
662
+ rescue
663
+ nil
664
+ end if @state == :broken
665
+
666
+ self
667
+ end
668
+
669
+ def on_success(&block)
670
+ if @state == :pending
671
+ synchronize do
672
+ if @state == :pending
673
+ @listeners << Listeners::Success.new(&block)
674
+ return self
675
+ end
676
+ end
677
+ end
678
+
679
+ begin
680
+ yield(@value)
681
+ rescue
682
+ nil
683
+ end if @state == :fulfilled
684
+
685
+ self
686
+ end
687
+
688
+ def on_failure(&block)
689
+ if @state == :pending
690
+ synchronize do
691
+ if @state == :pending
692
+ @listeners << Listeners::Failure.new(&block)
693
+ return self
694
+ end
695
+ end
696
+ end
697
+
698
+ begin
699
+ yield(@error)
700
+ rescue
701
+ nil
702
+ end if @state == :broken
703
+
704
+ self
705
+ end
706
+
707
+ def on_complete(&block)
708
+ if @state == :pending
709
+ synchronize do
710
+ if @state == :pending
711
+ @listeners << Listeners::Complete.new(&block)
712
+ return self
713
+ end
714
+ end
715
+ end
716
+
717
+ begin
718
+ yield(@value, @error)
719
+ rescue
720
+ nil
721
+ end
722
+
723
+ self
724
+ end
725
+
726
+ def then(&block)
727
+ if @state == :pending
728
+ synchronize do
729
+ if @state == :pending
730
+ promise = Promise.new(@executor)
731
+ listener = Listeners::Then.new(promise, &block)
732
+ @listeners << listener
733
+ return promise.future
734
+ end
735
+ end
736
+ end
737
+
738
+ return Future::Error.new(@error) if @state == :broken
739
+
740
+ begin
741
+ result = yield(@value)
742
+ result = Future::Value.new(result) unless result.is_a?(Future)
743
+ result
744
+ rescue => e
745
+ Future::Error.new(e)
746
+ end
747
+ end
748
+
749
+ def fallback(&block)
750
+ if @state == :pending
751
+ synchronize do
752
+ if @state == :pending
753
+ promise = Promise.new(@executor)
754
+ listener = Listeners::Fallback.new(promise, &block)
755
+ @listeners << listener
756
+ return promise.future
757
+ end
758
+ end
759
+ end
760
+
761
+ return Future::Value.new(@value) if @state == :fulfilled
762
+
763
+ begin
764
+ result = yield(@error)
765
+ result = Future::Value.new(result) unless result.is_a?(Future)
766
+ result
767
+ rescue => e
768
+ Future::Error.new(e)
769
+ end
770
+ end
771
+ end
772
+
773
+ attr_reader :future
774
+
775
+ def initialize(executor)
776
+ @signal = Signal.new(executor)
777
+ @future = Future.new(@signal)
778
+ end
779
+
780
+ def break(error)
781
+ @signal.failure(error)
782
+ self
783
+ end
784
+
785
+ def fulfill(value)
786
+ @signal.success(value)
787
+ self
788
+ end
789
+
790
+ def observe(future)
791
+ future.add_listener(@signal)
792
+ end
793
+ end
794
+ end