cql-rb 1.0.6 → 1.1.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.
- data/README.md +4 -9
- data/lib/cql.rb +1 -0
- data/lib/cql/byte_buffer.rb +23 -7
- data/lib/cql/client.rb +11 -6
- data/lib/cql/client/asynchronous_client.rb +37 -83
- data/lib/cql/client/asynchronous_prepared_statement.rb +10 -4
- data/lib/cql/client/column_metadata.rb +16 -0
- data/lib/cql/client/request_runner.rb +46 -0
- data/lib/cql/future.rb +4 -5
- data/lib/cql/io.rb +2 -5
- data/lib/cql/io/connection.rb +220 -0
- data/lib/cql/io/io_reactor.rb +213 -185
- data/lib/cql/protocol.rb +1 -0
- data/lib/cql/protocol/cql_protocol_handler.rb +201 -0
- data/lib/cql/protocol/decoding.rb +6 -31
- data/lib/cql/protocol/encoding.rb +1 -5
- data/lib/cql/protocol/request.rb +4 -0
- data/lib/cql/protocol/responses/schema_change_result_response.rb +15 -0
- data/lib/cql/protocol/type_converter.rb +56 -76
- data/lib/cql/time_uuid.rb +104 -0
- data/lib/cql/uuid.rb +4 -2
- data/lib/cql/version.rb +1 -1
- data/spec/cql/client/asynchronous_client_spec.rb +47 -71
- data/spec/cql/client/asynchronous_prepared_statement_spec.rb +68 -0
- data/spec/cql/client/client_shared.rb +3 -3
- data/spec/cql/client/column_metadata_spec.rb +80 -0
- data/spec/cql/client/request_runner_spec.rb +120 -0
- data/spec/cql/future_spec.rb +26 -11
- data/spec/cql/io/connection_spec.rb +460 -0
- data/spec/cql/io/io_reactor_spec.rb +212 -265
- data/spec/cql/protocol/cql_protocol_handler_spec.rb +216 -0
- data/spec/cql/protocol/decoding_spec.rb +9 -28
- data/spec/cql/protocol/encoding_spec.rb +0 -5
- data/spec/cql/protocol/request_spec.rb +16 -0
- data/spec/cql/protocol/response_frame_spec.rb +2 -2
- data/spec/cql/protocol/responses/schema_change_result_response_spec.rb +70 -0
- data/spec/cql/time_uuid_spec.rb +136 -0
- data/spec/cql/uuid_spec.rb +1 -5
- data/spec/integration/client_spec.rb +34 -38
- data/spec/integration/io_spec.rb +283 -0
- data/spec/integration/protocol_spec.rb +53 -113
- data/spec/integration/regression_spec.rb +124 -0
- data/spec/integration/uuid_spec.rb +76 -0
- data/spec/spec_helper.rb +12 -9
- data/spec/support/fake_io_reactor.rb +52 -21
- data/spec/support/fake_server.rb +2 -2
- metadata +33 -10
- checksums.yaml +0 -15
- data/lib/cql/io/node_connection.rb +0 -209
- data/spec/cql/protocol/type_converter_spec.rb +0 -52
@@ -6,351 +6,298 @@ require 'spec_helper'
|
|
6
6
|
module Cql
|
7
7
|
module Io
|
8
8
|
describe IoReactor do
|
9
|
-
let :
|
10
|
-
|
9
|
+
let :reactor do
|
10
|
+
described_class.new(protocol_handler_factory, selector: selector)
|
11
11
|
end
|
12
12
|
|
13
|
-
let :
|
14
|
-
|
13
|
+
let :protocol_handler_factory do
|
14
|
+
stub(:protocol_handler_factory)
|
15
15
|
end
|
16
16
|
|
17
|
-
let :
|
18
|
-
|
17
|
+
let! :selector do
|
18
|
+
IoReactorSpec::FakeSelector.new
|
19
19
|
end
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
before do
|
26
|
-
server.start!
|
27
|
-
end
|
28
|
-
|
29
|
-
after do
|
30
|
-
begin
|
31
|
-
if io_reactor.running?
|
32
|
-
io_reactor.stop.get
|
33
|
-
end
|
34
|
-
ensure
|
35
|
-
server.stop!
|
21
|
+
describe '#start' do
|
22
|
+
after do
|
23
|
+
reactor.stop.get if reactor.running?
|
36
24
|
end
|
37
|
-
end
|
38
25
|
|
39
|
-
|
40
|
-
|
41
|
-
described_class.new
|
42
|
-
sleep(0.1)
|
43
|
-
server.connects.should == 0
|
26
|
+
it 'returns a future that completes when the reactor has started' do
|
27
|
+
reactor.start.get
|
44
28
|
end
|
45
|
-
end
|
46
29
|
|
47
|
-
|
48
|
-
|
49
|
-
io_reactor.should_not be_running
|
30
|
+
it 'returns a future that resolves to the reactor' do
|
31
|
+
reactor.start.get.should equal(reactor)
|
50
32
|
end
|
51
33
|
|
52
|
-
it 'is
|
53
|
-
|
54
|
-
|
34
|
+
it 'is running after being started' do
|
35
|
+
reactor.start.get
|
36
|
+
reactor.should be_running
|
55
37
|
end
|
56
38
|
|
57
|
-
it '
|
58
|
-
|
59
|
-
|
60
|
-
|
39
|
+
it 'cannot be started again once stopped' do
|
40
|
+
reactor.start.get
|
41
|
+
reactor.stop.get
|
42
|
+
expect { reactor.start }.to raise_error(ReactorError)
|
61
43
|
end
|
62
44
|
|
63
|
-
it '
|
64
|
-
|
65
|
-
|
66
|
-
|
45
|
+
it 'calls the selector' do
|
46
|
+
called = false
|
47
|
+
selector.handler { called = true; [[], [], []] }
|
48
|
+
reactor.start.get
|
49
|
+
await { called }
|
50
|
+
reactor.stop.get
|
51
|
+
called.should be_true, 'expected the selector to have been called'
|
67
52
|
end
|
68
53
|
end
|
69
54
|
|
70
55
|
describe '#stop' do
|
71
|
-
|
72
|
-
|
73
|
-
f1 = io_reactor.add_connection(host, port)
|
74
|
-
f2 = io_reactor.add_connection(host, port)
|
75
|
-
f3 = io_reactor.add_connection(host, port)
|
76
|
-
f4 = io_reactor.add_connection(host, port)
|
77
|
-
Future.combine(f1, f2, f3, f4).get
|
78
|
-
io_reactor.stop.get
|
79
|
-
server.await_disconnects!(4)
|
80
|
-
server.disconnects.should == 4
|
81
|
-
end
|
82
|
-
|
83
|
-
it 'succeeds connection futures when stopping while connecting' do
|
84
|
-
server.stop!
|
85
|
-
server.start!(accept_delay: 2)
|
86
|
-
f = io_reactor.add_connection(host, port)
|
87
|
-
io_reactor.start
|
88
|
-
io_reactor.stop
|
89
|
-
f.get
|
56
|
+
after do
|
57
|
+
reactor.stop.get if reactor.running?
|
90
58
|
end
|
91
|
-
end
|
92
59
|
|
93
|
-
|
94
|
-
|
95
|
-
|
60
|
+
it 'returns a future which completes when the reactor has stopped' do
|
61
|
+
reactor.start.get
|
62
|
+
reactor.stop.get
|
96
63
|
end
|
97
64
|
|
98
|
-
it '
|
99
|
-
|
100
|
-
|
101
|
-
server.await_connects!(1)
|
102
|
-
server.connects.should == 1
|
65
|
+
it 'returns a future which resolves to the reactor' do
|
66
|
+
reactor.start.get
|
67
|
+
reactor.stop.get.should equal(reactor)
|
103
68
|
end
|
104
69
|
|
105
|
-
it '
|
106
|
-
|
107
|
-
|
70
|
+
it 'is not running after being stopped' do
|
71
|
+
reactor.start.get
|
72
|
+
reactor.stop.get
|
73
|
+
reactor.should_not be_running
|
108
74
|
end
|
109
75
|
|
110
|
-
it '
|
111
|
-
|
112
|
-
|
76
|
+
it 'closes all sockets' do
|
77
|
+
connection = nil
|
78
|
+
protocol_handler_factory.stub(:new) do |sh|
|
79
|
+
connection = sh
|
80
|
+
stub(:protocol_handler)
|
81
|
+
end
|
82
|
+
reactor.start.get
|
83
|
+
reactor.connect('example.com', 9999, 5)
|
84
|
+
reactor.stop.get
|
85
|
+
connection.should be_closed
|
113
86
|
end
|
87
|
+
end
|
114
88
|
|
115
|
-
|
116
|
-
|
117
|
-
|
89
|
+
describe '#on_error' do
|
90
|
+
before do
|
91
|
+
selector.handler { raise 'Blurgh' }
|
118
92
|
end
|
119
93
|
|
120
|
-
it '
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
end
|
127
|
-
time_taken = (Time.now - started_at).to_f
|
128
|
-
time_taken.should be < 1.5
|
94
|
+
it 'calls the listeners when the reactor crashes' do
|
95
|
+
error = nil
|
96
|
+
reactor.on_error { |e| error = e }
|
97
|
+
reactor.start
|
98
|
+
await { error }
|
99
|
+
error.message.should == 'Blurgh'
|
129
100
|
end
|
130
101
|
|
131
|
-
it '
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
102
|
+
it 'calls the listener immediately when the reactor has already crashed' do
|
103
|
+
error = nil
|
104
|
+
reactor.start.get
|
105
|
+
await { !reactor.running? }
|
106
|
+
reactor.on_error { |e| error = e }
|
107
|
+
await { error }
|
137
108
|
end
|
138
|
-
end
|
139
109
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
await {
|
146
|
-
|
110
|
+
it 'ignores errors raised by listeners' do
|
111
|
+
called = false
|
112
|
+
reactor.on_error { raise 'Blurgh' }
|
113
|
+
reactor.on_error { called = true }
|
114
|
+
reactor.start
|
115
|
+
await { called }
|
116
|
+
called.should be_true, 'expected all close listeners to have been called'
|
147
117
|
end
|
118
|
+
end
|
148
119
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
io_reactor.add_connection(host, port).get
|
153
|
-
await { server.received_bytes.bytesize > 0 }
|
154
|
-
server.received_bytes[3, 1].should == "\x01"
|
120
|
+
describe '#connect' do
|
121
|
+
let :protocol_handler do
|
122
|
+
stub(:protocol_handler)
|
155
123
|
end
|
156
124
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
futures = 200.times.map do
|
164
|
-
io_reactor.queue_request(request)
|
165
|
-
end
|
166
|
-
|
167
|
-
128.times do |i|
|
168
|
-
server.broadcast!("\x81\x00#{[i].pack('c')}\b\x00\x00\x00\x04\x00\x00\x00\x01")
|
125
|
+
before do
|
126
|
+
protocol_handler_factory.stub(:new) do |connection|
|
127
|
+
connection.to_io.stub(:connect_nonblock)
|
128
|
+
protocol_handler.stub(:connection).and_return(connection)
|
129
|
+
protocol_handler
|
169
130
|
end
|
131
|
+
end
|
170
132
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
133
|
+
before do
|
134
|
+
selector.handler do |readables, writables, _, _|
|
135
|
+
writables.each do |writable|
|
136
|
+
fake_connected(writable)
|
137
|
+
end
|
138
|
+
[[], writables, []]
|
175
139
|
end
|
176
|
-
|
177
|
-
Future.combine(*futures).get
|
178
140
|
end
|
179
141
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
request = Cql::Protocol::StartupRequest.new
|
184
|
-
response = "\x81\x00\x00\x02\x00\x00\x00\x00"
|
185
|
-
|
186
|
-
io_reactor.start.get
|
187
|
-
c1_id = io_reactor.add_connection(host, port).get
|
188
|
-
c2_id = io_reactor.add_connection(host, port).get
|
189
|
-
q1_future = io_reactor.queue_request(request, c2_id)
|
190
|
-
q2_future = io_reactor.queue_request(request, c1_id)
|
142
|
+
def fake_connected(connection)
|
143
|
+
connection.to_io.stub(:connect_nonblock)
|
144
|
+
end
|
191
145
|
|
192
|
-
|
193
|
-
|
194
|
-
|
146
|
+
after do
|
147
|
+
reactor.stop if reactor.running?
|
148
|
+
end
|
195
149
|
|
196
|
-
|
197
|
-
|
150
|
+
it 'returns a future that resolves to a new protocol handler' do
|
151
|
+
reactor.start.get
|
152
|
+
f = reactor.connect('example.com', 9999, 5)
|
153
|
+
f.get.should equal(protocol_handler)
|
154
|
+
end
|
198
155
|
|
199
|
-
|
156
|
+
it 'returns a new protocol handler which wraps a socket handler' do
|
157
|
+
reactor.start.get
|
158
|
+
protocol_handler = reactor.connect('example.com', 9999, 5).get
|
159
|
+
protocol_handler.connection.should_not be_nil
|
160
|
+
protocol_handler.connection.host.should == 'example.com'
|
161
|
+
protocol_handler.connection.port.should == 9999
|
162
|
+
protocol_handler.connection.connection_timeout.should == 5
|
163
|
+
end
|
164
|
+
end
|
200
165
|
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
166
|
+
describe '#to_s' do
|
167
|
+
context 'returns a string that' do
|
168
|
+
it 'includes the class name' do
|
169
|
+
reactor.to_s.should include('Cql::Io::IoReactor')
|
205
170
|
end
|
206
171
|
|
207
|
-
it '
|
208
|
-
|
209
|
-
|
210
|
-
io_reactor.queue_request(Cql::Protocol::StartupRequest.new, 1234)
|
211
|
-
end
|
212
|
-
end
|
213
|
-
expect { f.get }.to raise_error(ConnectionNotFoundError)
|
172
|
+
it 'includes a list of its connections' do
|
173
|
+
reactor.to_s.should include('@connections=[')
|
174
|
+
reactor.to_s.should include('#<Cql::Io::Unblocker>')
|
214
175
|
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
215
179
|
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
connection_id = io_reactor.add_connection(host, port).get
|
221
|
-
|
222
|
-
futures = 200.times.map do
|
223
|
-
io_reactor.queue_request(request, connection_id)
|
224
|
-
end
|
225
|
-
|
226
|
-
128.times do |i|
|
227
|
-
server.broadcast!("\x81\x00#{[i].pack('c')}\b\x00\x00\x00\x04\x00\x00\x00\x01")
|
228
|
-
end
|
229
|
-
|
230
|
-
Future.combine(*futures.shift(128)).get
|
180
|
+
describe IoLoopBody do
|
181
|
+
let :loop_body do
|
182
|
+
described_class.new(selector: selector)
|
183
|
+
end
|
231
184
|
|
232
|
-
|
233
|
-
|
234
|
-
|
185
|
+
let :selector do
|
186
|
+
stub(:selector)
|
187
|
+
end
|
235
188
|
|
236
|
-
|
237
|
-
|
189
|
+
let :socket do
|
190
|
+
stub(:socket, connected?: false, connecting?: false, writable?: false, closed?: false)
|
191
|
+
end
|
238
192
|
|
239
|
-
|
240
|
-
|
193
|
+
describe '#tick' do
|
194
|
+
before do
|
195
|
+
loop_body.add_socket(socket)
|
196
|
+
end
|
241
197
|
|
242
|
-
|
243
|
-
|
198
|
+
it 'passes connected sockets as readables to the selector' do
|
199
|
+
socket.stub(:connected?).and_return(true)
|
200
|
+
selector.should_receive(:select).with([socket], anything, anything, anything).and_return([nil, nil, nil])
|
201
|
+
loop_body.tick
|
202
|
+
end
|
244
203
|
|
245
|
-
|
204
|
+
it 'passes writable sockets as writable to the selector' do
|
205
|
+
socket.stub(:writable?).and_return(true)
|
206
|
+
selector.should_receive(:select).with(anything, [socket], anything, anything).and_return([nil, nil, nil])
|
207
|
+
loop_body.tick
|
208
|
+
end
|
246
209
|
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
210
|
+
it 'passes connecting sockets as writable to the selector' do
|
211
|
+
socket.stub(:connecting?).and_return(true)
|
212
|
+
socket.stub(:connect)
|
213
|
+
selector.should_receive(:select).with(anything, [socket], anything, anything).and_return([nil, nil, nil])
|
214
|
+
loop_body.tick
|
215
|
+
end
|
253
216
|
|
254
|
-
|
217
|
+
it 'filters out closed sockets' do
|
218
|
+
socket.stub(:closed?).and_return(true)
|
219
|
+
selector.should_receive(:select).with([], [], anything, anything).and_return([nil, nil, nil])
|
220
|
+
loop_body.tick
|
221
|
+
socket.stub(:connected?).and_return(true)
|
222
|
+
selector.should_receive(:select).with([], [], anything, anything).and_return([nil, nil, nil])
|
223
|
+
loop_body.tick
|
224
|
+
end
|
255
225
|
|
256
|
-
|
226
|
+
it 'calls #read on all readable sockets returned by the selector' do
|
227
|
+
socket.stub(:connected?).and_return(true)
|
228
|
+
socket.should_receive(:read)
|
229
|
+
selector.stub(:select) do |r, w, _, _|
|
230
|
+
[[socket], nil, nil]
|
257
231
|
end
|
232
|
+
loop_body.tick
|
258
233
|
end
|
259
234
|
|
260
|
-
it '
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
f = io_reactor.queue_request(request)
|
266
|
-
expect { f.get }.to raise_error(Cql::ProtocolError)
|
235
|
+
it 'calls #connect on all connecting sockets' do
|
236
|
+
socket.stub(:connecting?).and_return(true)
|
237
|
+
socket.should_receive(:connect)
|
238
|
+
selector.stub(:select).and_return([nil, nil, nil])
|
239
|
+
loop_body.tick
|
267
240
|
end
|
268
241
|
|
269
|
-
it '
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
f.on_complete do |r, _|
|
275
|
-
response = r
|
242
|
+
it 'calls #flush on all writable sockets returned by the selector' do
|
243
|
+
socket.stub(:writable?).and_return(true)
|
244
|
+
socket.should_receive(:flush)
|
245
|
+
selector.stub(:select) do |r, w, _, _|
|
246
|
+
[nil, [socket], nil]
|
276
247
|
end
|
277
|
-
|
278
|
-
server.broadcast!("\x81\x00\x00\x02\x00\x00\x00\x00")
|
279
|
-
await { response }
|
280
|
-
response.should == Cql::Protocol::ReadyResponse.new
|
248
|
+
loop_body.tick
|
281
249
|
end
|
282
250
|
|
283
|
-
it '
|
284
|
-
|
285
|
-
|
286
|
-
io_reactor.add_connection(host, port).get
|
287
|
-
f = io_reactor.queue_request(Cql::Protocol::StartupRequest.new)
|
288
|
-
f.on_complete do |_, c|
|
289
|
-
connection = c
|
290
|
-
end
|
291
|
-
sleep(0.1)
|
292
|
-
server.broadcast!("\x81\x00\x00\x02\x00\x00\x00\x00")
|
293
|
-
await { connection }
|
294
|
-
connection.should_not be_nil
|
251
|
+
it 'allows the caller to specify a custom timeout' do
|
252
|
+
selector.should_receive(:select).with(anything, anything, anything, 99).and_return([[], [], []])
|
253
|
+
loop_body.tick(99)
|
295
254
|
end
|
296
255
|
end
|
297
256
|
|
298
|
-
describe '#
|
299
|
-
it '
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
event.should == Cql::Protocol::SchemaChangeEventResponse.new('DROPPED', 'keyspace01', 'users')
|
257
|
+
describe '#close_sockets' do
|
258
|
+
it 'closes all sockets' do
|
259
|
+
socket1 = stub(:socket1, closed?: false)
|
260
|
+
socket2 = stub(:socket2, closed?: false)
|
261
|
+
socket1.should_receive(:close)
|
262
|
+
socket2.should_receive(:close)
|
263
|
+
loop_body.add_socket(socket1)
|
264
|
+
loop_body.add_socket(socket2)
|
265
|
+
loop_body.close_sockets
|
308
266
|
end
|
309
|
-
end
|
310
267
|
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
server.broadcast!("\x01\x00\x00\x02\x00\x00\x00\x16")
|
320
|
-
expect { @request_future.get }.to raise_error(Protocol::UnsupportedFrameTypeError)
|
321
|
-
end
|
322
|
-
|
323
|
-
it 'does not kill the reactor' do
|
324
|
-
io_reactor.should be_running
|
325
|
-
end
|
326
|
-
|
327
|
-
it 'cleans out failed connections' do
|
328
|
-
pending 'There\'s some kind of race condition in the setup for this test'
|
329
|
-
f = io_reactor.queue_request(Protocol::QueryRequest.new('USE system', :one), @connection_id)
|
330
|
-
expect { f.get }.to raise_error(ConnectionNotFoundError)
|
331
|
-
end
|
268
|
+
it 'closes all sockets, even when one of them raises an error' do
|
269
|
+
socket1 = stub(:socket1, closed?: false)
|
270
|
+
socket2 = stub(:socket2, closed?: false)
|
271
|
+
socket1.stub(:close).and_raise('Blurgh')
|
272
|
+
socket2.should_receive(:close)
|
273
|
+
loop_body.add_socket(socket1)
|
274
|
+
loop_body.add_socket(socket2)
|
275
|
+
loop_body.close_sockets
|
332
276
|
end
|
333
277
|
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
@bad_request_future = io_reactor.queue_request(BadRequest.new)
|
340
|
-
end
|
341
|
-
|
342
|
-
it 'does not kill the reactor' do
|
343
|
-
@bad_request_future.get rescue nil
|
344
|
-
io_reactor.should be_running
|
345
|
-
end
|
278
|
+
it 'does not close already closed sockets' do
|
279
|
+
socket.stub(:closed?).and_return(true)
|
280
|
+
socket.should_not_receive(:close)
|
281
|
+
loop_body.add_socket(socket)
|
282
|
+
loop_body.close_sockets
|
346
283
|
end
|
347
284
|
end
|
348
285
|
end
|
286
|
+
end
|
287
|
+
end
|
349
288
|
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
289
|
+
module IoReactorSpec
|
290
|
+
class FakeSelector
|
291
|
+
def initialize
|
292
|
+
handler { [[], [], []] }
|
293
|
+
end
|
294
|
+
|
295
|
+
def handler(&body)
|
296
|
+
@body = body
|
297
|
+
end
|
298
|
+
|
299
|
+
def select(*args)
|
300
|
+
@body.call(*args)
|
354
301
|
end
|
355
302
|
end
|
356
303
|
end
|