cassandra-driver 1.0.0.beta.2-java
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 +7 -0
- data/.yardopts +4 -0
- data/README.md +125 -0
- data/lib/cassandra/auth/providers/password.rb +73 -0
- data/lib/cassandra/auth/providers.rb +16 -0
- data/lib/cassandra/auth.rb +97 -0
- data/lib/cassandra/client/batch.rb +212 -0
- data/lib/cassandra/client/client.rb +591 -0
- data/lib/cassandra/client/column_metadata.rb +54 -0
- data/lib/cassandra/client/connection_manager.rb +72 -0
- data/lib/cassandra/client/connector.rb +277 -0
- data/lib/cassandra/client/execute_options_decoder.rb +59 -0
- data/lib/cassandra/client/null_logger.rb +37 -0
- data/lib/cassandra/client/peer_discovery.rb +50 -0
- data/lib/cassandra/client/prepared_statement.rb +314 -0
- data/lib/cassandra/client/query_result.rb +230 -0
- data/lib/cassandra/client/request_runner.rb +71 -0
- data/lib/cassandra/client/result_metadata.rb +48 -0
- data/lib/cassandra/client/void_result.rb +78 -0
- data/lib/cassandra/client.rb +144 -0
- data/lib/cassandra/cluster/client.rb +768 -0
- data/lib/cassandra/cluster/connector.rb +244 -0
- data/lib/cassandra/cluster/control_connection.rb +425 -0
- data/lib/cassandra/cluster/metadata.rb +124 -0
- data/lib/cassandra/cluster/options.rb +42 -0
- data/lib/cassandra/cluster/registry.rb +198 -0
- data/lib/cassandra/cluster/schema/partitioners/murmur3.rb +47 -0
- data/lib/cassandra/cluster/schema/partitioners/ordered.rb +37 -0
- data/lib/cassandra/cluster/schema/partitioners/random.rb +37 -0
- data/lib/cassandra/cluster/schema/partitioners.rb +21 -0
- data/lib/cassandra/cluster/schema/replication_strategies/network_topology.rb +92 -0
- data/lib/cassandra/cluster/schema/replication_strategies/none.rb +39 -0
- data/lib/cassandra/cluster/schema/replication_strategies/simple.rb +44 -0
- data/lib/cassandra/cluster/schema/replication_strategies.rb +21 -0
- data/lib/cassandra/cluster/schema/type_parser.rb +138 -0
- data/lib/cassandra/cluster/schema.rb +340 -0
- data/lib/cassandra/cluster.rb +215 -0
- data/lib/cassandra/column.rb +92 -0
- data/lib/cassandra/compression/compressors/lz4.rb +72 -0
- data/lib/cassandra/compression/compressors/snappy.rb +66 -0
- data/lib/cassandra/compression.rb +66 -0
- data/lib/cassandra/driver.rb +111 -0
- data/lib/cassandra/errors.rb +79 -0
- data/lib/cassandra/execution/info.rb +51 -0
- data/lib/cassandra/execution/options.rb +80 -0
- data/lib/cassandra/execution/trace.rb +152 -0
- data/lib/cassandra/future.rb +675 -0
- data/lib/cassandra/host.rb +79 -0
- data/lib/cassandra/keyspace.rb +133 -0
- data/lib/cassandra/listener.rb +87 -0
- data/lib/cassandra/load_balancing/policies/dc_aware_round_robin.rb +149 -0
- data/lib/cassandra/load_balancing/policies/round_robin.rb +132 -0
- data/lib/cassandra/load_balancing/policies/token_aware.rb +119 -0
- data/lib/cassandra/load_balancing/policies/white_list.rb +90 -0
- data/lib/cassandra/load_balancing/policies.rb +19 -0
- data/lib/cassandra/load_balancing.rb +113 -0
- data/lib/cassandra/protocol/cql_byte_buffer.rb +307 -0
- data/lib/cassandra/protocol/cql_protocol_handler.rb +323 -0
- data/lib/cassandra/protocol/frame_decoder.rb +128 -0
- data/lib/cassandra/protocol/frame_encoder.rb +48 -0
- data/lib/cassandra/protocol/request.rb +38 -0
- data/lib/cassandra/protocol/requests/auth_response_request.rb +47 -0
- data/lib/cassandra/protocol/requests/batch_request.rb +76 -0
- data/lib/cassandra/protocol/requests/credentials_request.rb +47 -0
- data/lib/cassandra/protocol/requests/execute_request.rb +103 -0
- data/lib/cassandra/protocol/requests/options_request.rb +39 -0
- data/lib/cassandra/protocol/requests/prepare_request.rb +50 -0
- data/lib/cassandra/protocol/requests/query_request.rb +153 -0
- data/lib/cassandra/protocol/requests/register_request.rb +38 -0
- data/lib/cassandra/protocol/requests/startup_request.rb +49 -0
- data/lib/cassandra/protocol/requests/void_query_request.rb +24 -0
- data/lib/cassandra/protocol/response.rb +38 -0
- data/lib/cassandra/protocol/responses/auth_challenge_response.rb +41 -0
- data/lib/cassandra/protocol/responses/auth_success_response.rb +41 -0
- data/lib/cassandra/protocol/responses/authenticate_response.rb +41 -0
- data/lib/cassandra/protocol/responses/detailed_error_response.rb +60 -0
- data/lib/cassandra/protocol/responses/error_response.rb +50 -0
- data/lib/cassandra/protocol/responses/event_response.rb +39 -0
- data/lib/cassandra/protocol/responses/prepared_result_response.rb +64 -0
- data/lib/cassandra/protocol/responses/raw_rows_result_response.rb +43 -0
- data/lib/cassandra/protocol/responses/ready_response.rb +44 -0
- data/lib/cassandra/protocol/responses/result_response.rb +48 -0
- data/lib/cassandra/protocol/responses/rows_result_response.rb +139 -0
- data/lib/cassandra/protocol/responses/schema_change_event_response.rb +60 -0
- data/lib/cassandra/protocol/responses/schema_change_result_response.rb +57 -0
- data/lib/cassandra/protocol/responses/set_keyspace_result_response.rb +42 -0
- data/lib/cassandra/protocol/responses/status_change_event_response.rb +44 -0
- data/lib/cassandra/protocol/responses/supported_response.rb +41 -0
- data/lib/cassandra/protocol/responses/topology_change_event_response.rb +34 -0
- data/lib/cassandra/protocol/responses/void_result_response.rb +39 -0
- data/lib/cassandra/protocol/type_converter.rb +384 -0
- data/lib/cassandra/protocol.rb +93 -0
- data/lib/cassandra/reconnection/policies/constant.rb +48 -0
- data/lib/cassandra/reconnection/policies/exponential.rb +79 -0
- data/lib/cassandra/reconnection/policies.rb +20 -0
- data/lib/cassandra/reconnection.rb +49 -0
- data/lib/cassandra/result.rb +215 -0
- data/lib/cassandra/retry/policies/default.rb +47 -0
- data/lib/cassandra/retry/policies/downgrading_consistency.rb +71 -0
- data/lib/cassandra/retry/policies/fallthrough.rb +39 -0
- data/lib/cassandra/retry/policies.rb +21 -0
- data/lib/cassandra/retry.rb +142 -0
- data/lib/cassandra/session.rb +202 -0
- data/lib/cassandra/statement.rb +22 -0
- data/lib/cassandra/statements/batch.rb +95 -0
- data/lib/cassandra/statements/bound.rb +48 -0
- data/lib/cassandra/statements/prepared.rb +81 -0
- data/lib/cassandra/statements/simple.rb +58 -0
- data/lib/cassandra/statements/void.rb +33 -0
- data/lib/cassandra/statements.rb +23 -0
- data/lib/cassandra/table.rb +299 -0
- data/lib/cassandra/time_uuid.rb +142 -0
- data/lib/cassandra/util.rb +167 -0
- data/lib/cassandra/uuid.rb +104 -0
- data/lib/cassandra/version.rb +21 -0
- data/lib/cassandra.rb +428 -0
- data/lib/cassandra_murmur3.jar +0 -0
- metadata +211 -0
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
#--
|
|
4
|
+
# Copyright 2013-2014 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
|
+
module Protocol
|
|
21
|
+
# This class wraps a single connection and translates between request/
|
|
22
|
+
# response frames and raw bytes.
|
|
23
|
+
#
|
|
24
|
+
# You send requests with #send_request, and receive responses through the
|
|
25
|
+
# returned future.
|
|
26
|
+
#
|
|
27
|
+
# Instances of this class are thread safe.
|
|
28
|
+
#
|
|
29
|
+
# @example Sending an OPTIONS request
|
|
30
|
+
# future = protocol_handler.send_request(Cassandra::Protocol::OptionsRequest.new)
|
|
31
|
+
# response = future.get
|
|
32
|
+
# puts "These options are supported: #{response.options}"
|
|
33
|
+
#
|
|
34
|
+
class CqlProtocolHandler
|
|
35
|
+
# @return [String] the current keyspace for the underlying connection
|
|
36
|
+
attr_reader :keyspace
|
|
37
|
+
|
|
38
|
+
def initialize(connection, scheduler, protocol_version, compressor=nil)
|
|
39
|
+
@connection = connection
|
|
40
|
+
@scheduler = scheduler
|
|
41
|
+
@compressor = compressor
|
|
42
|
+
@connection.on_data(&method(:receive_data))
|
|
43
|
+
@connection.on_closed(&method(:socket_closed))
|
|
44
|
+
@promises = Array.new(128) { nil }
|
|
45
|
+
@read_buffer = CqlByteBuffer.new
|
|
46
|
+
@frame_encoder = FrameEncoder.new(protocol_version, @compressor)
|
|
47
|
+
@frame_decoder = FrameDecoder.new(@compressor)
|
|
48
|
+
@current_frame = FrameDecoder::NULL_FRAME
|
|
49
|
+
@request_queue_in = []
|
|
50
|
+
@request_queue_out = []
|
|
51
|
+
@event_listeners = []
|
|
52
|
+
@data = {}
|
|
53
|
+
@lock = Mutex.new
|
|
54
|
+
@closed_promise = Ione::Promise.new
|
|
55
|
+
@keyspace = nil
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Returns the hostname of the underlying connection
|
|
59
|
+
#
|
|
60
|
+
# @return [String] the hostname
|
|
61
|
+
def host
|
|
62
|
+
@connection.host
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Returns the port of the underlying connection
|
|
66
|
+
#
|
|
67
|
+
# @return [Integer] the port
|
|
68
|
+
def port
|
|
69
|
+
@connection.port
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Associate arbitrary data with this protocol handler object. This is
|
|
73
|
+
# useful in situations where additional metadata can be loaded after the
|
|
74
|
+
# connection has been set up, or to keep statistics specific to the
|
|
75
|
+
# connection this protocol handler wraps.
|
|
76
|
+
def []=(key, value)
|
|
77
|
+
@lock.lock
|
|
78
|
+
@data[key] = value
|
|
79
|
+
ensure
|
|
80
|
+
@lock.unlock
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# @see {#[]=}
|
|
84
|
+
# @return the value associated with the key
|
|
85
|
+
def [](key)
|
|
86
|
+
@lock.lock
|
|
87
|
+
@data[key]
|
|
88
|
+
ensure
|
|
89
|
+
@lock.unlock
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# @return [true, false] true if the underlying connection is connected
|
|
93
|
+
def connected?
|
|
94
|
+
@connection.connected?
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# @return [true, false] true if the underlying connection is closed
|
|
98
|
+
def closed?
|
|
99
|
+
@connection.closed?
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Register to receive notification when the underlying connection has
|
|
103
|
+
# closed. If the connection closed abruptly the error will be passed
|
|
104
|
+
# to the listener, otherwise it will not receive any parameters.
|
|
105
|
+
#
|
|
106
|
+
# @yieldparam error [nil, Error] the error that caused the connection to
|
|
107
|
+
# close, if any
|
|
108
|
+
def on_closed(&listener)
|
|
109
|
+
@closed_promise.future.on_value(&listener)
|
|
110
|
+
@closed_promise.future.on_failure(&listener)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Register to receive server sent events, like schema changes, nodes going
|
|
114
|
+
# up or down, etc. To actually receive events you also need to send a
|
|
115
|
+
# REGISTER request for the events you wish to receive.
|
|
116
|
+
#
|
|
117
|
+
# @yieldparam event [Cassandra::Protocol::EventResponse] an event sent by the server
|
|
118
|
+
def on_event(&listener)
|
|
119
|
+
@lock.lock
|
|
120
|
+
@event_listeners += [listener]
|
|
121
|
+
ensure
|
|
122
|
+
@lock.unlock
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Serializes and send a request over the underlying connection.
|
|
126
|
+
#
|
|
127
|
+
# Returns a future that will resolve to the response. When the connection
|
|
128
|
+
# closes the futures of all active requests will be failed with the error
|
|
129
|
+
# that caused the connection to close, or nil.
|
|
130
|
+
#
|
|
131
|
+
# When `timeout` is specified the future will fail with {Cassandra::TimeoutError}
|
|
132
|
+
# after that many seconds have passed. If a response arrives after that
|
|
133
|
+
# time it will be lost. If a response never arrives for the request the
|
|
134
|
+
# channel occupied by the request will _not_ be reused.
|
|
135
|
+
#
|
|
136
|
+
# @param [Cassandra::Protocol::Request] request
|
|
137
|
+
# @param [Float] timeout an optional number of seconds to wait until
|
|
138
|
+
# failing the request
|
|
139
|
+
# @return [Ione::Future<Cassandra::Protocol::Response>] a future that resolves to
|
|
140
|
+
# the response
|
|
141
|
+
def send_request(request, timeout=nil)
|
|
142
|
+
return Ione::Future.failed(Errors::NotConnectedError.new) if closed?
|
|
143
|
+
promise = RequestPromise.new(request, @frame_encoder)
|
|
144
|
+
id = nil
|
|
145
|
+
@lock.lock
|
|
146
|
+
begin
|
|
147
|
+
if (id = next_stream_id)
|
|
148
|
+
@promises[id] = promise
|
|
149
|
+
end
|
|
150
|
+
ensure
|
|
151
|
+
@lock.unlock
|
|
152
|
+
end
|
|
153
|
+
if id
|
|
154
|
+
@connection.write do |buffer|
|
|
155
|
+
@frame_encoder.encode_frame(request, id, buffer)
|
|
156
|
+
end
|
|
157
|
+
else
|
|
158
|
+
@lock.lock
|
|
159
|
+
begin
|
|
160
|
+
promise.encode_frame
|
|
161
|
+
@request_queue_in << promise
|
|
162
|
+
ensure
|
|
163
|
+
@lock.unlock
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
if timeout
|
|
167
|
+
@scheduler.schedule_timer(timeout).on_value do
|
|
168
|
+
promise.time_out!
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
promise.future
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Closes the underlying connection.
|
|
175
|
+
#
|
|
176
|
+
# @return [Ione::Future] a future that completes when the connection has closed
|
|
177
|
+
def close
|
|
178
|
+
@connection.close
|
|
179
|
+
@closed_promise.future
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
private
|
|
183
|
+
|
|
184
|
+
# @private
|
|
185
|
+
class RequestPromise < Ione::Promise
|
|
186
|
+
attr_reader :request, :frame
|
|
187
|
+
|
|
188
|
+
def initialize(request, frame_encoder)
|
|
189
|
+
@request = request
|
|
190
|
+
@frame_encoder = frame_encoder
|
|
191
|
+
@timed_out = false
|
|
192
|
+
super()
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def timed_out?
|
|
196
|
+
@timed_out
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def time_out!
|
|
200
|
+
unless future.completed?
|
|
201
|
+
@timed_out = true
|
|
202
|
+
fail(TimeoutError.new)
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def encode_frame
|
|
207
|
+
@frame = @frame_encoder.encode_frame(@request)
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def receive_data(data)
|
|
212
|
+
@read_buffer << data
|
|
213
|
+
@current_frame = @frame_decoder.decode_frame(@read_buffer, @current_frame)
|
|
214
|
+
while @current_frame.complete?
|
|
215
|
+
id = @current_frame.stream_id
|
|
216
|
+
if id == -1
|
|
217
|
+
notify_event_listeners(@current_frame.body)
|
|
218
|
+
else
|
|
219
|
+
complete_request(id, @current_frame.body)
|
|
220
|
+
end
|
|
221
|
+
@current_frame = @frame_decoder.decode_frame(@read_buffer)
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def notify_event_listeners(event_response)
|
|
226
|
+
event_listeners = nil
|
|
227
|
+
@lock.lock
|
|
228
|
+
begin
|
|
229
|
+
event_listeners = @event_listeners
|
|
230
|
+
return if event_listeners.empty?
|
|
231
|
+
ensure
|
|
232
|
+
@lock.unlock
|
|
233
|
+
end
|
|
234
|
+
event_listeners.each do |listener|
|
|
235
|
+
listener.call(@current_frame.body) rescue nil
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def complete_request(id, response)
|
|
240
|
+
promise = nil
|
|
241
|
+
@lock.lock
|
|
242
|
+
begin
|
|
243
|
+
promise = @promises[id]
|
|
244
|
+
@promises[id] = nil
|
|
245
|
+
ensure
|
|
246
|
+
@lock.unlock
|
|
247
|
+
end
|
|
248
|
+
if response.is_a?(Protocol::SetKeyspaceResultResponse)
|
|
249
|
+
@keyspace = response.keyspace
|
|
250
|
+
end
|
|
251
|
+
flush_request_queue
|
|
252
|
+
unless promise.timed_out?
|
|
253
|
+
promise.fulfill(response)
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def flush_request_queue
|
|
258
|
+
@lock.lock
|
|
259
|
+
begin
|
|
260
|
+
if @request_queue_out.empty? && !@request_queue_in.empty?
|
|
261
|
+
@request_queue_out = @request_queue_in
|
|
262
|
+
@request_queue_in = []
|
|
263
|
+
end
|
|
264
|
+
ensure
|
|
265
|
+
@lock.unlock
|
|
266
|
+
end
|
|
267
|
+
while true
|
|
268
|
+
id = nil
|
|
269
|
+
frame = nil
|
|
270
|
+
@lock.lock
|
|
271
|
+
begin
|
|
272
|
+
if @request_queue_out.any? && (id = next_stream_id)
|
|
273
|
+
promise = @request_queue_out.shift
|
|
274
|
+
if promise.timed_out?
|
|
275
|
+
next
|
|
276
|
+
else
|
|
277
|
+
frame = promise.frame
|
|
278
|
+
@promises[id] = promise
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
ensure
|
|
282
|
+
@lock.unlock
|
|
283
|
+
end
|
|
284
|
+
if id
|
|
285
|
+
@frame_encoder.change_stream_id(id, frame)
|
|
286
|
+
@connection.write(frame)
|
|
287
|
+
else
|
|
288
|
+
break
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
def socket_closed(cause)
|
|
294
|
+
request_failure_cause = cause || Io::ConnectionClosedError.new
|
|
295
|
+
promises_to_fail = nil
|
|
296
|
+
@lock.synchronize do
|
|
297
|
+
promises_to_fail = @promises.compact
|
|
298
|
+
promises_to_fail.concat(@request_queue_in)
|
|
299
|
+
promises_to_fail.concat(@request_queue_out)
|
|
300
|
+
@promises.fill(nil)
|
|
301
|
+
@request_queue_in.clear
|
|
302
|
+
@request_queue_out.clear
|
|
303
|
+
end
|
|
304
|
+
promises_to_fail.each do |promise|
|
|
305
|
+
promise.fail(request_failure_cause)
|
|
306
|
+
end
|
|
307
|
+
if cause
|
|
308
|
+
@closed_promise.fail(cause)
|
|
309
|
+
else
|
|
310
|
+
@closed_promise.fulfill
|
|
311
|
+
end
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
def next_stream_id
|
|
315
|
+
if (stream_id = @promises.index(nil))
|
|
316
|
+
stream_id
|
|
317
|
+
else
|
|
318
|
+
nil
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
end
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
#--
|
|
4
|
+
# Copyright 2013-2014 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
|
+
module Protocol
|
|
21
|
+
# @private
|
|
22
|
+
class FrameDecoder
|
|
23
|
+
def initialize(compressor=nil)
|
|
24
|
+
@compressor = compressor
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def decode_frame(buffer, partial_frame=nil)
|
|
28
|
+
partial_frame ||= NULL_FRAME
|
|
29
|
+
if partial_frame == NULL_FRAME
|
|
30
|
+
buffer_length = buffer.length
|
|
31
|
+
return NULL_FRAME if buffer_length < 8
|
|
32
|
+
fields = buffer.read_int
|
|
33
|
+
size = buffer.read_int
|
|
34
|
+
if buffer_length - 8 >= size
|
|
35
|
+
actual_decode(buffer, fields, size)
|
|
36
|
+
else
|
|
37
|
+
PartialFrame.new(fields, size)
|
|
38
|
+
end
|
|
39
|
+
elsif buffer.length >= partial_frame.size
|
|
40
|
+
actual_decode(buffer, partial_frame.fields, partial_frame.size)
|
|
41
|
+
else
|
|
42
|
+
partial_frame
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def actual_decode(buffer, fields, size)
|
|
49
|
+
if (fields >> 24) & 0x80 == 0
|
|
50
|
+
raise UnsupportedFrameTypeError, 'Request frames are not supported'
|
|
51
|
+
end
|
|
52
|
+
protocol_version = (fields >> 24) & 0x7f
|
|
53
|
+
compression = (fields >> 16) & 0x01
|
|
54
|
+
tracing = (fields >> 16) & 0x02
|
|
55
|
+
stream_id = (fields >> 8) & 0xff
|
|
56
|
+
stream_id = (stream_id & 0x7f) - (stream_id & 0x80)
|
|
57
|
+
opcode = fields & 0xff
|
|
58
|
+
if compression == 1
|
|
59
|
+
buffer = decompress(buffer, size)
|
|
60
|
+
size = buffer.size
|
|
61
|
+
end
|
|
62
|
+
if tracing == 2
|
|
63
|
+
trace_id = buffer.read_uuid
|
|
64
|
+
size -= 16
|
|
65
|
+
else
|
|
66
|
+
trace_id = nil
|
|
67
|
+
end
|
|
68
|
+
extra_length = buffer.length - size
|
|
69
|
+
response = Response.decode(opcode, protocol_version, buffer, size, trace_id)
|
|
70
|
+
if buffer.length > extra_length
|
|
71
|
+
buffer.discard(buffer.length - extra_length)
|
|
72
|
+
end
|
|
73
|
+
CompleteFrame.new(stream_id, response)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def decompress(buffer, size)
|
|
77
|
+
if @compressor
|
|
78
|
+
compressed_body = buffer.read(size)
|
|
79
|
+
CqlByteBuffer.new(@compressor.decompress(compressed_body))
|
|
80
|
+
else
|
|
81
|
+
raise UnexpectedCompressionError, 'Compressed frame received, but no compressor configured'
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
class NullFrame
|
|
86
|
+
def size
|
|
87
|
+
nil
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def complete?
|
|
91
|
+
false
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
class PartialFrame
|
|
96
|
+
attr_reader :fields, :size
|
|
97
|
+
|
|
98
|
+
def initialize(fields, size)
|
|
99
|
+
@fields = fields
|
|
100
|
+
@size = size
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def stream_id
|
|
104
|
+
nil
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def complete?
|
|
108
|
+
false
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
class CompleteFrame
|
|
113
|
+
attr_reader :stream_id, :body
|
|
114
|
+
|
|
115
|
+
def initialize(stream_id, body)
|
|
116
|
+
@stream_id = stream_id
|
|
117
|
+
@body = body
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def complete?
|
|
121
|
+
true
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
NULL_FRAME = NullFrame.new
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
#--
|
|
4
|
+
# Copyright 2013-2014 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
|
+
module Protocol
|
|
21
|
+
# @private
|
|
22
|
+
class FrameEncoder
|
|
23
|
+
def initialize(protocol_version=1, compressor=nil)
|
|
24
|
+
@protocol_version = protocol_version
|
|
25
|
+
@compressor = compressor
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def encode_frame(request, stream_id=0, buffer=nil)
|
|
29
|
+
raise InvalidStreamIdError, 'The stream ID must be between 0 and 127' unless 0 <= stream_id && stream_id < 128
|
|
30
|
+
buffer ||= CqlByteBuffer.new
|
|
31
|
+
flags = request.trace? ? 2 : 0
|
|
32
|
+
body = request.write(@protocol_version, CqlByteBuffer.new)
|
|
33
|
+
if @compressor && request.compressable? && @compressor.compress?(body)
|
|
34
|
+
flags |= 1
|
|
35
|
+
body = @compressor.compress(body)
|
|
36
|
+
end
|
|
37
|
+
header = [@protocol_version, flags, stream_id, request.opcode, body.bytesize]
|
|
38
|
+
buffer << header.pack(Formats::HEADER_FORMAT)
|
|
39
|
+
buffer << body
|
|
40
|
+
buffer
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def change_stream_id(new_stream_id, buffer, offset=0)
|
|
44
|
+
buffer.update(offset + 2, new_stream_id.chr)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
#--
|
|
4
|
+
# Copyright 2013-2014 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
|
+
module Protocol
|
|
21
|
+
class Request
|
|
22
|
+
attr_reader :opcode, :trace
|
|
23
|
+
|
|
24
|
+
def initialize(opcode, trace=false)
|
|
25
|
+
@opcode = opcode
|
|
26
|
+
@trace = trace
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def trace?
|
|
30
|
+
@trace
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def compressable?
|
|
34
|
+
true
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
#--
|
|
4
|
+
# Copyright 2013-2014 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
|
+
module Protocol
|
|
21
|
+
class AuthResponseRequest < Request
|
|
22
|
+
attr_reader :token
|
|
23
|
+
|
|
24
|
+
def initialize(token)
|
|
25
|
+
super(0x0f)
|
|
26
|
+
@token = token
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def write(protocol_version, buffer)
|
|
30
|
+
buffer.append_bytes(@token)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def to_s
|
|
34
|
+
%(AUTH_RESPONSE #{@token.bytesize})
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def eql?(other)
|
|
38
|
+
self.token == other.token
|
|
39
|
+
end
|
|
40
|
+
alias_method :==, :eql?
|
|
41
|
+
|
|
42
|
+
def hash
|
|
43
|
+
@token.hash
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
#--
|
|
4
|
+
# Copyright 2013-2014 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
|
+
module Protocol
|
|
21
|
+
class BatchRequest < Request
|
|
22
|
+
LOGGED_TYPE = 0
|
|
23
|
+
UNLOGGED_TYPE = 1
|
|
24
|
+
COUNTER_TYPE = 2
|
|
25
|
+
|
|
26
|
+
attr_reader :type, :part_count
|
|
27
|
+
attr_accessor :consistency, :retries
|
|
28
|
+
|
|
29
|
+
def initialize(type, consistency, trace=false)
|
|
30
|
+
super(0x0D, trace)
|
|
31
|
+
@type = type
|
|
32
|
+
@part_count = 0
|
|
33
|
+
@encoded_queries = CqlByteBuffer.new
|
|
34
|
+
@consistency = consistency
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def add_query(cql, values=nil, type_hints=nil)
|
|
38
|
+
@encoded_queries.append(QUERY_KIND)
|
|
39
|
+
@encoded_queries.append_long_string(cql)
|
|
40
|
+
QueryRequest.encode_values(@encoded_queries, values, type_hints)
|
|
41
|
+
@part_count += 1
|
|
42
|
+
nil
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def add_prepared(id, metadata, values)
|
|
46
|
+
@encoded_queries.append(PREPARED_KIND)
|
|
47
|
+
@encoded_queries.append_short_bytes(id)
|
|
48
|
+
ExecuteRequest.encode_values(@encoded_queries, metadata, values)
|
|
49
|
+
@part_count += 1
|
|
50
|
+
nil
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def write(protocol_version, buffer)
|
|
54
|
+
buffer.append(@type.chr)
|
|
55
|
+
buffer.append_short(@part_count)
|
|
56
|
+
buffer.append(@encoded_queries)
|
|
57
|
+
buffer.append_consistency(@consistency)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def to_s
|
|
61
|
+
type_str = case @type
|
|
62
|
+
when LOGGED_TYPE then 'LOGGED'
|
|
63
|
+
when UNLOGGED_TYPE then 'UNLOGGED'
|
|
64
|
+
when COUNTER_TYPE then 'COUNTER'
|
|
65
|
+
end
|
|
66
|
+
%(BATCH #{type_str} #{@part_count} #{@consistency.to_s.upcase})
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
TYPE_CONVERTER = TypeConverter.new
|
|
72
|
+
QUERY_KIND = "\x00".freeze
|
|
73
|
+
PREPARED_KIND = "\x01".freeze
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
#--
|
|
4
|
+
# Copyright 2013-2014 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
|
+
module Protocol
|
|
21
|
+
class CredentialsRequest < Request
|
|
22
|
+
attr_reader :credentials
|
|
23
|
+
|
|
24
|
+
def initialize(credentials)
|
|
25
|
+
super(4)
|
|
26
|
+
@credentials = credentials.dup.freeze
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def write(protocol_version, buffer)
|
|
30
|
+
buffer.append_string_map(@credentials)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def to_s
|
|
34
|
+
%(CREDENTIALS #{@credentials})
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def eql?(rq)
|
|
38
|
+
self.class === rq && rq.credentials.eql?(@credentials)
|
|
39
|
+
end
|
|
40
|
+
alias_method :==, :eql?
|
|
41
|
+
|
|
42
|
+
def hash
|
|
43
|
+
@h ||= @credentials.hash
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|