yugabyte-ycql-driver 3.2.3.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.
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