cql-rb 1.2.2 → 2.0.0.pre0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.yardopts +4 -0
- data/README.md +139 -17
- data/lib/cql/client.rb +237 -8
- data/lib/cql/client/asynchronous_client.rb +138 -54
- data/lib/cql/client/asynchronous_prepared_statement.rb +41 -6
- data/lib/cql/client/authenticators.rb +46 -0
- data/lib/cql/client/batch.rb +115 -0
- data/lib/cql/client/connector.rb +255 -0
- data/lib/cql/client/execute_options_decoder.rb +25 -9
- data/lib/cql/client/keyspace_changer.rb +5 -5
- data/lib/cql/client/peer_discovery.rb +33 -0
- data/lib/cql/client/query_result.rb +124 -1
- data/lib/cql/client/request_runner.rb +4 -2
- data/lib/cql/client/synchronous_client.rb +14 -2
- data/lib/cql/client/synchronous_prepared_statement.rb +19 -1
- data/lib/cql/future.rb +97 -50
- data/lib/cql/io/connection.rb +0 -1
- data/lib/cql/io/io_reactor.rb +1 -1
- data/lib/cql/protocol.rb +8 -1
- data/lib/cql/protocol/cql_protocol_handler.rb +2 -2
- data/lib/cql/protocol/decoding.rb +10 -15
- data/lib/cql/protocol/frame_decoder.rb +2 -1
- data/lib/cql/protocol/frame_encoder.rb +5 -4
- data/lib/cql/protocol/requests/auth_response_request.rb +31 -0
- data/lib/cql/protocol/requests/batch_request.rb +59 -0
- data/lib/cql/protocol/requests/credentials_request.rb +1 -1
- data/lib/cql/protocol/requests/execute_request.rb +45 -17
- data/lib/cql/protocol/requests/options_request.rb +1 -1
- data/lib/cql/protocol/requests/prepare_request.rb +1 -1
- data/lib/cql/protocol/requests/query_request.rb +97 -5
- data/lib/cql/protocol/requests/register_request.rb +1 -1
- data/lib/cql/protocol/requests/startup_request.rb +4 -4
- data/lib/cql/protocol/response.rb +2 -2
- data/lib/cql/protocol/responses/auth_challenge_response.rb +25 -0
- data/lib/cql/protocol/responses/auth_success_response.rb +25 -0
- data/lib/cql/protocol/responses/authenticate_response.rb +1 -1
- data/lib/cql/protocol/responses/detailed_error_response.rb +1 -1
- data/lib/cql/protocol/responses/error_response.rb +3 -2
- data/lib/cql/protocol/responses/event_response.rb +3 -2
- data/lib/cql/protocol/responses/prepared_result_response.rb +10 -6
- data/lib/cql/protocol/responses/raw_rows_result_response.rb +27 -0
- data/lib/cql/protocol/responses/ready_response.rb +1 -1
- data/lib/cql/protocol/responses/result_response.rb +2 -2
- data/lib/cql/protocol/responses/rows_result_response.rb +43 -23
- data/lib/cql/protocol/responses/schema_change_event_response.rb +1 -1
- data/lib/cql/protocol/responses/schema_change_result_response.rb +1 -1
- data/lib/cql/protocol/responses/set_keyspace_result_response.rb +1 -1
- data/lib/cql/protocol/responses/status_change_event_response.rb +1 -1
- data/lib/cql/protocol/responses/supported_response.rb +1 -1
- data/lib/cql/protocol/responses/void_result_response.rb +1 -1
- data/lib/cql/protocol/type_converter.rb +2 -2
- data/lib/cql/uuid.rb +2 -2
- data/lib/cql/version.rb +1 -1
- data/spec/cql/client/asynchronous_client_spec.rb +493 -50
- data/spec/cql/client/asynchronous_prepared_statement_spec.rb +193 -11
- data/spec/cql/client/authenticators_spec.rb +56 -0
- data/spec/cql/client/batch_spec.rb +277 -0
- data/spec/cql/client/connector_spec.rb +606 -0
- data/spec/cql/client/execute_options_decoder_spec.rb +95 -0
- data/spec/cql/client/keyspace_changer_spec.rb +8 -8
- data/spec/cql/client/peer_discovery_spec.rb +92 -0
- data/spec/cql/client/query_result_spec.rb +352 -0
- data/spec/cql/client/request_runner_spec.rb +31 -5
- data/spec/cql/client/synchronous_client_spec.rb +44 -1
- data/spec/cql/client/synchronous_prepared_statement_spec.rb +63 -1
- data/spec/cql/future_spec.rb +50 -2
- data/spec/cql/protocol/cql_protocol_handler_spec.rb +16 -5
- data/spec/cql/protocol/decoding_spec.rb +16 -6
- data/spec/cql/protocol/encoding_spec.rb +3 -1
- data/spec/cql/protocol/frame_encoder_spec.rb +99 -50
- data/spec/cql/protocol/requests/auth_response_request_spec.rb +62 -0
- data/spec/cql/protocol/requests/batch_request_spec.rb +155 -0
- data/spec/cql/protocol/requests/credentials_request_spec.rb +1 -1
- data/spec/cql/protocol/requests/execute_request_spec.rb +184 -71
- data/spec/cql/protocol/requests/options_request_spec.rb +1 -1
- data/spec/cql/protocol/requests/prepare_request_spec.rb +1 -1
- data/spec/cql/protocol/requests/query_request_spec.rb +255 -32
- data/spec/cql/protocol/requests/register_request_spec.rb +1 -1
- data/spec/cql/protocol/requests/startup_request_spec.rb +12 -6
- data/spec/cql/protocol/responses/auth_challenge_response_spec.rb +31 -0
- data/spec/cql/protocol/responses/auth_success_response_spec.rb +31 -0
- data/spec/cql/protocol/responses/authenticate_response_spec.rb +2 -1
- data/spec/cql/protocol/responses/detailed_error_response_spec.rb +14 -7
- data/spec/cql/protocol/responses/error_response_spec.rb +4 -2
- data/spec/cql/protocol/responses/event_response_spec.rb +7 -4
- data/spec/cql/protocol/responses/prepared_result_response_spec.rb +89 -34
- data/spec/cql/protocol/responses/raw_rows_result_response_spec.rb +66 -0
- data/spec/cql/protocol/responses/ready_response_spec.rb +1 -1
- data/spec/cql/protocol/responses/result_response_spec.rb +19 -7
- data/spec/cql/protocol/responses/rows_result_response_spec.rb +56 -11
- data/spec/cql/protocol/responses/schema_change_event_response_spec.rb +2 -1
- data/spec/cql/protocol/responses/schema_change_result_response_spec.rb +2 -1
- data/spec/cql/protocol/responses/set_keyspace_result_response_spec.rb +1 -1
- data/spec/cql/protocol/responses/status_change_event_response_spec.rb +2 -1
- data/spec/cql/protocol/responses/supported_response_spec.rb +2 -1
- data/spec/cql/protocol/responses/topology_change_event_response_spec.rb +2 -1
- data/spec/cql/protocol/responses/void_result_response_spec.rb +1 -1
- data/spec/cql/protocol/type_converter_spec.rb +21 -4
- data/spec/cql/uuid_spec.rb +10 -3
- data/spec/integration/client_spec.rb +251 -28
- data/spec/integration/protocol_spec.rb +213 -62
- data/spec/integration/regression_spec.rb +4 -1
- data/spec/integration/uuid_spec.rb +4 -1
- data/spec/support/fake_io_reactor.rb +5 -5
- metadata +36 -7
- data/lib/cql/client/connection_helper.rb +0 -181
- data/spec/cql/client/connection_helper_spec.rb +0 -429
@@ -5,25 +5,26 @@ module Cql
|
|
5
5
|
# @private
|
6
6
|
class AsynchronousClient < Client
|
7
7
|
def initialize(options={})
|
8
|
-
compressor = options[:compressor]
|
8
|
+
@compressor = options[:compressor]
|
9
|
+
@cql_version = options[:cql_version]
|
9
10
|
@logger = options[:logger] || NullLogger.new
|
10
|
-
@
|
11
|
+
@protocol_version = options[:protocol_version] || 2
|
12
|
+
@io_reactor = options[:io_reactor] || Io::IoReactor.new(protocol_handler_factory)
|
11
13
|
@hosts = extract_hosts(options)
|
12
14
|
@initial_keyspace = options[:keyspace]
|
15
|
+
@connections_per_node = options[:connections_per_node] || 1
|
13
16
|
@lock = Mutex.new
|
14
|
-
@connected = false
|
15
|
-
@connecting = false
|
16
|
-
@closing = false
|
17
17
|
@request_runner = RequestRunner.new
|
18
18
|
@keyspace_changer = KeyspaceChanger.new
|
19
19
|
@connection_manager = ConnectionManager.new
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
@
|
26
|
-
@
|
20
|
+
@execute_options_decoder = ExecuteOptionsDecoder.new(options[:default_consistency] || DEFAULT_CONSISTENCY)
|
21
|
+
@port = options[:port] || DEFAULT_PORT
|
22
|
+
@connection_timeout = options[:connection_timeout] || DEFAULT_CONNECTION_TIMEOUT
|
23
|
+
@credentials = options[:credentials]
|
24
|
+
@auth_provider = options[:auth_provider] || @credentials && PlainTextAuthProvider.new(*@credentials.values_at(:username, :password))
|
25
|
+
@connected = false
|
26
|
+
@connecting = false
|
27
|
+
@closing = false
|
27
28
|
end
|
28
29
|
|
29
30
|
def connect
|
@@ -32,12 +33,15 @@ module Cql
|
|
32
33
|
return @connected_future if can_execute?
|
33
34
|
@connecting = true
|
34
35
|
@connected_future = begin
|
35
|
-
f = @
|
36
|
-
f.
|
36
|
+
f = @io_reactor.start
|
37
|
+
f = f.flat_map { connect_with_protocol_version_fallback }
|
38
|
+
f = f.flat_map { |connections| connect_to_all_peers(connections) }
|
39
|
+
f = f.flat_map do |connections|
|
37
40
|
@connection_manager.add_connections(connections)
|
38
41
|
register_event_listener(@connection_manager.random_connection)
|
39
42
|
end
|
40
|
-
f.
|
43
|
+
f = f.flat_map { use_keyspace(@connection_manager.snapshot, @initial_keyspace) }
|
44
|
+
f.map(self)
|
41
45
|
end
|
42
46
|
end
|
43
47
|
@connected_future.on_complete(&method(:connected))
|
@@ -49,15 +53,16 @@ module Cql
|
|
49
53
|
return @closed_future if @closing
|
50
54
|
@closing = true
|
51
55
|
@closed_future = begin
|
52
|
-
f = @io_reactor.stop
|
53
56
|
if @connecting
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
+
f = @connected_future.recover
|
58
|
+
f = f.flat_map { @io_reactor.stop }
|
59
|
+
f = f.map(self)
|
60
|
+
f
|
57
61
|
else
|
58
|
-
|
62
|
+
f = @io_reactor.stop
|
63
|
+
f = f.map(self)
|
64
|
+
f
|
59
65
|
end
|
60
|
-
ff.map { self }
|
61
66
|
end
|
62
67
|
end
|
63
68
|
@closed_future.on_complete(&method(:closed))
|
@@ -74,20 +79,25 @@ module Cql
|
|
74
79
|
|
75
80
|
def use(keyspace)
|
76
81
|
with_failure_handler do
|
77
|
-
connections = @connection_manager.
|
78
|
-
if connections.
|
79
|
-
|
80
|
-
Future.all(*futures).map { nil }
|
81
|
-
else
|
82
|
-
Future.resolved
|
83
|
-
end
|
82
|
+
connections = @connection_manager.reject { |c| c.keyspace == keyspace }
|
83
|
+
return Future.resolved if connections.empty?
|
84
|
+
use_keyspace(connections, keyspace).map(nil)
|
84
85
|
end
|
85
86
|
end
|
86
87
|
|
87
|
-
def execute(cql,
|
88
|
+
def execute(cql, *args)
|
88
89
|
with_failure_handler do
|
89
|
-
|
90
|
-
|
90
|
+
options_or_consistency = nil
|
91
|
+
if args.last.is_a?(Symbol) || args.last.is_a?(Hash)
|
92
|
+
options_or_consistency = args.pop
|
93
|
+
end
|
94
|
+
options = @execute_options_decoder.decode_options(options_or_consistency)
|
95
|
+
request = Protocol::QueryRequest.new(cql, args, options[:type_hints], options[:consistency], options[:serial_consistency], options[:page_size], options[:paging_state], options[:trace])
|
96
|
+
f = execute_request(request, options[:timeout])
|
97
|
+
if options.include?(:page_size)
|
98
|
+
f = f.map { |result| AsynchronousQueryPagedQueryResult.new(self, request, result, options) }
|
99
|
+
end
|
100
|
+
f
|
91
101
|
end
|
92
102
|
end
|
93
103
|
|
@@ -97,15 +107,32 @@ module Cql
|
|
97
107
|
end
|
98
108
|
end
|
99
109
|
|
110
|
+
def batch(type=:logged, options=nil)
|
111
|
+
if type.is_a?(Hash)
|
112
|
+
options = type
|
113
|
+
type = :logged
|
114
|
+
end
|
115
|
+
b = AsynchronousBatch.new(type, @execute_options_decoder, @connection_manager, options)
|
116
|
+
if block_given?
|
117
|
+
yield b
|
118
|
+
b.execute
|
119
|
+
else
|
120
|
+
b
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
100
124
|
private
|
101
125
|
|
126
|
+
DEFAULT_CQL_VERSIONS = {1 => '3.0.0'}
|
127
|
+
DEFAULT_CQL_VERSIONS.default = '3.1.0'
|
128
|
+
DEFAULT_CQL_VERSIONS.freeze
|
102
129
|
DEFAULT_CONSISTENCY = :quorum
|
103
130
|
DEFAULT_PORT = 9042
|
104
131
|
DEFAULT_CONNECTION_TIMEOUT = 10
|
105
132
|
MAX_RECONNECTION_ATTEMPTS = 5
|
106
133
|
|
107
|
-
def protocol_handler_factory
|
108
|
-
lambda { |connection, timeout| Protocol::CqlProtocolHandler.new(connection, timeout, compressor) }
|
134
|
+
def protocol_handler_factory
|
135
|
+
lambda { |connection, timeout| Protocol::CqlProtocolHandler.new(connection, timeout, @protocol_version, @compressor) }
|
109
136
|
end
|
110
137
|
|
111
138
|
def extract_hosts(options)
|
@@ -118,6 +145,52 @@ module Cql
|
|
118
145
|
end
|
119
146
|
end
|
120
147
|
|
148
|
+
def create_cluster_connector
|
149
|
+
cql_version = @cql_version || DEFAULT_CQL_VERSIONS[@protocol_version]
|
150
|
+
authentication_step = @protocol_version == 1 ? CredentialsAuthenticationStep.new(@credentials) : SaslAuthenticationStep.new(@auth_provider)
|
151
|
+
ClusterConnector.new(
|
152
|
+
Connector.new([
|
153
|
+
ConnectStep.new(@io_reactor, @port, @connection_timeout, @logger),
|
154
|
+
CacheOptionsStep.new,
|
155
|
+
InitializeStep.new(cql_version, @compressor, @logger),
|
156
|
+
authentication_step,
|
157
|
+
CachePropertiesStep.new,
|
158
|
+
]),
|
159
|
+
@logger
|
160
|
+
)
|
161
|
+
end
|
162
|
+
|
163
|
+
def connect_with_protocol_version_fallback
|
164
|
+
f = create_cluster_connector.connect_all(@hosts, @connections_per_node)
|
165
|
+
f.fallback do |error|
|
166
|
+
if error.is_a?(QueryError) && error.code == 0x0a && @protocol_version > 1
|
167
|
+
@logger.warn('Could not connect using protocol version %d (will try again with %d): %s' % [@protocol_version, @protocol_version - 1, error.message])
|
168
|
+
@protocol_version -= 1
|
169
|
+
connect_with_protocol_version_fallback
|
170
|
+
else
|
171
|
+
raise error
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def connect_to_all_peers(seed_connections, initial_keyspace=@initial_keyspace)
|
177
|
+
@logger.debug('Looking for additional nodes')
|
178
|
+
peer_discovery = PeerDiscovery.new(seed_connections)
|
179
|
+
peer_discovery.new_hosts.flat_map do |hosts|
|
180
|
+
if hosts.empty?
|
181
|
+
@logger.debug('No additional nodes found')
|
182
|
+
Future.resolved(seed_connections)
|
183
|
+
else
|
184
|
+
@logger.debug('%d additional nodes found' % hosts.size)
|
185
|
+
f = create_cluster_connector.connect_all(hosts, @connections_per_node)
|
186
|
+
f = f.map do |discovered_connections|
|
187
|
+
seed_connections + discovered_connections
|
188
|
+
end
|
189
|
+
f.recover(seed_connections)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
121
194
|
def connected(f)
|
122
195
|
if f.resolved?
|
123
196
|
@lock.synchronize do
|
@@ -163,40 +236,51 @@ module Cql
|
|
163
236
|
Future.failed(e)
|
164
237
|
end
|
165
238
|
|
239
|
+
def use_keyspace(connections, keyspace)
|
240
|
+
futures = connections.map { |connection| @keyspace_changer.use_keyspace(connection, keyspace) }
|
241
|
+
Future.all(*futures)
|
242
|
+
end
|
243
|
+
|
166
244
|
def register_event_listener(connection)
|
167
245
|
register_request = Protocol::RegisterRequest.new(Protocol::TopologyChangeEventResponse::TYPE, Protocol::StatusChangeEventResponse::TYPE)
|
168
|
-
execute_request(register_request, nil, connection)
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
246
|
+
f = execute_request(register_request, nil, connection)
|
247
|
+
f.on_value do
|
248
|
+
connection.on_closed do
|
249
|
+
if connected?
|
250
|
+
begin
|
251
|
+
register_event_listener(@connection_manager.random_connection)
|
252
|
+
rescue NotConnectedError
|
253
|
+
# we had started closing down after the connection check
|
254
|
+
end
|
175
255
|
end
|
176
256
|
end
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
257
|
+
connection.on_event do |event|
|
258
|
+
if event.change == 'UP' || event.change == 'NEW_NODE'
|
259
|
+
@logger.debug('Received %s event' % event.change)
|
260
|
+
unless @looking_for_nodes
|
261
|
+
@looking_for_nodes = true
|
262
|
+
handle_topology_change.on_complete do |f|
|
263
|
+
@looking_for_nodes = false
|
264
|
+
end
|
185
265
|
end
|
186
266
|
end
|
187
267
|
end
|
188
268
|
end
|
269
|
+
f
|
189
270
|
end
|
190
271
|
|
191
272
|
def handle_topology_change(remaning_attempts=MAX_RECONNECTION_ATTEMPTS)
|
192
273
|
with_failure_handler do
|
193
274
|
seed_connections = @connection_manager.snapshot
|
194
|
-
f =
|
195
|
-
f.flat_map do |
|
196
|
-
|
197
|
-
if
|
198
|
-
|
199
|
-
|
275
|
+
f = connect_to_all_peers(seed_connections, keyspace)
|
276
|
+
f.flat_map do |all_connections|
|
277
|
+
new_connections = all_connections - seed_connections
|
278
|
+
if new_connections.size > 0
|
279
|
+
f = use_keyspace(new_connections, keyspace)
|
280
|
+
f.on_value do
|
281
|
+
@connection_manager.add_connections(new_connections)
|
282
|
+
end
|
283
|
+
f
|
200
284
|
elsif remaning_attempts > 0
|
201
285
|
timeout = 2**(MAX_RECONNECTION_ATTEMPTS - remaning_attempts)
|
202
286
|
@logger.debug('Scheduling new peer discovery in %ds' % timeout)
|
@@ -18,7 +18,7 @@ module Cql
|
|
18
18
|
futures = connection_manager.map do |connection|
|
19
19
|
statement.prepare(connection)
|
20
20
|
end
|
21
|
-
Future.all(*futures).map
|
21
|
+
Future.all(*futures).map(statement)
|
22
22
|
rescue => e
|
23
23
|
Future.failed(e)
|
24
24
|
end
|
@@ -36,6 +36,21 @@ module Cql
|
|
36
36
|
Future.failed(e)
|
37
37
|
end
|
38
38
|
|
39
|
+
def batch(type=:logged, options=nil)
|
40
|
+
if type.is_a?(Hash)
|
41
|
+
options = type
|
42
|
+
type = :logged
|
43
|
+
end
|
44
|
+
b = AsynchronousBatch.new(type, @execute_options_decoder, @connection_manager, options)
|
45
|
+
pb = AsynchronousPreparedStatementBatch.new(self, b)
|
46
|
+
if block_given?
|
47
|
+
yield pb
|
48
|
+
pb.execute
|
49
|
+
else
|
50
|
+
pb
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
39
54
|
# @private
|
40
55
|
def prepare(connection)
|
41
56
|
prepare_request = Protocol::PrepareRequest.new(@cql)
|
@@ -46,25 +61,45 @@ module Cql
|
|
46
61
|
# is that we assign the same data multiple times
|
47
62
|
@raw_metadata = response.metadata
|
48
63
|
@metadata = ResultMetadata.new(@raw_metadata)
|
64
|
+
@raw_result_metadata = response.result_metadata
|
65
|
+
if @raw_result_metadata
|
66
|
+
@result_metadata = ResultMetadata.new(@raw_result_metadata)
|
67
|
+
end
|
49
68
|
end
|
50
69
|
hex_id = response.id.each_byte.map { |x| x.to_s(16).rjust(2, '0') }.join('')
|
51
70
|
@logger.debug('Statement %s prepared on node %s (%s:%d)' % [hex_id, connection[:host_id].to_s, connection.host, connection.port])
|
52
71
|
end
|
53
|
-
f.map
|
72
|
+
f.map(self)
|
73
|
+
end
|
74
|
+
|
75
|
+
# @private
|
76
|
+
def add_to_batch(batch, connection, bound_args)
|
77
|
+
statement_id = connection[self]
|
78
|
+
unless statement_id
|
79
|
+
raise NotPreparedError
|
80
|
+
end
|
81
|
+
unless bound_args.size == @raw_metadata.size
|
82
|
+
raise ArgumentError, "Expected #{@raw_metadata.size} arguments, got #{bound_args.size}"
|
83
|
+
end
|
84
|
+
batch.add_prepared(statement_id, @raw_metadata, bound_args)
|
54
85
|
end
|
55
86
|
|
56
87
|
private
|
57
88
|
|
58
89
|
def run(args, connection)
|
59
|
-
statement_id = connection[self]
|
60
90
|
bound_args = args.shift(@raw_metadata.size)
|
61
91
|
unless bound_args.size == @raw_metadata.size && args.size <= 1
|
62
92
|
raise ArgumentError, "Expected #{@raw_metadata.size} arguments, got #{bound_args.size}"
|
63
93
|
end
|
64
|
-
|
94
|
+
options = @execute_options_decoder.decode_options(args.last)
|
65
95
|
statement_id = connection[self]
|
66
|
-
|
67
|
-
|
96
|
+
request_metadata = @raw_result_metadata.nil?
|
97
|
+
request = Protocol::ExecuteRequest.new(statement_id, @raw_metadata, bound_args, request_metadata, options[:consistency], options[:serial_consistency], options[:page_size], options[:paging_state], options[:trace])
|
98
|
+
f = @request_runner.execute(connection, request, options[:timeout], @raw_result_metadata)
|
99
|
+
if options.include?(:page_size)
|
100
|
+
f = f.map { |result| AsynchronousPreparedPagedQueryResult.new(self, request, result, options) }
|
101
|
+
end
|
102
|
+
f
|
68
103
|
end
|
69
104
|
end
|
70
105
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Cql
|
4
|
+
module Client
|
5
|
+
# Auth provider used for Cassandra's built in authentication.
|
6
|
+
#
|
7
|
+
# There is no need to create instances of this class to pass as `:auth_provider`
|
8
|
+
# to {Cql::Client.connect}, instead use the `:credentials` option and one
|
9
|
+
# will be created automatically for you.
|
10
|
+
class PlainTextAuthProvider
|
11
|
+
def initialize(username, password)
|
12
|
+
@username = username
|
13
|
+
@password = password
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_authenticator(authentication_class)
|
17
|
+
if authentication_class == PASSWORD_AUTHENTICATOR_FQCN
|
18
|
+
PlainTextAuthenticator.new(@username, @password)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
PASSWORD_AUTHENTICATOR_FQCN = 'org.apache.cassandra.auth.PasswordAuthenticator'.freeze
|
25
|
+
end
|
26
|
+
|
27
|
+
# Authenticator used for Cassandra's built in authentication,
|
28
|
+
# see {Cql::Client::PlainTextAuthProvider}
|
29
|
+
class PlainTextAuthenticator
|
30
|
+
def initialize(username, password)
|
31
|
+
@username = username
|
32
|
+
@password = password
|
33
|
+
end
|
34
|
+
|
35
|
+
def initial_response
|
36
|
+
"\x00#{@username}\x00#{@password}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def challenge_response(token)
|
40
|
+
end
|
41
|
+
|
42
|
+
def authentication_successful(token)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Cql
|
4
|
+
module Client
|
5
|
+
# @private
|
6
|
+
class AsynchronousBatch < Batch
|
7
|
+
def initialize(type, execute_options_decoder, connection_manager, options=nil)
|
8
|
+
raise ArgumentError, "Unknown batch type: #{type}" unless BATCH_TYPES.include?(type)
|
9
|
+
@type = type
|
10
|
+
@execute_options_decoder = execute_options_decoder
|
11
|
+
@connection_manager = connection_manager
|
12
|
+
@options = options
|
13
|
+
@request_runner = RequestRunner.new
|
14
|
+
@parts = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def add(*args)
|
18
|
+
@parts << args
|
19
|
+
nil
|
20
|
+
end
|
21
|
+
|
22
|
+
def execute(options=nil)
|
23
|
+
options = @execute_options_decoder.decode_options(@options, options)
|
24
|
+
connection = nil
|
25
|
+
attempts = 0
|
26
|
+
begin
|
27
|
+
connection = @connection_manager.random_connection
|
28
|
+
request = Protocol::BatchRequest.new(BATCH_TYPES[@type], options[:consistency], options[:trace])
|
29
|
+
@parts.each do |cql_or_statement, *bound_args|
|
30
|
+
if cql_or_statement.is_a?(String)
|
31
|
+
type_hints = nil
|
32
|
+
if bound_args.last.is_a?(Hash) && bound_args.last.include?(:type_hints)
|
33
|
+
bound_args = bound_args.dup
|
34
|
+
type_hints = bound_args.pop[:type_hints]
|
35
|
+
end
|
36
|
+
request.add_query(cql_or_statement, bound_args, type_hints)
|
37
|
+
else
|
38
|
+
cql_or_statement.add_to_batch(request, connection, bound_args)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
rescue NotPreparedError
|
42
|
+
attempts += 1
|
43
|
+
if attempts < 3
|
44
|
+
retry
|
45
|
+
else
|
46
|
+
raise
|
47
|
+
end
|
48
|
+
end
|
49
|
+
@request_runner.execute(connection, request, options[:timeout])
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
BATCH_TYPES = {
|
55
|
+
:logged => Protocol::BatchRequest::LOGGED_TYPE,
|
56
|
+
:unlogged => Protocol::BatchRequest::UNLOGGED_TYPE,
|
57
|
+
:counter => Protocol::BatchRequest::COUNTER_TYPE,
|
58
|
+
}.freeze
|
59
|
+
end
|
60
|
+
|
61
|
+
# @private
|
62
|
+
class SynchronousBatch < Batch
|
63
|
+
include SynchronousBacktrace
|
64
|
+
|
65
|
+
def initialize(asynchronous_batch)
|
66
|
+
@asynchronous_batch = asynchronous_batch
|
67
|
+
end
|
68
|
+
|
69
|
+
def async
|
70
|
+
@asynchronous_batch
|
71
|
+
end
|
72
|
+
|
73
|
+
def add(*args)
|
74
|
+
@asynchronous_batch.add(*args)
|
75
|
+
end
|
76
|
+
|
77
|
+
def execute(options=nil)
|
78
|
+
synchronous_backtrace { @asynchronous_batch.execute(options).value }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# @private
|
83
|
+
class AsynchronousPreparedStatementBatch < PreparedStatementBatch
|
84
|
+
def initialize(prepared_statement, batch)
|
85
|
+
@prepared_statement = prepared_statement
|
86
|
+
@batch = batch
|
87
|
+
end
|
88
|
+
|
89
|
+
def add(*args)
|
90
|
+
@batch.add(@prepared_statement, *args)
|
91
|
+
end
|
92
|
+
|
93
|
+
def execute(options=nil)
|
94
|
+
@batch.execute(options)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# @private
|
99
|
+
class SynchronousPreparedStatementBatch < PreparedStatementBatch
|
100
|
+
include SynchronousBacktrace
|
101
|
+
|
102
|
+
def initialize(asynchronous_batch)
|
103
|
+
@asynchronous_batch = asynchronous_batch
|
104
|
+
end
|
105
|
+
|
106
|
+
def add(*args)
|
107
|
+
@asynchronous_batch.add(*args)
|
108
|
+
end
|
109
|
+
|
110
|
+
def execute(options=nil)
|
111
|
+
synchronous_backtrace { @asynchronous_batch.execute(options).value }
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|