cassandra-driver 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -140,7 +140,7 @@ module Cassandra
140
140
  end
141
141
 
142
142
  def host_down(host)
143
- manager = nil
143
+ pool = nil
144
144
 
145
145
  synchronize do
146
146
  return Ione::Future.resolved if !@connections.has_key?(host) && !@connecting_hosts.include?(host)
@@ -149,11 +149,11 @@ module Cassandra
149
149
  @prepared_statements.delete(host)
150
150
  @preparing_statements.delete(host)
151
151
 
152
- manager = @connections.delete(host)
152
+ pool = @connections.delete(host)
153
153
  end
154
154
 
155
- if manager
156
- Ione::Future.all(*manager.snapshot.map! {|c| c.close}).map(nil)
155
+ if pool
156
+ Ione::Future.all(*pool.snapshot.map! {|c| c.close}).map(nil)
157
157
  else
158
158
  Ione::Future.resolved
159
159
  end
@@ -171,8 +171,8 @@ module Cassandra
171
171
  end
172
172
 
173
173
 
174
- def query(statement, options, paging_state = nil)
175
- request = Protocol::QueryRequest.new(statement.cql, statement.params, nil, options.consistency, options.serial_consistency, options.page_size, paging_state, options.trace?)
174
+ def query(statement, options)
175
+ request = Protocol::QueryRequest.new(statement.cql, statement.params, nil, options.consistency, options.serial_consistency, options.page_size, options.paging_state, options.trace?)
176
176
  timeout = options.timeout
177
177
  promise = @futures.promise
178
178
 
@@ -198,10 +198,10 @@ module Cassandra
198
198
  promise.future
199
199
  end
200
200
 
201
- def execute(statement, options, paging_state = nil)
201
+ def execute(statement, options)
202
202
  timeout = options.timeout
203
203
  result_metadata = statement.result_metadata
204
- request = Protocol::ExecuteRequest.new(nil, statement.params_metadata, statement.params, result_metadata.nil?, options.consistency, options.serial_consistency, options.page_size, paging_state, options.trace?)
204
+ request = Protocol::ExecuteRequest.new(nil, statement.params_metadata, statement.params, result_metadata.nil?, options.consistency, options.serial_consistency, options.page_size, options.paging_state, options.trace?)
205
205
  promise = @futures.promise
206
206
 
207
207
  keyspace = @keyspace
@@ -345,19 +345,46 @@ module Cassandra
345
345
  return NO_CONNECTIONS
346
346
  end
347
347
 
348
- f = @connector.connect_many(host, pool_size)
348
+ pool = nil
349
+ existing_connections = 0
350
+
351
+ synchronize do
352
+ pool = @connections[host]
353
+ end
354
+
355
+ existing_connections = pool.size if pool
356
+ pool = nil
357
+ missing_connections = (pool_size - existing_connections)
358
+ return Ione::Future.resolved if missing_connections <= 0
359
+
360
+ f = @connector.connect_many(host, missing_connections)
349
361
 
350
362
  f.on_value do |connections|
351
- manager = nil
363
+ pool = nil
352
364
 
353
365
  synchronize do
354
366
  @connecting_hosts.delete(host)
355
367
  @prepared_statements[host] = {}
356
368
  @preparing_statements[host] = {}
357
- manager = @connections[host] ||= ConnectionPool.new
369
+ pool = @connections[host] ||= ConnectionPool.new
358
370
  end
359
371
 
360
- manager.add_connections(connections)
372
+ pool.add_connections(connections)
373
+
374
+ connections.each do |connection|
375
+ connection.on_closed do
376
+ distance = nil
377
+
378
+ synchronize do
379
+ if !(@state == :closed || @state == :closing) && !@connecting_hosts.include?(host) && @connections.include?(host)
380
+ distance = @load_balancing_policy.distance(host)
381
+ @connecting_hosts[host] = distance unless distance == :ignore
382
+ end
383
+ end
384
+
385
+ connect_to_host_maybe_retry(host, distance).map(nil) if distance
386
+ end
387
+ end
361
388
  end
362
389
 
363
390
  f
@@ -370,16 +397,16 @@ module Cassandra
370
397
  end
371
398
 
372
399
  hosts << host = plan.next
373
- manager = nil
374
- synchronize { manager = @connections[host] }
400
+ pool = nil
401
+ synchronize { pool = @connections[host] }
375
402
 
376
- unless manager
403
+ unless pool
377
404
  errors ||= {}
378
405
  errors[host] = NOT_CONNECTED
