cassandra-driver 1.0.0 → 1.1.0

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.
@@ -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