cql-rb 1.1.0.pre6 → 1.1.0.pre7
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 +15 -0
- data/lib/cql/client.rb +2 -0
- data/lib/cql/client/asynchronous_client.rb +11 -8
- data/lib/cql/client/asynchronous_prepared_statement.rb +9 -6
- data/lib/cql/client/connection_helper.rb +7 -4
- data/lib/cql/client/execute_options_decoder.rb +24 -0
- data/lib/cql/client/request_runner.rb +2 -2
- data/lib/cql/io/io_reactor.rb +2 -2
- data/lib/cql/protocol/cql_protocol_handler.rb +38 -6
- data/lib/cql/version.rb +1 -1
- data/spec/cql/client/asynchronous_client_spec.rb +26 -3
- data/spec/cql/client/asynchronous_prepared_statement_spec.rb +28 -10
- data/spec/cql/client/connection_helper_spec.rb +18 -2
- data/spec/cql/client/keyspace_changer_spec.rb +3 -3
- data/spec/cql/client/request_runner_spec.rb +8 -1
- data/spec/cql/io/io_reactor_spec.rb +8 -2
- data/spec/cql/protocol/cql_protocol_handler_spec.rb +49 -2
- data/spec/integration/client_spec.rb +1 -0
- data/spec/integration/io_spec.rb +4 -4
- data/spec/support/fake_io_reactor.rb +3 -3
- metadata +6 -7
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
N2VjM2JjM2ZiNjU3MTkzOTk4MTU2MjUyMWViZWIwYTExMjc4MTQ0MA==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
OWQ2NWYxY2UwN2YxNjM0NmYxZmRlMmViMmI3OGJlMDdmZTE2NDY3Zg==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
OTZkNmMzNjk5MDRhODdjZmNkMzljZDMxMzA2ZWQ1MWFjM2EyNWI3NmZmMjI2
|
10
|
+
OWZlMzY3NTczOTZiYzY2YmQ2ZWFmZWQ0ZTM1M2NjODBkOTI0MzMwODc2YmEx
|
11
|
+
OTBhNzdiZWNjYWY0YzA4NTQyNzc2NThhNTY2ZWM1ZDFmYmRjMDg=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
NGM4NmMyZDRhOTE3MTg4NmNjOGFmYmZhNmU5MWRiMTkzOWM4NzhlYzE2YTRk
|
14
|
+
ZGFmODJmMGNiMGRlNjFmZTNhMTZhYjdmYzhmOTMwNmQwOWFjMDc5NTk5Y2Jj
|
15
|
+
OTRiYWVkMWY2ZTYxMzUxMmRiODY4ZGQ5MzE3NWRkYzk5MTVlMGU=
|
data/lib/cql/client.rb
CHANGED
@@ -12,6 +12,7 @@ module Cql
|
|
12
12
|
end
|
13
13
|
|
14
14
|
NotConnectedError = Class.new(CqlError)
|
15
|
+
TimeoutError = Class.new(CqlError)
|
15
16
|
ClientError = Class.new(CqlError)
|
16
17
|
AuthenticationError = Class.new(ClientError)
|
17
18
|
|
@@ -171,6 +172,7 @@ require 'cql/client/null_logger'
|
|
171
172
|
require 'cql/client/column_metadata'
|
172
173
|
require 'cql/client/result_metadata'
|
173
174
|
require 'cql/client/query_result'
|
175
|
+
require 'cql/client/execute_options_decoder'
|
174
176
|
require 'cql/client/keyspace_changer'
|
175
177
|
require 'cql/client/asynchronous_client'
|
176
178
|
require 'cql/client/asynchronous_prepared_statement'
|
@@ -9,7 +9,6 @@ module Cql
|
|
9
9
|
@io_reactor = options[:io_reactor] || Io::IoReactor.new(Protocol::CqlProtocolHandler)
|
10
10
|
@hosts = extract_hosts(options)
|
11
11
|
@initial_keyspace = options[:keyspace]
|
12
|
-
@default_consistency = options[:default_consistency] || DEFAULT_CONSISTENCY
|
13
12
|
@lock = Mutex.new
|
14
13
|
@connected = false
|
15
14
|
@connecting = false
|
@@ -19,8 +18,11 @@ module Cql
|
|
19
18
|
@connection_manager = ConnectionManager.new
|
20
19
|
port = options[:port] || DEFAULT_PORT
|
21
20
|
credentials = options[:credentials]
|
21
|
+
connections_per_node = options[:connections_per_node] || 1
|
22
22
|
connection_timeout = options[:connection_timeout] || DEFAULT_CONNECTION_TIMEOUT
|
23
|
-
|
23
|
+
default_consistency = options[:default_consistency] || DEFAULT_CONSISTENCY
|
24
|
+
@execute_options_decoder = ExecuteOptionsDecoder.new(default_consistency)
|
25
|
+
@connection_helper = ConnectionHelper.new(@io_reactor, port, credentials, connections_per_node, connection_timeout, @logger)
|
24
26
|
end
|
25
27
|
|
26
28
|
def connect
|
@@ -87,15 +89,16 @@ module Cql
|
|
87
89
|
end
|
88
90
|
end
|
89
91
|
|
90
|
-
def execute(cql,
|
92
|
+
def execute(cql, options_or_consistency=nil)
|
91
93
|
with_failure_handler do
|
92
|
-
|
94
|
+
consistency, timeout = @execute_options_decoder.decode_options(options_or_consistency)
|
95
|
+
execute_request(Protocol::QueryRequest.new(cql, consistency), timeout)
|
93
96
|
end
|
94
97
|
end
|
95
98
|
|
96
99
|
def prepare(cql)
|
97
100
|
with_failure_handler do
|
98
|
-
AsynchronousPreparedStatement.prepare(cql, @
|
101
|
+
AsynchronousPreparedStatement.prepare(cql, @execute_options_decoder, @connection_manager, @logger)
|
99
102
|
end
|
100
103
|
end
|
101
104
|
|
@@ -161,7 +164,7 @@ module Cql
|
|
161
164
|
|
162
165
|
def register_event_listener(connection)
|
163
166
|
register_request = Protocol::RegisterRequest.new(Protocol::TopologyChangeEventResponse::TYPE, Protocol::StatusChangeEventResponse::TYPE)
|
164
|
-
execute_request(register_request, connection)
|
167
|
+
execute_request(register_request, nil, connection)
|
165
168
|
connection.on_closed do
|
166
169
|
if connected?
|
167
170
|
begin
|
@@ -198,8 +201,8 @@ module Cql
|
|
198
201
|
end
|
199
202
|
end
|
200
203
|
|
201
|
-
def execute_request(request, connection=nil)
|
202
|
-
f = @request_runner.execute(connection || @connection_manager.random_connection, request)
|
204
|
+
def execute_request(request, timeout=nil, connection=nil)
|
205
|
+
f = @request_runner.execute(connection || @connection_manager.random_connection, request, timeout)
|
203
206
|
f.map do |result|
|
204
207
|
if result.is_a?(KeyspaceChanged)
|
205
208
|
use(result.keyspace)
|
@@ -5,16 +5,16 @@ module Cql
|
|
5
5
|
# @private
|
6
6
|
class AsynchronousPreparedStatement < PreparedStatement
|
7
7
|
# @private
|
8
|
-
def initialize(cql,
|
8
|
+
def initialize(cql, execute_options_decoder, connection_manager, logger)
|
9
9
|
@cql = cql
|
10
|
-
@
|
10
|
+
@execute_options_decoder = execute_options_decoder
|
11
11
|
@connection_manager = connection_manager
|
12
12
|
@logger = logger
|
13
13
|
@request_runner = RequestRunner.new
|
14
14
|
end
|
15
15
|
|
16
|
-
def self.prepare(cql,
|
17
|
-
statement = new(cql,
|
16
|
+
def self.prepare(cql, execute_options_decoder, connection_manager, logger)
|
17
|
+
statement = new(cql, execute_options_decoder, connection_manager, logger)
|
18
18
|
futures = connection_manager.map do |connection|
|
19
19
|
statement.prepare(connection)
|
20
20
|
end
|
@@ -57,10 +57,13 @@ module Cql
|
|
57
57
|
def run(args, connection)
|
58
58
|
statement_id = connection[self]
|
59
59
|
bound_args = args.shift(@raw_metadata.size)
|
60
|
-
|
60
|
+
unless bound_args.size == @raw_metadata.size && args.size <= 1
|
61
|
+
raise ArgumentError, "Expected #{@raw_metadata.size} arguments, got #{bound_args.size}"
|
62
|
+
end
|
63
|
+
consistency, timeout = @execute_options_decoder.decode_options(args.last)
|
61
64
|
statement_id = connection[self]
|
62
65
|
request = Protocol::ExecuteRequest.new(statement_id, @raw_metadata, bound_args, consistency)
|
63
|
-
@request_runner.execute(connection, request)
|
66
|
+
@request_runner.execute(connection, request, timeout)
|
64
67
|
end
|
65
68
|
end
|
66
69
|
end
|
@@ -4,10 +4,11 @@ module Cql
|
|
4
4
|
module Client
|
5
5
|
# @private
|
6
6
|
class ConnectionHelper
|
7
|
-
def initialize(io_reactor, port, credentials, connection_timeout, logger)
|
7
|
+
def initialize(io_reactor, port, credentials, connections_per_node, connection_timeout, logger)
|
8
8
|
@io_reactor = io_reactor
|
9
9
|
@port = port
|
10
10
|
@credentials = credentials
|
11
|
+
@connections_per_node = connections_per_node
|
11
12
|
@connection_timeout = connection_timeout
|
12
13
|
@logger = logger
|
13
14
|
@request_runner = RequestRunner.new
|
@@ -67,9 +68,11 @@ module Cql
|
|
67
68
|
private
|
68
69
|
|
69
70
|
def connect_to_hosts(hosts, initial_keyspace, peer_discovery)
|
70
|
-
connection_futures = hosts.
|
71
|
-
|
72
|
-
|
71
|
+
connection_futures = hosts.flat_map do |host|
|
72
|
+
Array.new(@connections_per_node) do
|
73
|
+
connect_to_host(host, initial_keyspace).recover do |error|
|
74
|
+
FailedConnection.new(error, host, @port)
|
75
|
+
end
|
73
76
|
end
|
74
77
|
end
|
75
78
|
connection_futures.each do |cf|
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Cql
|
4
|
+
module Client
|
5
|
+
class ExecuteOptionsDecoder
|
6
|
+
def initialize(default_consistency)
|
7
|
+
@default_consistency = default_consistency
|
8
|
+
end
|
9
|
+
|
10
|
+
def decode_options(options_or_consistency)
|
11
|
+
consistency = @default_consistency
|
12
|
+
timeout = nil
|
13
|
+
case options_or_consistency
|
14
|
+
when Symbol
|
15
|
+
consistency = options_or_consistency
|
16
|
+
when Hash
|
17
|
+
consistency = options_or_consistency[:consistency] || consistency
|
18
|
+
timeout = options_or_consistency[:timeout]
|
19
|
+
end
|
20
|
+
return consistency, timeout
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -4,8 +4,8 @@ module Cql
|
|
4
4
|
module Client
|
5
5
|
# @private
|
6
6
|
class RequestRunner
|
7
|
-
def execute(connection, request)
|
8
|
-
connection.send_request(request).map do |response|
|
7
|
+
def execute(connection, request, timeout=nil)
|
8
|
+
connection.send_request(request, timeout).map do |response|
|
9
9
|
case response
|
10
10
|
when Protocol::RowsResultResponse
|
11
11
|
QueryResult.new(response.metadata, response.rows)
|
data/lib/cql/io/io_reactor.rb
CHANGED
@@ -32,7 +32,7 @@ module Cql
|
|
32
32
|
# @example A protocol handler that processes whole lines
|
33
33
|
#
|
34
34
|
# class LineProtocolHandler
|
35
|
-
# def initialize(connection)
|
35
|
+
# def initialize(connection, scheduler)
|
36
36
|
# @connection = connection
|
37
37
|
# # register a listener method for new data, this must be done in the
|
38
38
|
# # in the constructor, and only one listener can be registered
|
@@ -184,7 +184,7 @@ module Cql
|
|
184
184
|
def connect(host, port, timeout)
|
185
185
|
connection = Connection.new(host, port, timeout, @unblocker, @clock)
|
186
186
|
f = connection.connect
|
187
|
-
protocol_handler = @protocol_handler_factory.new(connection)
|
187
|
+
protocol_handler = @protocol_handler_factory.new(connection, self)
|
188
188
|
@io_loop.add_socket(connection)
|
189
189
|
@unblocker.unblock!
|
190
190
|
f.map { protocol_handler }
|
@@ -19,8 +19,9 @@ module Cql
|
|
19
19
|
# @return [String] the current keyspace for the underlying connection
|
20
20
|
attr_reader :keyspace
|
21
21
|
|
22
|
-
def initialize(connection)
|
22
|
+
def initialize(connection, scheduler)
|
23
23
|
@connection = connection
|
24
|
+
@scheduler = scheduler
|
24
25
|
@connection.on_data(&method(:receive_data))
|
25
26
|
@connection.on_closed(&method(:socket_closed))
|
26
27
|
@promises = Array.new(128) { nil }
|
@@ -98,11 +99,19 @@ module Cql
|
|
98
99
|
#
|
99
100
|
# Returns a future that will resolve to the response. When the connection
|
100
101
|
# closes the futures of all active requests will be failed with the error
|
101
|
-
# that caused the connection to close, or nil
|
102
|
+
# that caused the connection to close, or nil.
|
102
103
|
#
|
104
|
+
# When `timeout` is specified the future will fail with {Cql::TimeoutError}
|
105
|
+
# after that many seconds have passed. If a response arrives after that
|
106
|
+
# time it will be lost. If a response never arrives for the request the
|
107
|
+
# channel occupied by the request will _not_ be reused.
|
108
|
+
#
|
109
|
+
# @param [Cql::Protocol::Request] request
|
110
|
+
# @param [Float] timeout an optional number of seconds to wait until
|
111
|
+
# failing the request
|
103
112
|
# @return [Cql::Future<Cql::Protocol::Response>] a future that resolves to
|
104
113
|
# the response
|
105
|
-
def send_request(request)
|
114
|
+
def send_request(request, timeout=nil)
|
106
115
|
return Future.failed(NotConnectedError.new) if closed?
|
107
116
|
promise = RequestPromise.new(request)
|
108
117
|
id = nil
|
@@ -121,6 +130,11 @@ module Cql
|
|
121
130
|
@request_queue_in << promise
|
122
131
|
end
|
123
132
|
end
|
133
|
+
if timeout
|
134
|
+
@scheduler.schedule_timer(timeout).on_value do
|
135
|
+
promise.time_out!
|
136
|
+
end
|
137
|
+
end
|
124
138
|
promise.future
|
125
139
|
end
|
126
140
|
|
@@ -140,9 +154,21 @@ module Cql
|
|
140
154
|
|
141
155
|
def initialize(request)
|
142
156
|
@request = request
|
157
|
+
@timed_out = false
|
143
158
|
super()
|
144
159
|
end
|
145
160
|
|
161
|
+
def timed_out?
|
162
|
+
@timed_out
|
163
|
+
end
|
164
|
+
|
165
|
+
def time_out!
|
166
|
+
unless future.completed?
|
167
|
+
@timed_out = true
|
168
|
+
fail(TimeoutError.new)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
146
172
|
def encode_frame!
|
147
173
|
@frame = @request.encode_frame(0)
|
148
174
|
end
|
@@ -182,7 +208,9 @@ module Cql
|
|
182
208
|
if response.is_a?(Protocol::SetKeyspaceResultResponse)
|
183
209
|
@keyspace = response.keyspace
|
184
210
|
end
|
185
|
-
promise.
|
211
|
+
unless promise.timed_out?
|
212
|
+
promise.fulfill(response)
|
213
|
+
end
|
186
214
|
end
|
187
215
|
|
188
216
|
def flush_request_queue
|
@@ -198,8 +226,12 @@ module Cql
|
|
198
226
|
@lock.synchronize do
|
199
227
|
if @request_queue_out.any? && (id = next_stream_id)
|
200
228
|
promise = @request_queue_out.shift
|
201
|
-
|
202
|
-
|
229
|
+
if promise.timed_out?
|
230
|
+
id = nil
|
231
|
+
else
|
232
|
+
frame = promise.frame
|
233
|
+
@promises[id] = promise
|
234
|
+
end
|
203
235
|
end
|
204
236
|
end
|
205
237
|
if id
|
data/lib/cql/version.rb
CHANGED
@@ -44,10 +44,10 @@ module Cql
|
|
44
44
|
|
45
45
|
before do
|
46
46
|
io_reactor.on_connection do |connection|
|
47
|
-
connection.handle_request do |request|
|
47
|
+
connection.handle_request do |request, timeout|
|
48
48
|
response = nil
|
49
49
|
if @request_handler
|
50
|
-
response = @request_handler.call(request, connection, proc { connection.default_request_handler(request) })
|
50
|
+
response = @request_handler.call(request, connection, proc { connection.default_request_handler(request) }, timeout)
|
51
51
|
end
|
52
52
|
unless response
|
53
53
|
response = connection.default_request_handler(request)
|
@@ -170,6 +170,12 @@ module Cql
|
|
170
170
|
connections.should have(2).items
|
171
171
|
end
|
172
172
|
|
173
|
+
it 'connects to each host the specifie number of times' do
|
174
|
+
c = described_class.new(connection_options.merge(hosts: %w[h1.example.com h2.example.com], connections_per_node: 3))
|
175
|
+
c.connect.value
|
176
|
+
connections.should have(6).items
|
177
|
+
end
|
178
|
+
|
173
179
|
it 'succeeds even if only one of the connections succeeded' do
|
174
180
|
io_reactor.node_down('h1.example.com')
|
175
181
|
io_reactor.node_down('h3.example.com')
|
@@ -474,11 +480,16 @@ module Cql
|
|
474
480
|
last_request.should == Protocol::QueryRequest.new(cql, :all)
|
475
481
|
end
|
476
482
|
|
477
|
-
it 'uses the
|
483
|
+
it 'uses the consistency given as last argument' do
|
478
484
|
client.execute('UPDATE stuff SET thing = 1 WHERE id = 3', :three).value
|
479
485
|
last_request.should == Protocol::QueryRequest.new('UPDATE stuff SET thing = 1 WHERE id = 3', :three)
|
480
486
|
end
|
481
487
|
|
488
|
+
it 'uses the consistency given as an option' do
|
489
|
+
client.execute('UPDATE stuff SET thing = 1 WHERE id = 3', consistency: :local_quorum).value
|
490
|
+
last_request.should == Protocol::QueryRequest.new('UPDATE stuff SET thing = 1 WHERE id = 3', :local_quorum)
|
491
|
+
end
|
492
|
+
|
482
493
|
context 'with a void CQL query' do
|
483
494
|
it 'returns nil' do
|
484
495
|
handle_request do |request|
|
@@ -619,6 +630,18 @@ module Cql
|
|
619
630
|
end
|
620
631
|
end
|
621
632
|
end
|
633
|
+
|
634
|
+
context 'with a timeout' do
|
635
|
+
it 'passes the timeout along with the request' do
|
636
|
+
sent_timeout = nil
|
637
|
+
handle_request do |request, _, _, timeout|
|
638
|
+
sent_timeout = timeout
|
639
|
+
nil
|
640
|
+
end
|
641
|
+
client.execute(cql, timeout: 3).value
|
642
|
+
sent_timeout.should == 3
|
643
|
+
end
|
644
|
+
end
|
622
645
|
end
|
623
646
|
|
624
647
|
describe '#prepare' do
|
@@ -41,7 +41,7 @@ module Cql
|
|
41
41
|
]
|
42
42
|
end
|
43
43
|
|
44
|
-
def handle_request(connection, request)
|
44
|
+
def handle_request(connection, request, timeout)
|
45
45
|
case request
|
46
46
|
when Protocol::PrepareRequest
|
47
47
|
statement_id = [rand(2**31)].pack('c*')
|
@@ -56,14 +56,14 @@ module Cql
|
|
56
56
|
|
57
57
|
before do
|
58
58
|
connections.each do |c|
|
59
|
-
c.handle_request { |r| handle_request(c, r) }
|
59
|
+
c.handle_request { |r, t| handle_request(c, r, t) }
|
60
60
|
end
|
61
61
|
connection_manager.add_connections(connections)
|
62
62
|
end
|
63
63
|
|
64
64
|
describe '.prepare' do
|
65
65
|
it 'prepares a statement on all connections' do
|
66
|
-
f = described_class.prepare(cql, :one, connection_manager, logger)
|
66
|
+
f = described_class.prepare(cql, ExecuteOptionsDecoder.new(:one), connection_manager, logger)
|
67
67
|
f.value
|
68
68
|
connections.each do |c|
|
69
69
|
c.requests.should include(Protocol::PrepareRequest.new(cql))
|
@@ -71,13 +71,13 @@ module Cql
|
|
71
71
|
end
|
72
72
|
|
73
73
|
it 'returns a prepared statement object' do
|
74
|
-
f = described_class.prepare(cql, :two, connection_manager, logger)
|
74
|
+
f = described_class.prepare(cql, ExecuteOptionsDecoder.new(:two), connection_manager, logger)
|
75
75
|
f.value.should be_a(PreparedStatement)
|
76
76
|
end
|
77
77
|
|
78
78
|
it 'returns a failed future when something goes wrong in the preparation' do
|
79
79
|
connections.each(&:close)
|
80
|
-
f = described_class.prepare(cql, :three, connection_manager, logger)
|
80
|
+
f = described_class.prepare(cql, ExecuteOptionsDecoder.new(:three), connection_manager, logger)
|
81
81
|
expect { f.value }.to raise_error(NotConnectedError)
|
82
82
|
end
|
83
83
|
|
@@ -92,7 +92,7 @@ module Cql
|
|
92
92
|
|
93
93
|
describe '#metadata' do
|
94
94
|
let :statement do
|
95
|
-
described_class.prepare(cql, :all, connection_manager, logger).value
|
95
|
+
described_class.prepare(cql, ExecuteOptionsDecoder.new(:all), connection_manager, logger).value
|
96
96
|
end
|
97
97
|
|
98
98
|
it 'returns the interpreted metadata' do
|
@@ -103,7 +103,7 @@ module Cql
|
|
103
103
|
|
104
104
|
describe '#execute' do
|
105
105
|
let :statement do
|
106
|
-
described_class.prepare(cql, :local_quorum, connection_manager, logger).value
|
106
|
+
described_class.prepare(cql, ExecuteOptionsDecoder.new(:local_quorum), connection_manager, logger).value
|
107
107
|
end
|
108
108
|
|
109
109
|
it 'executes itself on one of the connections' do
|
@@ -132,6 +132,24 @@ module Cql
|
|
132
132
|
request.consistency.should == :two
|
133
133
|
end
|
134
134
|
|
135
|
+
it 'sends the consistency given as an option' do
|
136
|
+
statement.execute(11, 'hello', consistency: :two)
|
137
|
+
request = connections.flat_map(&:requests).find { |r| r.is_a?(Protocol::ExecuteRequest) }
|
138
|
+
request.consistency.should == :two
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'uses the specified timeout' do
|
142
|
+
sent_timeout = nil
|
143
|
+
connections.each do |c|
|
144
|
+
c.handle_request do |r, t|
|
145
|
+
sent_timeout = t
|
146
|
+
handle_request(c, r, t)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
statement.execute(11, 'hello', timeout: 3)
|
150
|
+
sent_timeout.should == 3
|
151
|
+
end
|
152
|
+
|
135
153
|
context 'when it receives a new connection from the connection manager' do
|
136
154
|
let :new_connection do
|
137
155
|
FakeConnection.new('h3.example.com', 1234, 5)
|
@@ -139,7 +157,7 @@ module Cql
|
|
139
157
|
|
140
158
|
before do
|
141
159
|
statement
|
142
|
-
new_connection.handle_request { |r| handle_request(new_connection, r) }
|
160
|
+
new_connection.handle_request { |r, t| handle_request(new_connection, r, t) }
|
143
161
|
connections.each(&:close)
|
144
162
|
connection_manager.add_connections([new_connection])
|
145
163
|
end
|
@@ -169,8 +187,8 @@ module Cql
|
|
169
187
|
it 'returns a failed future when the number of arguments is wrong' do
|
170
188
|
f1 = statement.execute(11, :one)
|
171
189
|
f2 = statement.execute(11, 'foo', 22, :one)
|
172
|
-
expect { f1.value }.to raise_error
|
173
|
-
expect { f2.value }.to raise_error
|
190
|
+
expect { f1.value }.to raise_error(NoMethodError)
|
191
|
+
expect { f2.value }.to raise_error(ArgumentError)
|
174
192
|
end
|
175
193
|
end
|
176
194
|
end
|
@@ -7,7 +7,7 @@ module Cql
|
|
7
7
|
module Client
|
8
8
|
describe ConnectionHelper do
|
9
9
|
let :connection_helper do
|
10
|
-
described_class.new(io_reactor, 9876, nil, 7, logger)
|
10
|
+
described_class.new(io_reactor, 9876, nil, 1, 7, logger)
|
11
11
|
end
|
12
12
|
|
13
13
|
let :io_reactor do
|
@@ -114,7 +114,7 @@ module Cql
|
|
114
114
|
|
115
115
|
it 'authenticates when authentication is required and credentials were specified' do
|
116
116
|
credentials = {'username' => 'foo', 'password' => 'bar'}
|
117
|
-
connection_helper = described_class.new(io_reactor, 9876, credentials, 7, logger)
|
117
|
+
connection_helper = described_class.new(io_reactor, 9876, credentials, 1, 7, logger)
|
118
118
|
connection = FakeConnection.new('host0', 9876, 7)
|
119
119
|
authentication_sent = false
|
120
120
|
connection.handle_request do |request|
|
@@ -178,6 +178,13 @@ module Cql
|
|
178
178
|
connection_helper.connect(hosts, 'some_keyspace')
|
179
179
|
connection_helper.should have_received(:discover_peers).with([connection], 'some_keyspace')
|
180
180
|
end
|
181
|
+
|
182
|
+
it 'connects to each node a configurable number of times' do
|
183
|
+
connection_helper = described_class.new(io_reactor, 9876, nil, connections_per_node = 3, 7, logger)
|
184
|
+
connection_helper.connect(hosts, nil)
|
185
|
+
io_reactor.should have_received(:connect).with('host0', 9876, 7).exactly(3).times
|
186
|
+
io_reactor.should have_received(:connect).with('host1', 9876, 7).exactly(3).times
|
187
|
+
end
|
181
188
|
end
|
182
189
|
|
183
190
|
describe '#discover_peers' do
|
@@ -329,6 +336,15 @@ module Cql
|
|
329
336
|
f = connection_helper.discover_peers(seed_connections, nil)
|
330
337
|
f.value
|
331
338
|
end
|
339
|
+
|
340
|
+
it 'connects to each node a configurable number of times' do
|
341
|
+
connection_helper = described_class.new(io_reactor, 9876, nil, connections_per_node = 3, 7, logger)
|
342
|
+
connection = FakeConnection.new('host3', 9876, 7)
|
343
|
+
io_reactor.stub(:connect).with('1.0.0.3', 9876, 7).and_return(Future.resolved(connection))
|
344
|
+
peer_request_response { seed_connection_rows + extra_connection_rows.take(1) }
|
345
|
+
connection_helper.discover_peers(seed_connections, nil).value
|
346
|
+
io_reactor.should have_received(:connect).with('1.0.0.3', 9876, 7).exactly(3).times
|
347
|
+
end
|
332
348
|
end
|
333
349
|
end
|
334
350
|
end
|
@@ -16,13 +16,13 @@ module Cql
|
|
16
16
|
|
17
17
|
describe '#use_keyspace' do
|
18
18
|
it 'sends a query request with a USE statement' do
|
19
|
-
connection.stub(:send_request).with(Protocol::QueryRequest.new('USE important_stuff', :one)).and_return(Future.resolved)
|
19
|
+
connection.stub(:send_request).with(Protocol::QueryRequest.new('USE important_stuff', :one), nil).and_return(Future.resolved)
|
20
20
|
f = keyspace_changer.use_keyspace('important_stuff', connection)
|
21
21
|
connection.should have_received(:send_request)
|
22
22
|
end
|
23
23
|
|
24
24
|
it 'accepts quoted keyspace names' do
|
25
|
-
connection.stub(:send_request).with(Protocol::QueryRequest.new('USE "ImportantStuff"', :one)).and_return(Future.resolved)
|
25
|
+
connection.stub(:send_request).with(Protocol::QueryRequest.new('USE "ImportantStuff"', :one), nil).and_return(Future.resolved)
|
26
26
|
f = keyspace_changer.use_keyspace('"ImportantStuff"', connection)
|
27
27
|
connection.should have_received(:send_request)
|
28
28
|
end
|
@@ -39,7 +39,7 @@ module Cql
|
|
39
39
|
end
|
40
40
|
|
41
41
|
it 'resolves to the given connection' do
|
42
|
-
connection.stub(:send_request).with(Protocol::QueryRequest.new('USE important_stuff', :one)).and_return(Future.resolved)
|
42
|
+
connection.stub(:send_request).with(Protocol::QueryRequest.new('USE important_stuff', :one), nil).and_return(Future.resolved)
|
43
43
|
f = keyspace_changer.use_keyspace('important_stuff', connection)
|
44
44
|
f.value.should equal(connection)
|
45
45
|
end
|
@@ -64,8 +64,15 @@ module Cql
|
|
64
64
|
end
|
65
65
|
|
66
66
|
it 'executes the request' do
|
67
|
-
connection.
|
67
|
+
connection.stub(:send_request).and_return(Future.resolved(rows_response))
|
68
68
|
runner.execute(connection, request)
|
69
|
+
connection.should have_received(:send_request)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'executes the request with the specified timeout' do
|
73
|
+
connection.stub(:send_request).and_return(Future.resolved(rows_response))
|
74
|
+
runner.execute(connection, request, 7)
|
75
|
+
connection.should have_received(:send_request).with(request, 7)
|
69
76
|
end
|
70
77
|
|
71
78
|
it 'transforms a RowsResultResponse to a query result' do
|
@@ -79,7 +79,7 @@ module Cql
|
|
79
79
|
|
80
80
|
it 'closes all sockets' do
|
81
81
|
connection = nil
|
82
|
-
protocol_handler_factory.stub(:new) do |sh|
|
82
|
+
protocol_handler_factory.stub(:new) do |sh, _|
|
83
83
|
connection = sh
|
84
84
|
double(:protocol_handler)
|
85
85
|
end
|
@@ -141,7 +141,7 @@ module Cql
|
|
141
141
|
end
|
142
142
|
|
143
143
|
before do
|
144
|
-
protocol_handler_factory.stub(:new) do |connection|
|
144
|
+
protocol_handler_factory.stub(:new) do |connection, _|
|
145
145
|
connection.to_io.stub(:connect_nonblock)
|
146
146
|
protocol_handler.stub(:connection).and_return(connection)
|
147
147
|
protocol_handler
|
@@ -165,6 +165,12 @@ module Cql
|
|
165
165
|
reactor.stop if reactor.running?
|
166
166
|
end
|
167
167
|
|
168
|
+
it 'calls #new on the protocol handler factory with the connection and the reactor itself' do
|
169
|
+
reactor.start.value
|
170
|
+
reactor.connect('example.com', 9999, 5).value
|
171
|
+
protocol_handler_factory.should have_received(:new).with(an_instance_of(Connection), reactor)
|
172
|
+
end
|
173
|
+
|
168
174
|
it 'returns a future that resolves to a new protocol handler' do
|
169
175
|
reactor.start.value
|
170
176
|
f = reactor.connect('example.com', 9999, 5)
|
@@ -7,13 +7,17 @@ module Cql
|
|
7
7
|
module Protocol
|
8
8
|
describe CqlProtocolHandler do
|
9
9
|
let :protocol_handler do
|
10
|
-
described_class.new(connection)
|
10
|
+
described_class.new(connection, scheduler)
|
11
11
|
end
|
12
12
|
|
13
13
|
let :connection do
|
14
14
|
double(:connection)
|
15
15
|
end
|
16
16
|
|
17
|
+
let :scheduler do
|
18
|
+
double(:scheduler)
|
19
|
+
end
|
20
|
+
|
17
21
|
let :request do
|
18
22
|
Protocol::OptionsRequest.new
|
19
23
|
end
|
@@ -32,6 +36,7 @@ module Cql
|
|
32
36
|
connection.stub(:on_connected) do |&h|
|
33
37
|
connection.stub(:connected_listener).and_return(h)
|
34
38
|
end
|
39
|
+
scheduler.stub(:schedule_timer).and_return(Promise.new.future)
|
35
40
|
protocol_handler
|
36
41
|
end
|
37
42
|
|
@@ -66,7 +71,6 @@ module Cql
|
|
66
71
|
before do
|
67
72
|
connection.stub(:write).and_yield(buffer)
|
68
73
|
connection.stub(:closed?).and_return(false)
|
69
|
-
|
70
74
|
connection.stub(:connected?).and_return(true)
|
71
75
|
end
|
72
76
|
|
@@ -151,6 +155,49 @@ module Cql
|
|
151
155
|
expect { f.value }.to raise_error(NotConnectedError)
|
152
156
|
end
|
153
157
|
end
|
158
|
+
|
159
|
+
context 'when the request times out' do
|
160
|
+
let :timer_promise do
|
161
|
+
Promise.new
|
162
|
+
end
|
163
|
+
|
164
|
+
before do
|
165
|
+
scheduler.stub(:schedule_timer).with(3).and_return(timer_promise.future)
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'raises a TimeoutError' do
|
169
|
+
f = protocol_handler.send_request(request, 3)
|
170
|
+
timer_promise.fulfill
|
171
|
+
expect { f.value }.to raise_error(TimeoutError)
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'does not attempt to fulfill the promise when the request has already timed out' do
|
175
|
+
f = protocol_handler.send_request(request, 3)
|
176
|
+
timer_promise.fulfill
|
177
|
+
expect { connection.data_listener.call([0x81, 0, 0, 2, 0].pack('C4N')) }.to_not raise_error
|
178
|
+
end
|
179
|
+
|
180
|
+
it 'never sends a request when it has already timed out' do
|
181
|
+
write_count = 0
|
182
|
+
connection.stub(:write) do |s, &h|
|
183
|
+
write_count += 1
|
184
|
+
if h
|
185
|
+
h.call(buffer)
|
186
|
+
else
|
187
|
+
buffer << s
|
188
|
+
end
|
189
|
+
end
|
190
|
+
128.times do
|
191
|
+
scheduler.stub(:schedule_timer).with(5).and_return(Promise.new.future)
|
192
|
+
protocol_handler.send_request(request)
|
193
|
+
end
|
194
|
+
scheduler.stub(:schedule_timer).with(5).and_return(timer_promise.future)
|
195
|
+
f = protocol_handler.send_request(request, 5)
|
196
|
+
timer_promise.fulfill
|
197
|
+
128.times { |i| connection.data_listener.call([0x81, 0, i, 2, 0].pack('C4N')) }
|
198
|
+
write_count.should == 128
|
199
|
+
end
|
200
|
+
end
|
154
201
|
end
|
155
202
|
|
156
203
|
describe '#close' do
|
data/spec/integration/io_spec.rb
CHANGED
@@ -124,7 +124,7 @@ end
|
|
124
124
|
|
125
125
|
module IoSpec
|
126
126
|
class TestConnection
|
127
|
-
def initialize(connection)
|
127
|
+
def initialize(connection, scheduler)
|
128
128
|
@connection = connection
|
129
129
|
@connection.on_data(&method(:receive_data))
|
130
130
|
@lock = Mutex.new
|
@@ -143,7 +143,7 @@ module IoSpec
|
|
143
143
|
end
|
144
144
|
|
145
145
|
class LineProtocolHandler
|
146
|
-
def initialize(connection)
|
146
|
+
def initialize(connection, scheduler)
|
147
147
|
@connection = connection
|
148
148
|
@connection.on_data(&method(:process_data))
|
149
149
|
@lock = Mutex.new
|
@@ -176,8 +176,8 @@ module IoSpec
|
|
176
176
|
end
|
177
177
|
|
178
178
|
class RedisProtocolHandler
|
179
|
-
def initialize(connection)
|
180
|
-
@line_protocol = LineProtocolHandler.new(connection)
|
179
|
+
def initialize(connection, scheduler)
|
180
|
+
@line_protocol = LineProtocolHandler.new(connection, scheduler)
|
181
181
|
@line_protocol.on_line(&method(:handle_line))
|
182
182
|
@lock = Mutex.new
|
183
183
|
@responses = []
|
@@ -127,7 +127,7 @@ class FakeConnection
|
|
127
127
|
@event_listeners.any? && @registered_event_types.any?
|
128
128
|
end
|
129
129
|
|
130
|
-
def send_request(request)
|
130
|
+
def send_request(request, timeout=nil)
|
131
131
|
if @closed
|
132
132
|
Cql::Future.failed(Cql::NotConnectedError.new)
|
133
133
|
else
|
@@ -136,7 +136,7 @@ class FakeConnection
|
|
136
136
|
when Cql::Protocol::RegisterRequest
|
137
137
|
@registered_event_types.concat(request.events)
|
138
138
|
end
|
139
|
-
response = @request_handler.call(request)
|
139
|
+
response = @request_handler.call(request, timeout)
|
140
140
|
if response.is_a?(Cql::Protocol::SetKeyspaceResultResponse)
|
141
141
|
@keyspace = response.keyspace
|
142
142
|
end
|
@@ -144,7 +144,7 @@ class FakeConnection
|
|
144
144
|
end
|
145
145
|
end
|
146
146
|
|
147
|
-
def default_request_handler(request)
|
147
|
+
def default_request_handler(request, timeout=nil)
|
148
148
|
response = @responses.shift
|
149
149
|
unless response
|
150
150
|
case request
|
metadata
CHANGED
@@ -1,15 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cql-rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.0.
|
5
|
-
prerelease: 6
|
4
|
+
version: 1.1.0.pre7
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Theo Hultberg
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2013-10-
|
11
|
+
date: 2013-10-07 00:00:00.000000000 Z
|
13
12
|
dependencies: []
|
14
13
|
description: A pure Ruby CQL3 driver for Cassandra
|
15
14
|
email:
|
@@ -24,6 +23,7 @@ files:
|
|
24
23
|
- lib/cql/client/column_metadata.rb
|
25
24
|
- lib/cql/client/connection_helper.rb
|
26
25
|
- lib/cql/client/connection_manager.rb
|
26
|
+
- lib/cql/client/execute_options_decoder.rb
|
27
27
|
- lib/cql/client/keyspace_changer.rb
|
28
28
|
- lib/cql/client/null_logger.rb
|
29
29
|
- lib/cql/client/query_result.rb
|
@@ -114,27 +114,26 @@ files:
|
|
114
114
|
homepage: http://github.com/iconara/cql-rb
|
115
115
|
licenses:
|
116
116
|
- Apache License 2.0
|
117
|
+
metadata: {}
|
117
118
|
post_install_message:
|
118
119
|
rdoc_options: []
|
119
120
|
require_paths:
|
120
121
|
- lib
|
121
122
|
required_ruby_version: !ruby/object:Gem::Requirement
|
122
|
-
none: false
|
123
123
|
requirements:
|
124
124
|
- - ! '>='
|
125
125
|
- !ruby/object:Gem::Version
|
126
126
|
version: 1.9.2
|
127
127
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
128
|
-
none: false
|
129
128
|
requirements:
|
130
129
|
- - ! '>'
|
131
130
|
- !ruby/object:Gem::Version
|
132
131
|
version: 1.3.1
|
133
132
|
requirements: []
|
134
133
|
rubyforge_project:
|
135
|
-
rubygems_version: 1.
|
134
|
+
rubygems_version: 2.1.5
|
136
135
|
signing_key:
|
137
|
-
specification_version:
|
136
|
+
specification_version: 4
|
138
137
|
summary: Cassandra CQL3 driver
|
139
138
|
test_files:
|
140
139
|
- spec/cql/byte_buffer_spec.rb
|