379
406
  return execute_by_plan(promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
380
407
  end
381
408
 
382
- connection = manager.random_connection
409
+ connection = pool.random_connection
383
410
 
384
411
  if keyspace && connection.keyspace != keyspace
385
412
  switch = switch_keyspace(connection, keyspace, timeout)
@@ -444,16 +471,16 @@ module Cassandra
444
471
  end
445
472
 
446
473
  hosts << host = plan.next
447
- manager = nil
448
- synchronize { manager = @connections[host] }
474
+ pool = nil
475
+ synchronize { pool = @connections[host] }
449
476
 
450
- unless manager
477
+ unless pool
451
478
  errors ||= {}
452
479
  errors[host] = NOT_CONNECTED
453
480
  return batch_by_plan(promise, keyspace, statement, options, plan, timeout, errors, hosts)
454
481
  end
455
482
 
456
- connection = manager.random_connection
483
+ connection = pool.random_connection
457
484
 
458
485
  if keyspace && connection.keyspace != keyspace
459
486
  switch = switch_keyspace(connection, keyspace, timeout)
@@ -543,16 +570,16 @@ module Cassandra
543
570
  end
544
571
 
545
572
  hosts << host = plan.next
546
- manager = nil
547
- synchronize { manager = @connections[host] }
573
+ pool = nil
574
+ synchronize { pool = @connections[host] }
548
575
 
549
- unless manager
576
+ unless pool
550
577
  errors ||= {}
551
578
  errors[host] = NOT_CONNECTED
552
579
  return send_request_by_plan(promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
553
580
  end
554
581
 
555
- connection = manager.random_connection
582
+ connection = pool.random_connection
556
583
 
557
584
  if keyspace && connection.keyspace != keyspace
558
585
  switch = switch_keyspace(connection, keyspace, timeout)
@@ -59,6 +59,12 @@ module Cassandra
59
59
  end
60
60
  end
61
61
 
62
+ def size
63
+ @lock.synchronize do
64
+ @connections.size
65
+ end
66
+ end
67
+
62
68
  def each_connection(&callback)
63
69
  return self unless block_given?
64
70
  raise Errors::IOError, 'Not connected' unless connected?
@@ -22,7 +22,7 @@ module Cassandra
22
22
  class ControlConnection
23
23
  include MonitorMixin
24
24
 
25
- def initialize(logger, io_reactor, cluster_registry, cluster_schema, cluster_metadata, load_balancing_policy, reconnection_policy, address_resolution_policy, connector)
25
+ def initialize(logger, io_reactor, cluster_registry, cluster_schema, cluster_metadata, load_balancing_policy, reconnection_policy, address_resolution_policy, connector, connection_options)
26
26
  @logger = logger
27
27
  @io_reactor = io_reactor
28
28
  @registry = cluster_registry
@@ -32,17 +32,24 @@ module Cassandra
32
32
  @reconnection_policy = reconnection_policy
33
33
  @address_resolver = address_resolution_policy
34
34
  @connector = connector
35
- @refreshing_statuses = Hash.new(false)
35
+ @connection_options = connection_options
36
+ @refreshing_statuses = ::Hash.new(false)
36
37
  @status = :closed
37
- @refreshing_schema = false
38
- @refreshing_keyspaces = Hash.new(false)
39
- @refreshing_tables = Hash.new
40
38
  @refreshing_hosts = false
41
- @refreshing_host = Hash.new(false)
39
+ @refreshing_host = ::Hash.new(false)
40
+ @closed_promise = Ione::Promise.new
41
+ @schema_changes = ::Array.new
42
+ @schema_refresh_timer = nil
43
+ @schema_refresh_window = nil
42
44
 
43
45
  mon_initialize
44
46
  end
45
47
 
48
+ def on_close(&block)
49
+ @closed_promise.future.on_value(&block)
50
+ @closed_promise.future.on_failure(&block)
51
+ end
52
+
46
53
  def connect_async
47
54
  synchronize do
48
55
  return Ione::Future.resolved if @status == :connecting || @status == :connected
@@ -110,10 +117,33 @@ module Cassandra
110
117
 
111
118
  def close_async
112
119
  synchronize do
113
- return Ione::Future.resolved if @status == :closing || @status == :closed
120
+ return @closed_promise.future if @status == :closing || @status == :closed
114
121
  @status = :closing
115
122
  end
116
- @io_reactor.stop
123
+ f = @io_reactor.stop
124
+
125
+ f.on_value(&method(:connection_closed))
126
+ f.on_failure(&method(:connection_closed))
127
+
128
+ @closed_promise.future
129
+ end
130
+
131
+ def connection_closed(cause)
132
+ @closed_promise.fulfill
133
+ end
134
+
135
+ def refresh_schema_async_maybe_retry
136
+ refresh_schema_async.fallback do |e|
137
+ case e
138
+ when Errors::HostError
139
+ refresh_schema_async_retry(e, @reconnection_policy.schedule)
140
+ else
141
+ connection = @connection
142
+ connection && connection.close(e)
143
+
144
+ Ione::Future.failed(e)
145
+ end
146
+ end
117
147
  end
118
148
 
119
149
  def inspect
@@ -127,11 +157,6 @@ module Cassandra
127
157
  SELECT_KEYSPACES = Protocol::QueryRequest.new('SELECT * FROM system.schema_keyspaces', nil, nil, :one)
128
158
  SELECT_TABLES = Protocol::QueryRequest.new('SELECT * FROM system.schema_columnfamilies', nil, nil, :one)
129
159
  SELECT_COLUMNS = Protocol::QueryRequest.new('SELECT * FROM system.schema_columns', nil, nil, :one)
130
- REGISTER = Protocol::RegisterRequest.new(
131
- Protocol::TopologyChangeEventResponse::TYPE,
132
- Protocol::StatusChangeEventResponse::TYPE,
133
- Protocol::SchemaChangeEventResponse::TYPE
134
- )
135
160
 
136
161
  def reconnect_async(schedule)
137
162
  timeout = schedule.next
@@ -163,7 +188,14 @@ module Cassandra
163
188
 
164
189
  return Ione::Future.failed(Errors::ClientError.new('Not connected')) if connection.nil?
165
190
 
166
- f = connection.send_request(REGISTER)
191
+ request = Protocol::RegisterRequest.new(
192
+ Protocol::TopologyChangeEventResponse::TYPE,
193
+ Protocol::StatusChangeEventResponse::TYPE
194
+ )
195
+
196
+ request.events << Protocol::SchemaChangeEventResponse::TYPE if @connection_options.synchronize_schema?
197
+
198
+ f = connection.send_request(request)
167
199
  f = f.map do |r|
168
200
  case r
169
201
  when Protocol::ReadyResponse
@@ -179,26 +211,7 @@ module Cassandra
179
211
  @logger.debug("Event received #{event}")
180
212
 
181
213
  if event.type == 'SCHEMA_CHANGE'
182
- case event.change
183
- when 'CREATED'
184
- if event.table.empty?
185
- refresh_schema_async_maybe_retry
186
- else
187
- refresh_keyspace_async_maybe_retry(event.keyspace)
188
- end
189
- when 'DROPPED'
190
- if event.table.empty?
191
- refresh_schema_async_maybe_retry
192
- else
193
- refresh_keyspace_async_maybe_retry(event.keyspace)
194
- end
195
- when 'UPDATED'
196
- if event.table.empty?
197
- refresh_keyspace_async_maybe_retry(event.keyspace)
198
- else
199
- refresh_table_async_maybe_retry(event.keyspace, event.table)
200
- end
201
- end
214
+ handle_schema_change(event)
202
215
  else
203
216
  case event.change
204
217
  when 'UP'
@@ -242,29 +255,6 @@ module Cassandra
242
255
  end
243
256
  end
244
257
 
245
- def refresh_schema_async_maybe_retry
246
- synchronize do
247
- return Ione::Future.resolved if @refreshing_schema
248
- @refreshing_schema = true
249
- end
250
-
251
- refresh_schema_async.fallback do |e|
252
- case e
253
- when Errors::HostError
254
- refresh_schema_async_retry(e, @reconnection_policy.schedule)
255
- else
256
- connection = @connection
257
- connection && connection.close(e)
258
-
259
- Ione::Future.resolved
260
- end
261
- end.map do
262
- synchronize do
263
- @refreshing_schema = false
264
- end
265
- end
266
- end
267
-
268
258
  def refresh_schema_async_retry(error, schedule)
269
259
  timeout = schedule.next
270
260
  @logger.info("Failed to refresh schema (#{error.class.name}: #{error.message}), retrying in #{timeout}")
@@ -279,18 +269,13 @@ module Cassandra
279
269
  connection = @connection
280
270
  connection && connection.close(e)
281
271
 
282
- Ione::Future.resolved
272
+ Ione::Future.failed(e)
283
273
  end
284
274
  end
285
275
  end
286
276
  end
287
277
 
288
278
  def refresh_keyspace_async_maybe_retry(keyspace)
289
- synchronize do
290
- return Ione::Future.resolved if @refreshing_schema || @refreshing_keyspaces[keyspace]
291
- @refreshing_keyspaces[keyspace] = true
292
- end
293
-
294
279
  refresh_keyspace_async(keyspace).fallback do |e|
295
280
  case e
296
281
  when Errors::HostError
@@ -299,11 +284,7 @@ module Cassandra
299
284
  connection = @connection
300
285
  connection && connection.close(e)
301
286
 
302
- Ione::Future.resolved
303
- end
304
- end.map do
305
- synchronize do
306
- @refreshing_keyspaces.delete(host)
287
+ Ione::Future.failed(e)
307
288
  end
308
289
  end
309
290
  end
@@ -322,7 +303,7 @@ module Cassandra
322
303
  connection = @connection
323
304
  connection && connection.close(e)
324
305
 
325
- Ione::Future.resolved
306
+ Ione::Future.failed(e)
326
307
  end
327
308
  end
328
309
  end
@@ -340,17 +321,15 @@ module Cassandra
340
321
  Ione::Future.all(keyspaces, tables, columns).map do |(keyspaces, tables, columns)|
341
322
  host = @registry.host(connection.host)
342
323
 
343
- @schema.update_keyspace(host, keyspaces.first, tables, columns) unless keyspaces.empty?
324
+ if keyspaces.empty?
325
+ @schema.delete_keyspace(keyspace)
326
+ else
327
+ @schema.update_keyspace(host, keyspaces.first, tables, columns)
328
+ end
344
329
  end
345
330
  end
346
331
 
347
332
  def refresh_table_async_maybe_retry(keyspace, table)
348
- synchronize do
349
- return Ione::Future.resolved if @refreshing_schema || @refreshing_keyspaces[keyspace] || @refreshing_tables[keyspace][table]
350
- @refreshing_tables[keyspace] ||= ::Hash.new(false)
351
- @refreshing_tables[keyspace][table] = true
352
- end
353
-
354
333
  refresh_table_async(keyspace, table).fallback do |e|
355
334
  case e
356
335
  when Errors::HostError
@@ -359,12 +338,7 @@ module Cassandra
359
338
  connection = @connection
360
339
  connection && connection.close(e)
361
340
 
362
- Ione::Future.resolved
363
- end
364
- end.map do
365
- synchronize do
366
- @refreshing_tables[keyspace].delete(table)
367
- @refreshing_tables.delete(keyspace) if @refreshing_tables[keyspace].empty?
341
+ Ione::Future.failed(e)
368
342
  end
369
343
  end
370
344
  end
@@ -383,7 +357,7 @@ module Cassandra
383
357
  connection = @connection
384
358
  connection && connection.close(e)
385
359
 
386
- Ione::Future.resolved
360
+ Ione::Future.failed(e)
387
361
  end
388
362
  end
389
363
  end
@@ -395,13 +369,17 @@ module Cassandra
395
369
  return Ione::Future.failed(Errors::ClientError.new('Not connected')) if connection.nil?
396
370
 
397
371
  params = [keyspace, table]
398
- table = send_select_request(connection, Protocol::QueryRequest.new("SELECT * FROM system.schema_columnfamilies WHERE keyspace_name = '%s' AND columnfamily_name = '%s'" % params, nil, nil, :one))
372
+ tables = send_select_request(connection, Protocol::QueryRequest.new("SELECT * FROM system.schema_columnfamilies WHERE keyspace_name = '%s' AND columnfamily_name = '%s'" % params, nil, nil, :one))
399
373
  columns = send_select_request(connection, Protocol::QueryRequest.new("SELECT * FROM system.schema_columns WHERE keyspace_name = '%s' AND columnfamily_name = '%s'" % params, nil, nil, :one))
400
374
 
401
- Ione::Future.all(table, columns).map do |(table, columns)|
375
+ Ione::Future.all(tables, columns).map do |(tables, columns)|
402
376
  host = @registry.host(connection.host)
403
377
 
404
- @schema.udpate_table(host, keyspace, table, columns)
378
+ if tables.empty?
379
+ @schema.delete_table(keyspace, table)
380
+ else
381
+ @schema.udpate_table(host, keyspace, tables.first, columns)
382
+ end
405
383
  end
406
384
  end
407
385
 
@@ -419,7 +397,7 @@ module Cassandra
419
397
  connection = @connection
420
398
  connection && connection.close(e)
421
399
 
422
- Ione::Future.resolved
400
+ Ione::Future.failed(e)
423
401
  end
424
402
  end.map do
425
403
  synchronize do
@@ -442,7 +420,7 @@ module Cassandra
442
420
  connection = @connection
443
421
  connection && connection.close(e)
444
422
 
445
- Ione::Future.resolved
423
+ Ione::Future.failed(e)
446
424
  end
447
425
  end
448
426
  end
@@ -468,6 +446,7 @@ module Cassandra
468
446
  @metadata.update(data)
469
447
  end
470
448
 
449
+ peers.shuffle!
471
450
  peers.each do |data|
472
451
  ip = peer_ip(data)
473
452
  next unless ip
@@ -524,7 +503,7 @@ module Cassandra
524
503
  connection = @connection
525
504
  connection && connection.close(e)
526
505
 
527
- Ione::Future.resolved
506
+ Ione::Future.failed(e)
528
507
  end
529
508
  end.map do
530
509
  synchronize do
@@ -547,7 +526,7 @@ module Cassandra
547
526
  connection = @connection
548
527
  connection && connection.close(e)
549
528
 
550
- Ione::Future.resolved
529
+ Ione::Future.failed(e)
551
530
  end
552
531
  end
553
532
  end
@@ -661,6 +640,92 @@ Control connection failed and is unlikely to recover.
661
640
  @address_resolver.resolve(ip)
662
641
  end
663
642
 
643
+ def process_schema_changes(schema_changes)
644
+ refresh_keyspaces = ::Hash.new
645
+ refresh_tables = ::Hash.new
646
+
647
+ schema_changes.each do |change|
648
+ keyspace = change.keyspace
649
+ table = change.table
650
+
651
+ next if refresh_keyspaces.has_key?(keyspace)
652
+
653
+ if table.empty?
654
+ refresh_tables.delete(keyspace)
655
+ refresh_keyspaces[keyspace] = true
656
+ else
657
+ tables = refresh_tables[keyspace] ||= ::Hash.new
658
+ tables[table] = true
659
+ end
660
+ end
661
+
662
+ futures = ::Array.new
663
+
664
+ refresh_keyspaces.each do |(keyspace, _)|
665
+ futures << refresh_keyspace_async_maybe_retry(keyspace)
666
+ end
667
+
668
+ refresh_tables.each do |(keyspace, tables)|
669
+ tables.each do |(table, _)|
670
+ futures << refresh_table_async_maybe_retry(keyspace, table)
671
+ end
672
+ end
673
+
674
+ Ione::Future.all(*futures)
675
+ end
676
+
677
+ def handle_schema_change(change)
678
+ timer = nil
679
+ expiration_timer = nil
680
+
681
+ synchronize do
682
+ @schema_changes << change
683
+
684
+ @io_reactor.cancel_timer(@schema_refresh_timer) if @schema_refresh_timer
685
+ timer = @schema_refresh_timer = @io_reactor.schedule_timer(@connection_options.schema_refresh_delay)
686
+
687
+ unless @schema_refresh_window
688
+ expiration_timer = @schema_refresh_window = @io_reactor.schedule_timer(@connection_options.schema_refresh_timeout)
689
+ end
690
+ end
691
+
692
+ if expiration_timer
693
+ expiration_timer.on_value do
694
+ schema_changes = nil
695
+
696
+ synchronize do
697
+ @io_reactor.cancel_timer(@schema_refresh_timer)
698
+
699
+ @schema_refresh_window = nil
700
+ @schema_refresh_timer = nil
701
+
702
+ schema_changes = @schema_changes
703
+ @schema_changes = ::Array.new
704
+ end
705
+
706
+ process_schema_changes(schema_changes)
707
+ end
708
+ end
709
+
710
+ timer.on_value do
711
+ schema_changes = nil
712
+
713
+ synchronize do
714
+ @io_reactor.cancel_timer(@schema_refresh_window)
715
+
716
+ @schema_refresh_window = nil
717
+ @schema_refresh_timer = nil
718
+
719
+ schema_changes = @schema_changes
720
+ @schema_changes = ::Array.new
721
+ end
722
+
723
+ process_schema_changes(schema_changes)
724
+ end
725
+
726
+ nil
727
+ end
728
+
664
729
  def send_select_request(connection, request)
665
730
  connection.send_request(request).map do |r|
666
731
  case r