cassandra-driver 2.1.7 → 3.0.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +8 -8
  2. data/README.md +31 -53
  3. data/lib/cassandra.rb +22 -3
  4. data/lib/cassandra/aggregate.rb +109 -0
  5. data/lib/cassandra/argument.rb +51 -0
  6. data/lib/cassandra/auth/providers/password.rb +7 -4
  7. data/lib/cassandra/cluster.rb +14 -3
  8. data/lib/cassandra/cluster/client.rb +56 -34
  9. data/lib/cassandra/cluster/connector.rb +6 -6
  10. data/lib/cassandra/cluster/control_connection.rb +204 -251
  11. data/lib/cassandra/cluster/metadata.rb +2 -0
  12. data/lib/cassandra/cluster/schema.rb +131 -209
  13. data/lib/cassandra/cluster/schema/cql_type_parser.rb +104 -0
  14. data/lib/cassandra/cluster/schema/fetchers.rb +1174 -0
  15. data/lib/cassandra/cluster/schema/{type_parser.rb → fqcn_type_parser.rb} +7 -3
  16. data/lib/cassandra/column.rb +2 -2
  17. data/lib/cassandra/driver.rb +27 -9
  18. data/lib/cassandra/errors.rb +179 -25
  19. data/lib/cassandra/execution/info.rb +8 -1
  20. data/lib/cassandra/execution/options.rb +34 -0
  21. data/lib/cassandra/execution/trace.rb +42 -10
  22. data/lib/cassandra/function.rb +150 -0
  23. data/lib/cassandra/future.rb +66 -35
  24. data/lib/cassandra/host.rb +7 -4
  25. data/lib/cassandra/keyspace.rb +112 -13
  26. data/lib/cassandra/load_balancing.rb +1 -1
  27. data/lib/cassandra/protocol.rb +9 -3
  28. data/lib/cassandra/protocol/coder.rb +434 -155
  29. data/lib/cassandra/protocol/cql_byte_buffer.rb +43 -0
  30. data/lib/cassandra/protocol/cql_protocol_handler.rb +4 -1
  31. data/lib/cassandra/protocol/request.rb +4 -0
  32. data/lib/cassandra/protocol/requests/auth_response_request.rb +5 -1
  33. data/lib/cassandra/protocol/requests/batch_request.rb +7 -2
  34. data/lib/cassandra/protocol/requests/credentials_request.rb +5 -1
  35. data/lib/cassandra/protocol/requests/execute_request.rb +16 -10
  36. data/lib/cassandra/protocol/requests/prepare_request.rb +12 -3
  37. data/lib/cassandra/protocol/requests/query_request.rb +20 -11
  38. data/lib/cassandra/protocol/responses/already_exists_error_response.rb +4 -4
  39. data/lib/cassandra/protocol/responses/error_response.rb +14 -14
  40. data/lib/cassandra/protocol/responses/function_failure_error_response.rb +41 -0
  41. data/lib/cassandra/protocol/responses/prepared_result_response.rb +12 -9
  42. data/lib/cassandra/protocol/responses/raw_rows_result_response.rb +5 -3
  43. data/lib/cassandra/protocol/responses/read_failure_error_response.rb +43 -0
  44. data/lib/cassandra/protocol/responses/read_timeout_error_response.rb +4 -4
  45. data/lib/cassandra/protocol/responses/ready_response.rb +5 -1
  46. data/lib/cassandra/protocol/responses/result_response.rb +3 -3
  47. data/lib/cassandra/protocol/responses/rows_result_response.rb +2 -2
  48. data/lib/cassandra/protocol/responses/schema_change_event_response.rb +25 -24
  49. data/lib/cassandra/protocol/responses/schema_change_result_response.rb +20 -23
  50. data/lib/cassandra/protocol/responses/set_keyspace_result_response.rb +2 -2
  51. data/lib/cassandra/protocol/responses/unavailable_error_response.rb +4 -4
  52. data/lib/cassandra/protocol/responses/unprepared_error_response.rb +4 -4
  53. data/lib/cassandra/protocol/responses/write_failure_error_response.rb +45 -0
  54. data/lib/cassandra/protocol/responses/write_timeout_error_response.rb +4 -4
  55. data/lib/cassandra/protocol/v1.rb +38 -13
  56. data/lib/cassandra/protocol/v3.rb +34 -29
  57. data/lib/cassandra/protocol/v4.rb +334 -0
  58. data/lib/cassandra/result.rb +10 -9
  59. data/lib/cassandra/retry.rb +17 -3
  60. data/lib/cassandra/retry/policies/default.rb +9 -3
  61. data/lib/cassandra/session.rb +15 -7
  62. data/lib/cassandra/statement.rb +5 -0
  63. data/lib/cassandra/statements/batch.rb +36 -12
  64. data/lib/cassandra/statements/bound.rb +2 -1
  65. data/lib/cassandra/statements/prepared.rb +106 -35
  66. data/lib/cassandra/statements/simple.rb +4 -2
  67. data/lib/cassandra/table.rb +70 -105
  68. data/lib/cassandra/time.rb +98 -0
  69. data/lib/cassandra/time_uuid.rb +1 -1
  70. data/lib/cassandra/tuple.rb +7 -0
  71. data/lib/cassandra/types.rb +472 -272
  72. data/lib/cassandra/udt.rb +10 -0
  73. data/lib/cassandra/util.rb +32 -1
  74. data/lib/cassandra/uuid.rb +6 -1
  75. data/lib/cassandra/uuid/generator.rb +7 -7
  76. data/lib/cassandra/version.rb +1 -1
  77. data/lib/datastax/cassandra.rb +5 -2
  78. metadata +16 -6
@@ -208,8 +208,10 @@ module Cassandra
208
208
  return @futures.error(Errors::ClientError.new("Positional arguments are not supported by the current version of Apache Cassandra")) if !statement.params.empty? && @connection_options.protocol_version == 1
209
209
 
210
210
  timestamp = nil
211
- timestamp = Time.now if @connection_options.client_timestamps? && @connection_options.protocol_version > 2
212
- request = Protocol::QueryRequest.new(statement.cql, statement.params, statement.params_types, options.consistency, options.serial_consistency, options.page_size, options.paging_state, options.trace?, statement.params_names, timestamp)
211
+ timestamp = ::Time.now if @connection_options.client_timestamps? && @connection_options.protocol_version > 2
212
+ payload = nil
213
+ payload = options.payload if @connection_options.protocol_version >= 4
214
+ request = Protocol::QueryRequest.new(statement.cql, statement.params, statement.params_types, options.consistency, options.serial_consistency, options.page_size, options.paging_state, options.trace?, statement.params_names, timestamp, payload)
213
215
  timeout = options.timeout
214
216
  promise = @futures.promise
215
217
 
@@ -222,7 +224,9 @@ module Cassandra
222
224
  end
223
225
 
224
226
  def prepare(cql, options)
225
- request = Protocol::PrepareRequest.new(cql, options.trace?)
227
+ payload = nil
228
+ payload = options.payload if @connection_options.protocol_version >= 4
229
+ request = Protocol::PrepareRequest.new(cql, options.trace?, payload)
226
230
  timeout = options.timeout
227
231
  promise = @futures.promise
228
232
 
@@ -237,10 +241,12 @@ module Cassandra
237
241
 
238
242
  def execute(statement, options)
239
243
  timestamp = nil
240
- timestamp = Time.now if @connection_options.client_timestamps? && @connection_options.protocol_version > 2
244
+ timestamp = ::Time.now if @connection_options.client_timestamps? && @connection_options.protocol_version > 2
245
+ payload = nil
246
+ payload = options.payload if @connection_options.protocol_version >= 4
241
247
  timeout = options.timeout
242
248
  result_metadata = statement.result_metadata
243
- request = Protocol::ExecuteRequest.new(nil, statement.params_types, statement.params, result_metadata.nil?, options.consistency, options.serial_consistency, options.page_size, options.paging_state, options.trace?, timestamp)
249
+ request = Protocol::ExecuteRequest.new(nil, statement.params_types, statement.params, result_metadata.nil?, options.consistency, options.serial_consistency, options.page_size, options.paging_state, options.trace?, timestamp, payload)
244
250
  promise = @futures.promise
245
251
 
246
252
  keyspace = @keyspace
@@ -255,9 +261,11 @@ module Cassandra
255
261
  return @futures.error(Errors::ClientError.new("Batch statements are not supported by the current version of Apache Cassandra")) if @connection_options.protocol_version < 2
256
262
 
257
263
  timestamp = nil
258
- timestamp = Time.now if @connection_options.client_timestamps? && @connection_options.protocol_version > 2
264
+ timestamp = ::Time.now if @connection_options.client_timestamps? && @connection_options.protocol_version > 2
265
+ payload = nil
266
+ payload = options.payload if @connection_options.protocol_version >= 4
259
267
  timeout = options.timeout
260
- request = Protocol::BatchRequest.new(BATCH_TYPES[statement.type], options.consistency, options.trace?, options.serial_consistency, timestamp)
268
+ request = Protocol::BatchRequest.new(BATCH_TYPES[statement.type], options.consistency, options.trace?, options.serial_consistency, timestamp, payload)
261
269
  keyspace = @keyspace
262
270
  plan = @load_balancing_policy.plan(keyspace, statement, options)
263
271
  promise = @futures.promise
@@ -467,8 +475,7 @@ module Cassandra
467
475
  prepare_and_send_request_by_plan(host, connection, promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
468
476
  else
469
477
  s.on_failure do |e|
470
- case e
471
- when Errors::HostError
478
+ if e.is_a?(Errors::HostError) || (e.is_a?(Errors::TimeoutError) && statement.idempotent?)
472
479
  errors ||= {}
473
480
  errors[host] = e
474
481
  execute_by_plan(promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
@@ -502,8 +509,7 @@ module Cassandra
502
509
  do_send_request_by_plan(host, connection, promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
503
510
  else
504
511
  prepare.on_failure do |e|
505
- case e
506
- when Errors::HostError
512
+ if e.is_a?(Errors::HostError) || (e.is_a?(Errors::TimeoutError) && statement.idempotent?)
507
513
  errors ||= {}
508
514
  errors[host] = e
509
515
  execute_by_plan(promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
@@ -514,6 +520,8 @@ module Cassandra
514
520
  end
515
521
  end
516
522
  end
523
+ rescue => e
524
+ promise.break(e)
517
525
  end
518
526
 
519
527
  def batch_by_plan(promise, keyspace, statement, options, request, plan, timeout, errors = nil, hosts = [])
@@ -541,8 +549,7 @@ module Cassandra
541
549
  batch_and_send_request_by_plan(host, connection, promise, keyspace, statement, request, options, plan, timeout, errors, hosts)
542
550
  else
543
551
  s.on_failure do |e|
544
- case e
545
- when Errors::HostError
552
+ if e.is_a?(Errors::HostError) || (e.is_a?(Errors::TimeoutError) && statement.idempotent?)
546
553
  errors ||= {}
547
554
  errors[host] = e
548
555
  batch_by_plan(promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
@@ -601,8 +608,7 @@ module Cassandra
601
608
  do_send_request_by_plan(host, connection, promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
602
609
  else
603
610
  f.on_failure do |e|
604
- case e
605
- when Errors::HostError
611
+ if e.is_a?(Errors::HostError) || (e.is_a?(Errors::TimeoutError) && statement.idempotent?)
606
612
  errors ||= {}
607
613
  errors[host] = e
608
614
  batch_by_plan(promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
@@ -640,8 +646,7 @@ module Cassandra
640
646
  do_send_request_by_plan(host, connection, promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
641
647
  else
642
648
  s.on_failure do |e|
643
- case e
644
- when Errors::HostError
649
+ if e.is_a?(Errors::HostError) || (e.is_a?(Errors::TimeoutError) && statement.idempotent?)
645
650
  errors ||= {}
646
651
  errors[host] = e
647
652
  send_request_by_plan(promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
@@ -693,8 +698,7 @@ module Cassandra
693
698
  do_send_request_by_plan(host, connection, promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
694
699
  else
695
700
  prepare.on_failure do |e|
696
- case e
697
- when Errors::HostError
701
+ if e.is_a?(Errors::HostError) || (e.is_a?(Errors::TimeoutError) && statement.idempotent?)
698
702
  errors ||= {}
699
703
  errors[host] = e
700
704
  execute_by_plan(promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
@@ -705,10 +709,9 @@ module Cassandra
705
709
  end
706
710
  end
707
711
  when Protocol::ErrorResponse
708
- error = r.to_error(statement)
712
+ error = r.to_error(keyspace, statement, options, hosts, request.consistency, retries)
709
713
 
710
- case error
711
- when Errors::HostError
714
+ if error.is_a?(Errors::HostError) || (error.is_a?(Errors::TimeoutError) && statement.idempotent?)
712
715
  errors ||= {}
713
716
  errors[host] = error
714
717
 
@@ -725,7 +728,7 @@ module Cassandra
725
728
  end
726
729
  when Protocol::SetKeyspaceResultResponse
727
730
  @keyspace = r.keyspace
728
- promise.fulfill(Results::Void.new(r.trace_id, keyspace, statement, options, hosts, request.consistency, retries, self, @futures))
731
+ promise.fulfill(Results::Void.new(r.custom_payload, r.warnings, r.trace_id, keyspace, statement, options, hosts, request.consistency, retries, self, @futures))
729
732
  when Protocol::PreparedResultResponse
730
733
  cql = request.cql
731
734
  synchronize do
@@ -733,12 +736,16 @@ module Cassandra
733
736
  @preparing_statements[host].delete(cql)
734
737
  end
735
738
 
736
- promise.fulfill(Statements::Prepared.new(cql, r.metadata, r.result_metadata, r.trace_id, keyspace, statement, options, hosts, request.consistency, retries, self, @futures, @schema))
739
+ metadata = r.metadata
740
+ pk_idx = r.pk_idx
741
+ pk_idx ||= @schema.get_pk_idx(metadata)
742
+
743
+ promise.fulfill(Statements::Prepared.new(r.custom_payload, r.warnings, cql, metadata, r.result_metadata, pk_idx, r.trace_id, keyspace, statement, options, hosts, request.consistency, retries, self, @connection_options))
737
744
  when Protocol::RawRowsResultResponse
738
745
  r.materialize(statement.result_metadata)
739
- promise.fulfill(Results::Paged.new(r.rows, r.paging_state, r.trace_id, keyspace, statement, options, hosts, request.consistency, retries, self, @futures))
746
+ promise.fulfill(Results::Paged.new(r.custom_payload, r.warnings, r.rows, r.paging_state, r.trace_id, keyspace, statement, options, hosts, request.consistency, retries, self, @futures))
740
747
  when Protocol::RowsResultResponse
741
- promise.fulfill(Results::Paged.new(r.rows, r.paging_state, r.trace_id, keyspace, statement, options, hosts, request.consistency, retries, self, @futures))
748
+ promise.fulfill(Results::Paged.new(r.custom_payload, r.warnings, r.rows, r.paging_state, r.trace_id, keyspace, statement, options, hosts, request.consistency, retries, self, @futures))
742
749
  when Protocol::SchemaChangeResultResponse
743
750
  @schema.delete_keyspace(r.keyspace) if r.change == 'DROPPED' && r.target == Protocol::Constants::SCHEMA_CHANGE_TARGET_KEYSPACE
744
751
 
@@ -749,10 +756,10 @@ module Cassandra
749
756
  @logger.error("Schema agreement failure (#{e.class.name}: #{e.message})")
750
757
  end
751
758
  end
752
- promise.fulfill(Results::Void.new(r.trace_id, keyspace, statement, options, hosts, request.consistency, retries, self, @futures))
759
+ promise.fulfill(Results::Void.new(r.custom_payload, r.warnings, r.trace_id, keyspace, statement, options, hosts, request.consistency, retries, self, @futures))
753
760
  end
754
761
  else
755
- promise.fulfill(Results::Void.new(r.trace_id, keyspace, statement, options, hosts, request.consistency, retries, self, @futures))
762
+ promise.fulfill(Results::Void.new(r.custom_payload, r.warnings, r.trace_id, keyspace, statement, options, hosts, request.consistency, retries, self, @futures))
756
763
  end
757
764
 
758
765
  if decision
@@ -760,12 +767,25 @@ module Cassandra
760
767
  when Retry::Decisions::Retry
761
768
  request.consistency = decision.consistency
762
769
  do_send_request_by_plan(host, connection, promise, keyspace, statement, options, request, plan, timeout, errors, hosts, retries + 1)
770
+ when Retry::Decisions::TryNextHost
771
+ errors ||= {}
772
+ errors[host] = r.to_error(keyspace, statement, options, hosts, request.consistency, retries)
773
+ case request
774
+ when Protocol::QueryRequest, Protocol::PrepareRequest
775
+ send_request_by_plan(promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
776
+ when Protocol::ExecuteRequest
777
+ execute_by_plan(promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
778
+ when Protocol::BatchRequest
779
+ batch_by_plan(promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
780
+ else
781
+ promise.break(e)
782
+ end
763
783
  when Retry::Decisions::Ignore
764
- promise.fulfill(Results::Void.new(r.trace_id, keyspace, statement, options, hosts, request.consistency, retries, self, @futures))
784
+ promise.fulfill(Results::Void.new(r.custom_payload, r.warnings, r.trace_id, keyspace, statement, options, hosts, request.consistency, retries, self, @futures))
765
785
  when Retry::Decisions::Reraise
766
- promise.break(r.to_error(statement))
786
+ promise.break(r.to_error(keyspace, statement, options, hosts, request.consistency, retries))
767
787
  else
768
- promise.break(r.to_error(statement))
788
+ promise.break(r.to_error(keyspace, statement, options, hosts, request.consistency, retries))
769
789
  end
770
790
  end
771
791
  rescue => e
@@ -788,6 +808,8 @@ module Cassandra
788
808
  end
789
809
  end
790
810
  end
811
+ rescue => e
812
+ promise.break(e)
791
813
  end
792
814
 
793
815
  def wait_for_schema_agreement(connection, schedule)
@@ -848,7 +870,7 @@ module Cassandra
848
870
  @keyspace = r.keyspace
849
871
  nil
850
872
  when Protocol::ErrorResponse
851
- raise r.to_error(Statements::Simple.new("USE #{Util.escape_name(keyspace)}"))
873
+ raise r.to_error(nil, Statements::Simple.new("USE #{Util.escape_name(keyspace)}"), VOID_OPTIONS, EMPTY_LIST, :one, 0)
852
874
  else
853
875
  raise Errors::InternalError, "Unexpected response #{r.inspect}"
854
876
  end
@@ -884,7 +906,7 @@ module Cassandra
884
906
  end
885
907
  id
886
908
  when Protocol::ErrorResponse
887
- raise r.to_error(VOID_STATEMENT)
909
+ raise r.to_error(nil, VOID_STATEMENT, VOID_OPTIONS, EMPTY_LIST, :one, 0)
888
910
  else
889
911
  raise Errors::InternalError, "Unexpected response #{r.inspect}"
890
912
  end
@@ -903,7 +925,7 @@ module Cassandra
903
925
  when Protocol::RowsResultResponse
904
926
  r.rows
905
927
  when Protocol::ErrorResponse
906
- raise r.to_error(VOID_STATEMENT)
928
+ raise r.to_error(nil, VOID_STATEMENT, VOID_OPTIONS, EMPTY_LIST, :one, 0)
907
929
  else
908
930
  raise Errors::InternalError, "Unexpected response #{r.inspect}"
909
931
  end
@@ -181,20 +181,20 @@ module Cassandra
181
181
  if credentials
182
182
  send_credentials(connection, credentials)
183
183
  else
184
- Ione::Future.failed(Errors::AuthenticationError.new('Server requested authentication, but client was not configured to authenticate'))
184
+ Ione::Future.failed(Errors::AuthenticationError.new('Server requested authentication, but client was not configured to authenticate', nil, nil, nil, VOID_STATEMENT, VOID_OPTIONS, EMPTY_LIST, :one, 0))
185
185
  end
186
186
  else
187
187
  authenticator = @connection_options.create_authenticator(r.authentication_class)
188
188
  if authenticator
189
189
  challenge_response_cycle(connection, authenticator, authenticator.initial_response)
190
190
  else
191
- Ione::Future.failed(Errors::AuthenticationError.new('Server requested authentication, but client was not configured to authenticate'))
191
+ Ione::Future.failed(Errors::AuthenticationError.new('Server requested authentication, but client was not configured to authenticate', nil, nil, nil, VOID_STATEMENT, VOID_OPTIONS, EMPTY_LIST, :one, 0))
192
192
  end
193
193
  end
194
194
  when Protocol::ReadyResponse
195
195
  ::Ione::Future.resolved(connection)
196
196
  when Protocol::ErrorResponse
197
- ::Ione::Future.failed(r.to_error(VOID_STATEMENT))
197
+ ::Ione::Future.failed(r.to_error(nil, VOID_STATEMENT, VOID_OPTIONS, EMPTY_LIST, :one, 0))
198
198
  else
199
199
  ::Ione::Future.failed(Errors::InternalError.new("Unexpected response #{r.inspect}"))
200
200
  end
@@ -207,7 +207,7 @@ module Cassandra
207
207
  when Protocol::SupportedResponse
208
208
  r.options
209
209
  when Protocol::ErrorResponse
210
- raise r.to_error(VOID_STATEMENT)
210
+ raise r.to_error(nil, VOID_STATEMENT, VOID_OPTIONS, EMPTY_LIST, :one, 0)
211
211
  else
212
212
  raise Errors::InternalError, "Unexpected response #{r.inspect}"
213
213
  end
@@ -220,7 +220,7 @@ module Cassandra
220
220
  when Protocol::ReadyResponse
221
221
  connection
222
222
  when Protocol::ErrorResponse
223
- raise r.to_error(VOID_STATEMENT)
223
+ raise r.to_error(nil, VOID_STATEMENT, VOID_OPTIONS, EMPTY_LIST, :one, 0)
224
224
  else
225
225
  raise Errors::InternalError, "Unexpected response #{r.inspect}"
226
226
  end
@@ -237,7 +237,7 @@ module Cassandra
237
237
  authenticator.authentication_successful(r.token) rescue nil
238
238
  ::Ione::Future.resolved(connection)
239
239
  when Protocol::ErrorResponse
240
- ::Ione::Future.failed(r.to_error(VOID_STATEMENT))
240
+ ::Ione::Future.failed(r.to_error(nil, VOID_STATEMENT, VOID_OPTIONS, EMPTY_LIST, :one, 0))
241
241
  else
242
242
  ::Ione::Future.failed(Errors::InternalError.new("Unexpected response #{r.inspect}"))
243
243
  end
@@ -22,7 +22,10 @@ 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, connection_options)
25
+ def initialize(logger, io_reactor, cluster_registry, cluster_schema,
26
+ cluster_metadata, load_balancing_policy,
27
+ reconnection_policy, address_resolution_policy, connector,
28
+ connection_options, schema_fetcher)
26
29
  @logger = logger
27
30
  @io_reactor = io_reactor
28
31
  @registry = cluster_registry
@@ -33,6 +36,7 @@ module Cassandra
33
36
  @address_resolver = address_resolution_policy
34
37
  @connector = connector
35
38
  @connection_options = connection_options
39
+ @schema_fetcher = schema_fetcher
36
40
  @refreshing_statuses = ::Hash.new(false)
37
41
  @status = :closed
38
42
  @refreshing_hosts = false
@@ -129,32 +133,14 @@ module Cassandra
129
133
  @closed_promise.fulfill
130
134
  end
131
135
 
132
- def refresh_schema_async_maybe_retry
133
- refresh_schema_async.fallback do |e|
134
- case e
135
- when Errors::HostError
136
- refresh_schema_async_retry(e, @reconnection_policy.schedule)
137
- else
138
- connection = @connection
139
- connection && connection.close(e)
140
-
141
- Ione::Future.failed(e)
142
- end
143
- end
144
- end
145
-
146
136
  def inspect
147
137
  "#<#{self.class.name}:0x#{self.object_id.to_s(16)}>"
148
138
  end
149
139
 
150
140
  private
151
141
 
152
- SELECT_LOCAL = Protocol::QueryRequest.new('SELECT rack, data_center, host_id, release_version, tokens, partitioner FROM system.local', EMPTY_LIST, EMPTY_LIST, :one)
153
- SELECT_PEERS = Protocol::QueryRequest.new('SELECT peer, rack, data_center, host_id, rpc_address, release_version, tokens FROM system.peers', EMPTY_LIST, EMPTY_LIST, :one)
154
- SELECT_KEYSPACES = Protocol::QueryRequest.new('SELECT * FROM system.schema_keyspaces', EMPTY_LIST, EMPTY_LIST, :one)
155
- SELECT_TABLES = Protocol::QueryRequest.new('SELECT * FROM system.schema_columnfamilies', EMPTY_LIST, EMPTY_LIST, :one)
156
- SELECT_COLUMNS = Protocol::QueryRequest.new('SELECT * FROM system.schema_columns', EMPTY_LIST, EMPTY_LIST, :one)
157
- SELECT_TYPES = Protocol::QueryRequest.new('SELECT * FROM system.schema_usertypes', EMPTY_LIST, EMPTY_LIST, :one)
142
+ SELECT_LOCAL = Protocol::QueryRequest.new('SELECT rack, data_center, host_id, release_version, tokens, partitioner FROM system.local', EMPTY_LIST, EMPTY_LIST, :one)
143
+ SELECT_PEERS = Protocol::QueryRequest.new('SELECT peer, rack, data_center, host_id, rpc_address, release_version, tokens FROM system.peers', EMPTY_LIST, EMPTY_LIST, :one)
158
144
 
159
145
  def reconnect_async(schedule)
160
146
  timeout = schedule.next
@@ -199,7 +185,7 @@ module Cassandra
199
185
  when Protocol::ReadyResponse
200
186
  nil
201
187
  when Protocol::ErrorResponse
202
- raise r.to_error(VOID_STATEMENT)
188
+ raise r.to_error(nil, VOID_STATEMENT, VOID_OPTIONS, EMPTY_LIST, :one, 0)
203
189
  else
204
190
  raise Errors::InternalError, "Unexpected response #{r.inspect}"
205
191
  end
@@ -215,7 +201,12 @@ module Cassandra
215
201
  when 'UP'
216
202
  address = event.address
217
203
 
218
- refresh_host_async_maybe_retry(address) if @registry.has_host?(address)
204
+ if @registry.has_host?(address)
205
+ @registry.host_up(address)
206
+ else
207
+ refresh_host_async_maybe_retry(address)
208
+ refresh_maybe_retry(:schema)
209
+ end
219
210
  when 'DOWN'
220
211
  @registry.host_down(event.address)
221
212
  when 'NEW_NODE'
@@ -223,11 +214,11 @@ module Cassandra
223
214
 
224
215
  unless @registry.has_host?(address)
225
216
  refresh_host_async_maybe_retry(address)
226
- refresh_schema_async_maybe_retry
217
+ refresh_maybe_retry(:schema)
227
218
  end
228
219
  when 'REMOVED_NODE'
229
220
  @registry.host_lost(event.address)
230
- refresh_schema_async_maybe_retry
221
+ refresh_maybe_retry(:schema)
231
222
  end
232
223
  end
233
224
  end
@@ -243,222 +234,117 @@ module Cassandra
243
234
 
244
235
  return Ione::Future.failed(Errors::ClientError.new('Not connected')) if connection.nil?
245
236
 
246
- keyspaces = send_select_request(connection, SELECT_KEYSPACES)
247
- tables = send_select_request(connection, SELECT_TABLES)
248
- columns = send_select_request(connection, SELECT_COLUMNS)
249
-
250
- if @connection_options.protocol_version > 2
251
- types = send_select_request(connection, SELECT_TYPES)
252
- else
253
- types = Ione::Future.resolved(EMPTY_LIST)
254
- end
255
-
256
- Ione::Future.all(keyspaces, tables, columns, types).map do |(keyspaces, tables, columns, types)|
257
- host = @registry.host(connection.host)
258
-
259
- @schema.update_keyspaces(host, keyspaces, tables, columns, types)
237
+ @schema_fetcher.fetch(connection).map do |keyspaces|
238
+ @schema.replace(keyspaces)
260
239
  @metadata.rebuild_token_map
261
240
  @logger.info("Schema refreshed")
262
241
  end
263
242
  end
264
243
 
265
- def refresh_schema_async_retry(error, schedule)
266
- timeout = schedule.next
267
- @logger.info("Failed to refresh schema (#{error.class.name}: #{error.message}), retrying in #{timeout}")
244
+ def refresh_keyspace_async(keyspace_name)
245
+ connection = @connection
268
246
 
269
- timer = @io_reactor.schedule_timer(timeout)
270
- timer.flat_map do
271
- refresh_schema_async.fallback do |e|
272
- case e
273
- when Errors::HostError
274
- refresh_schema_async_retry(e, schedule)
275
- else
276
- connection = @connection
277
- connection && connection.close(e)
247
+ return Ione::Future.failed(Errors::ClientError.new('Not connected')) if connection.nil?
278
248
 
279
- Ione::Future.failed(e)
280
- end
281
- end
282
- end
283
- end
249
+ @logger.info("Refreshing keyspace \"#{keyspace_name}\"")
284
250
 
285
- def refresh_keyspace_async_maybe_retry(keyspace)
286
- refresh_keyspace_async(keyspace).fallback do |e|
287
- case e
288
- when Errors::HostError
289
- refresh_keyspace_async_retry(keyspace, e, @reconnection_policy.schedule)
251
+ @schema_fetcher.fetch_keyspace(connection, keyspace_name).map do |keyspace|
252
+ if keyspace
253
+ @schema.replace_keyspace(keyspace)
290
254
  else
291
- connection = @connection
292
- connection && connection.close(e)
293
-
294
- Ione::Future.failed(e)
255
+ @schema.delete_keyspace(keyspace_name)
295
256
  end
296
- end
297
- end
298
257
 
299
- def refresh_keyspace_async_retry(keyspace, error, schedule)
300
- timeout = schedule.next
301
- @logger.info("Failed to refresh keyspace #{keyspace} (#{error.class.name}: #{error.message}), retrying in #{timeout}")
302
-
303
- timer = @io_reactor.schedule_timer(timeout)
304
- timer.flat_map do
305
- refresh_keyspace_async(keyspace).fallback do |e|
306
- case e
307
- when Errors::HostError
308
- refresh_keyspace_async_retry(keyspace, e, schedule)
309
- else
310
- connection = @connection
311
- connection && connection.close(e)
312
-
313
- Ione::Future.failed(e)
314
- end
315
- end
258
+ @logger.info("Refreshed keyspace \"#{keyspace_name}\"")
316
259
  end
317
260
  end
318
261
 
319
- def refresh_keyspace_async(keyspace)
262
+ def refresh_table_async(keyspace_name, table_name)
320
263
  connection = @connection
321
264
 
322
265
  return Ione::Future.failed(Errors::ClientError.new('Not connected')) if connection.nil?
323
266
 
324
- keyspaces = send_select_request(connection, Protocol::QueryRequest.new("SELECT * FROM system.schema_keyspaces WHERE keyspace_name = '%s'" % keyspace, EMPTY_LIST, EMPTY_LIST, :one))
325
- tables = send_select_request(connection, Protocol::QueryRequest.new("SELECT * FROM system.schema_columnfamilies WHERE keyspace_name = '%s'" % keyspace, EMPTY_LIST, EMPTY_LIST, :one))
326
- columns = send_select_request(connection, Protocol::QueryRequest.new("SELECT * FROM system.schema_columns WHERE keyspace_name = '%s'" % keyspace, EMPTY_LIST, EMPTY_LIST, :one))
327
-
328
- if @connection_options.protocol_version > 2
329
- types = send_select_request(connection, Protocol::QueryRequest.new("SELECT * FROM system.schema_usertypes WHERE keyspace_name = '%s'" % keyspace, EMPTY_LIST, EMPTY_LIST, :one))
330
- else
331
- types = Ione::Future.resolved(EMPTY_LIST)
332
- end
333
-
334
- Ione::Future.all(keyspaces, tables, columns, types).map do |(keyspaces, tables, columns, types)|
335
- host = @registry.host(connection.host)
267
+ @logger.info("Refreshing table \"#{keyspace_name}.#{table_name}\"")
336
268
 
337
- if keyspaces.empty?
338
- @schema.delete_keyspace(keyspace)
269
+ @schema_fetcher.fetch_table(connection, keyspace_name, table_name).map do |table|
270
+ if table
271
+ @schema.replace_table(table)
339
272
  else
340
- @schema.update_keyspace(host, keyspaces.first, tables, columns, types)
341
- end
342
- end
343
- end
344
-
345
- def refresh_table_async_maybe_retry(keyspace, table)
346
- refresh_table_async(keyspace, table).fallback do |e|
347
- case e
348
- when Errors::HostError
349
- refresh_keyspace_async_retry(keyspace, e, @reconnection_policy.schedule)
350
- else
351
- connection = @connection
352
- connection && connection.close(e)
353
-
354
- Ione::Future.failed(e)
273
+ @schema.delete_table(keyspace_name, table_name)
355
274
  end
356
- end
357
- end
358
-
359
- def refresh_table_async_retry(keyspace, table, error, schedule)
360
- timeout = schedule.next
361
- @logger.info("Failed to refresh keyspace #{keyspace} (#{error.class.name}: #{error.message}), retrying in #{timeout}")
362
-
363
- timer = @io_reactor.schedule_timer(timeout)
364
- timer.flat_map do
365
- refresh_keyspace_async(keyspace).fallback do |e|
366
- case e
367
- when Errors::HostError
368
- refresh_keyspace_async_retry(keyspace, e, schedule)
369
- else
370
- connection = @connection
371
- connection && connection.close(e)
372
275
 
373
- Ione::Future.failed(e)
374
- end
375
- end
276
+ @logger.info("Refreshed table \"#{keyspace_name}.#{table_name}\"")
376
277
  end
377
278
  end
378
279
 
379
- def refresh_table_async(keyspace, table)
280
+ def refresh_type_async(keyspace_name, type_name)
380
281
  connection = @connection
381
282
 
382
283
  return Ione::Future.failed(Errors::ClientError.new('Not connected')) if connection.nil?
383
284
 
384
- params = [keyspace, table]
385
- tables = send_select_request(connection, Protocol::QueryRequest.new("SELECT * FROM system.schema_columnfamilies WHERE keyspace_name = '%s' AND columnfamily_name = '%s'" % params, EMPTY_LIST, EMPTY_LIST, :one))
386
- columns = send_select_request(connection, Protocol::QueryRequest.new("SELECT * FROM system.schema_columns WHERE keyspace_name = '%s' AND columnfamily_name = '%s'" % params, EMPTY_LIST, EMPTY_LIST, :one))
285
+ @logger.info("Refreshing user-defined type \"#{keyspace_name}.#{type_name}\"")
387
286
 
388
- Ione::Future.all(tables, columns).map do |(tables, columns)|
389
- host = @registry.host(connection.host)
390
-
391
- if tables.empty?
392
- @schema.delete_table(keyspace, table)
287
+ @schema_fetcher.fetch_type(connection, keyspace_name, type_name).map do |type|
288
+ if type
289
+ @schema.replace_type(type)
393
290
  else
394
- @schema.update_table(host, keyspace, tables.first, columns)
291
+ @schema.delete_type(keyspace_name, type_name)
395
292
  end
396
- end
397
- end
398
293
 
399
- def refresh_type_async_maybe_retry(keyspace, type)
400
- refresh_type_async(keyspace, type).fallback do |e|
401
- case e
402
- when Errors::HostError
403
- refresh_keyspace_async_retry(keyspace, e, @reconnection_policy.schedule)
404
- else
405
- connection = @connection
406
- connection && connection.close(e)
407
-
408
- Ione::Future.failed(e)
409
- end
294
+ @logger.info("Refreshed user-defined type \"#{keyspace_name}.#{type_name}\"")
410
295
  end
411
296
  end
412
297
 
413
- def refresh_type_async_retry(keyspace, type, error, schedule)
414
- timeout = schedule.next
415
- @logger.info("Failed to refresh type #{keyspace}.#{type} (#{error.class.name}: #{error.message}), retrying in #{timeout}")
298
+ def refresh_function_async(keyspace_name, function_name, function_args)
299
+ connection = @connection
416
300
 
417
- timer = @io_reactor.schedule_timer(timeout)
418
- timer.flat_map do
419
- refresh_keyspace_async(keyspace).fallback do |e|
420
- case e
421
- when Errors::HostError
422
- refresh_keyspace_async_retry(keyspace, e, schedule)
423
- else
424
- connection = @connection
425
- connection && connection.close(e)
301
+ return Ione::Future.failed(Errors::ClientError.new('Not connected')) if connection.nil?
426
302
 
427
- Ione::Future.failed(e)
428
- end
303
+ @logger.info("Refreshing user-defined function \"#{keyspace_name}.#{function_name}\"")
304
+
305
+ # function_args is an array of string, and we need an array of parsed types.
306
+ parsed_function_args = @schema_fetcher.parse_argument_types(connection, keyspace_name, function_args)
307
+ @schema_fetcher.fetch_function(connection, keyspace_name, function_name, parsed_function_args).map do |function|
308
+ if function
309
+ @schema.replace_function(function)
310
+ else
311
+ @schema.delete_function(keyspace_name, function_name, parsed_function_args)
429
312
  end
313
+
314
+ @logger.info("Refreshed user-defined function \"#{keyspace_name}.#{function_name}(#{function_args.join(',')})\"")
430
315
  end
431
316
  end
432
317
 
433
- def refresh_type_async(keyspace, type)
318
+ def refresh_aggregate_async(keyspace_name, aggregate_name, aggregate_args)
434
319
  connection = @connection
435
320
 
436
321
  return Ione::Future.failed(Errors::ClientError.new('Not connected')) if connection.nil?
437
322
 
438
- params = [keyspace, type]
439
- types = send_select_request(connection, Protocol::QueryRequest.new("SELECT * FROM system.schema_usertypes WHERE keyspace_name = '%s' AND type_name = '%s'" % params, EMPTY_LIST, EMPTY_LIST, :one))
440
-
441
- types.map do |types|
442
- host = @registry.host(connection.host)
323
+ @logger.info("Refreshing user-defined aggregate \"#{keyspace_name}.#{aggregate_name}\"")
443
324
 
444
- if types.empty?
445
- @schema.delete_type(keyspace, type)
325
+ # aggregate_args is an array of string, and we need an array of parsed types.
326
+ parsed_aggregate_args = @schema_fetcher.parse_argument_types(connection, keyspace_name, aggregate_args)
327
+ @schema_fetcher.fetch_aggregate(connection, keyspace_name, aggregate_name, parsed_aggregate_args).map do |aggregate|
328
+ if aggregate
329
+ @schema.replace_aggregate(aggregate)
446
330
  else
447
- @schema.update_type(host, keyspace, types.first)
331
+ @schema.delete_aggregate(keyspace_name, aggregate_name, parsed_aggregate_args)
448
332
  end
333
+
334
+ @logger.info("Refreshed user-defined aggregate \"#{keyspace_name}.#{aggregate_name}(#{aggregate_args.join(',')})\"")
449
335
  end
450
336
  end
451
337
 
452
- def refresh_hosts_async_maybe_retry
338
+ def refresh_peers_async_maybe_retry
453
339
  synchronize do
454
340
  return Ione::Future.resolved if @refreshing_hosts
455
341
  @refreshing_hosts = true
456
342
  end
457
343
 
458
- refresh_hosts_async.fallback do |e|
344
+ refresh_peers_async.fallback do |e|
459
345
  case e
460
- when Errors::HostError
461
- refresh_hosts_async_retry(e, @reconnection_policy.schedule)
346
+ when Errors::HostError, Errors::TimeoutError
347
+ refresh_peers_async_retry(e, @reconnection_policy.schedule)
462
348
  else
463
349
  connection = @connection
464
350
  connection && connection.close(e)
@@ -472,16 +358,16 @@ module Cassandra
472
358
  end
473
359
  end
474
360
 
475
- def refresh_hosts_async_retry(error, schedule)
361
+ def refresh_peers_async_retry(error, schedule)
476
362
  timeout = schedule.next
477
363
  @logger.info("Failed to refresh hosts (#{error.class.name}: #{error.message}), retrying in #{timeout}")
478
364
 
479
365
  timer = @io_reactor.schedule_timer(timeout)
480
366
  timer.flat_map do
481
- refresh_hosts_async.fallback do |e|
367
+ refresh_peers_async.fallback do |e|
482
368
  case e
483
- when Errors::HostError
484
- refresh_hosts_async_retry(e, schedule)
369
+ when Errors::HostError, Errors::TimeoutError
370
+ refresh_peers_async_retry(e, schedule)
485
371
  else
486
372
  connection = @connection
487
373
  connection && connection.close(e)
@@ -492,38 +378,55 @@ module Cassandra
492
378
  end
493
379
  end
494
380
 
495
- def refresh_hosts_async
381
+ def refresh_peers_async
496
382
  connection = @connection
497
383
 
498
384
  return Ione::Future.failed(Errors::ClientError.new('Not connected')) if connection.nil?
499
385
 
500
- local = send_select_request(connection, SELECT_LOCAL)
501
- peers = send_select_request(connection, SELECT_PEERS)
386
+ @logger.info("Refreshing peers metadata")
502
387
 
503
- Ione::Future.all(local, peers).map do |(local, peers)|
388
+ send_select_request(connection, SELECT_PEERS).map do |peers|
504
389
  @logger.debug("#{peers.size} peer(s) found")
505
390
 
506
391
  ips = ::Set.new
507
392
 
508
- unless local.empty?
509
- ips << ip = IPAddr.new(connection.host)
510
- data = local.first
511
- @registry.host_found(ip, data)
512
- @metadata.update(data)
513
- end
514
-
515
393
  peers.shuffle!
516
394
  peers.each do |data|
517
- ip = peer_ip(data, connection.host)
395
+ ip = peer_ip(data)
518
396
  next unless ip
519
397
  ips << ip
520
398
  @registry.host_found(ip, data)
521
399
  end
522
400
 
523
401
  @registry.each_host do |host|
402
+ next if host.ip == connection.host
524
403
  @registry.host_lost(host.ip) unless ips.include?(host.ip)
525
404
  end
526
405
 
406
+ @logger.info("Refreshed peers metadata")
407
+
408
+ nil
409
+ end
410
+ end
411
+
412
+ def refresh_metadata_async
413
+ connection = @connection
414
+
415
+ return Ione::Future.failed(Errors::ClientError.new('Not connected')) if connection.nil?
416
+
417
+ @logger.info("Refreshing connected host's metadata")
418
+
419
+ send_select_request(connection, SELECT_LOCAL).map do |local|
420
+ if local.empty?
421
+ raise Errors::InternalError, "Unable to fetch connected host's metadata"
422
+ else
423
+ data = local.first
424
+ @registry.host_found(IPAddr.new(connection.host), data)
425
+ @metadata.update(data)
426
+ end
427
+
428
+ @logger.info("Refreshed connected host's metadata")
429
+
527
430
  nil
528
431
  end
529
432
  end
@@ -555,16 +458,16 @@ module Cassandra
555
458
  end
556
459
  end
557
460
 
558
- def refresh_host_async_maybe_retry(host)
461
+ def refresh_host_async_maybe_retry(address)
559
462
  synchronize do
560
- return Ione::Future.resolved if @refreshing_hosts || @refreshing_host[host]
561
- @refreshing_host[host] = true
463
+ return Ione::Future.resolved if @refreshing_hosts || @refreshing_host[address]
464
+ @refreshing_host[address] = true
562
465
  end
563
466
 
564
- refresh_host_async(host).fallback do |e|
467
+ refresh_host_async(address).fallback do |e|
565
468
  case e
566
- when Errors::HostError
567
- refresh_host_async_retry(host, e, @reconnection_policy.schedule)
469
+ when Errors::HostError, Errors::TimeoutError
470
+ refresh_host_async_retry(address, e, @reconnection_policy.schedule)
568
471
  else
569
472
  connection = @connection
570
473
  connection && connection.close(e)
@@ -573,21 +476,21 @@ module Cassandra
573
476
  end
574
477
  end.map do
575
478
  synchronize do
576
- @refreshing_host.delete(host)
479
+ @refreshing_host.delete(address)
577
480
  end
578
481
  end
579
482
  end
580
483
 
581
- def refresh_host_async_retry(host, error, schedule)
484
+ def refresh_host_async_retry(address, error, schedule)
582
485
  timeout = schedule.next
583
- @logger.info("Failed to refresh host #{host.ip.to_s} (#{error.class.name}: #{error.message}), retrying in #{timeout}")
486
+ @logger.info("Failed to refresh host #{address.to_s} (#{error.class.name}: #{error.message}), retrying in #{timeout}")
584
487
 
585
488
  timer = @io_reactor.schedule_timer(timeout)
586
489
  timer.flat_map do
587
- refresh_host_async(host).fallback do |e|
490
+ refresh_host_async(address).fallback do |e|
588
491
  case e
589
- when Errors::HostError
590
- refresh_host_async_retry(host, e, schedule)
492
+ when Errors::HostError, Errors::TimeoutError
493
+ refresh_host_async_retry(address, e, schedule)
591
494
  else
592
495
  connection = @connection
593
496
  connection && connection.close(e)
@@ -604,19 +507,26 @@ module Cassandra
604
507
 
605
508
  ip = address.to_s
606
509
 
510
+ @logger.info("Refreshing host metadata: #{ip}")
511
+
607
512
  if ip == connection.host
608
513
  request = SELECT_LOCAL
609
514
  else
610
- request = Protocol::QueryRequest.new("SELECT rack, data_center, host_id, rpc_address, release_version, tokens FROM system.peers WHERE peer = '%s'" % address, EMPTY_LIST, EMPTY_LIST, :one)
515
+ request = Protocol::QueryRequest.new("SELECT rack, data_center, host_id, rpc_address, release_version, tokens FROM system.peers WHERE peer = '%s'" % ip, EMPTY_LIST, EMPTY_LIST, :one)
611
516
  end
612
517
 
613
518
  send_select_request(connection, request).map do |rows|
614
- unless rows.empty?
519
+ if rows.empty?
520
+ raise Errors::InternalError, "Unable to find host metadata: #{ip}"
521
+ else
522
+ @logger.info("Refreshed host metadata: #{ip}")
615
523
  @registry.host_found(address, rows.first)
616
524
  end
617
525
 
618
526
  self
619
527
  end
528
+ rescue => e
529
+ @logger.error("Refreshing host metadata failed (#{e.class.name}: #{e.message})")
620
530
  end
621
531
 
622
532
  def connect_to_first_available(plan, errors = nil)
@@ -671,15 +581,16 @@ Control connection failed and is unlikely to recover.
671
581
  end
672
582
  end
673
583
 
674
- register_async
584
+ refresh_maybe_retry(:metadata)
675
585
  end
676
- f = f.flat_map { refresh_hosts_async_maybe_retry }
677
- f = f.flat_map { refresh_schema_async_maybe_retry } if @connection_options.synchronize_schema?
586
+ f = f.flat_map { register_async }
587
+ f = f.flat_map { refresh_peers_async_maybe_retry }
588
+ f = f.flat_map { refresh_maybe_retry(:schema) } if @connection_options.synchronize_schema?
678
589
  f = f.fallback do |error|
679
590
  @logger.debug("Connection to #{host.ip} failed (#{error.class.name}: #{error.message})")
680
591
 
681
592
  case error
682
- when Errors::HostError
593
+ when Errors::HostError, Errors::TimeoutError
683
594
  errors ||= {}
684
595
  errors[host] = error
685
596
  connect_to_first_available(plan, errors)
@@ -699,38 +610,23 @@ Control connection failed and is unlikely to recover.
699
610
  @connector.connect(host)
700
611
  end
701
612
 
702
- def peer_ip(data, host_address)
703
- peer = data['peer']
704
-
705
- return nil unless peer && data['host_id'] && data['data_center'] && data['rack'] && data['tokens']
706
-
707
- rpc_address = data['rpc_address']
708
-
709
- if rpc_address.nil?
710
- @logger.info("The system.peers row for '#{data['peer']}' has no rpc_address. This is likely " +
711
- 'a gossip or snitch issue. This host will be ignored.')
712
- return nil
713
- end
714
-
715
- if peer == host_address || rpc_address == host_address
716
- # Some DSE versions were inserting a line for the local node in peers (with mostly null values).
717
- # This has been fixed, but if we detect that's the case, ignore it as it's not really a big deal.
718
-
719
- @logger.debug("System.peers on node #{host_address} has a line for itself. This is not normal but is a " +
720
- 'known problem of some DSE versions. Ignoring the entry.')
721
- return nil
722
- end
723
-
724
- ip = rpc_address
725
- ip = peer if ip == '0.0.0.0'
613
+ def peer_ip(data)
614
+ ip = data['rpc_address']
615
+ ip = data['peer'] if ip == '0.0.0.0'
726
616
 
727
617
  @address_resolver.resolve(ip)
728
618
  end
729
619
 
730
620
  def process_schema_changes(schema_changes)
731
- refresh_keyspaces = ::Hash.new
732
- refresh_tables = ::Hash.new
733
- refresh_types = ::Hash.new
621
+ refresh_keyspaces = ::Hash.new
622
+ refresh_tables = ::Hash.new
623
+ refresh_types = ::Hash.new
624
+
625
+ # This hash is of the form <keyspace, [Change (for function changes)]>
626
+ refresh_functions = ::Hash.new
627
+
628
+ # This hash is of the form <keyspace, [Change (for aggregate changes)]>
629
+ refresh_aggregates = ::Hash.new
734
630
 
735
631
  schema_changes.each do |change|
736
632
  keyspace = change.keyspace
@@ -741,37 +637,91 @@ Control connection failed and is unlikely to recover.
741
637
  when Protocol::Constants::SCHEMA_CHANGE_TARGET_KEYSPACE
742
638
  refresh_tables.delete(keyspace)
743
639
  refresh_types.delete(keyspace)
640
+ refresh_functions.delete(keyspace)
641
+ refresh_aggregates.delete(keyspace)
744
642
  refresh_keyspaces[keyspace] = true
745
643
  when Protocol::Constants::SCHEMA_CHANGE_TARGET_TABLE
746
644
  tables = refresh_tables[keyspace] ||= ::Hash.new
747
- tables[change.table] = true
645
+ tables[change.name] = true
748
646
  when Protocol::Constants::SCHEMA_CHANGE_TARGET_UDT
749
647
  types = refresh_types[keyspace] ||= ::Hash.new
750
- types[change.type] = true
648
+ types[change.name] = true
649
+ when Protocol::Constants::SCHEMA_CHANGE_TARGET_FUNCTION
650
+ functions = refresh_functions[keyspace] ||= []
651
+ functions << change
652
+ when Protocol::Constants::SCHEMA_CHANGE_TARGET_AGGREGATE
653
+ aggregates = refresh_aggregates[keyspace] ||= []
654
+ aggregates << change
751
655
  end
752
656
  end
753
657
 
754
658
  futures = ::Array.new
755
659
 
756
660
  refresh_keyspaces.each_key do |keyspace|
757
- futures << refresh_keyspace_async_maybe_retry(keyspace)
661
+ futures << refresh_maybe_retry(:keyspace, keyspace)
758
662
  end
759
663
 
760
664
  refresh_tables.each do |(keyspace, tables)|
761
665
  tables.each_key do |table|
762
- futures << refresh_table_async_maybe_retry(keyspace, table)
666
+ futures << refresh_maybe_retry(:table, keyspace, table)
763
667
  end
764
668
  end
765
669
 
766
670
  refresh_types.each do |(keyspace, types)|
767
671
  types.each_key do |type|
768
- futures << refresh_type_async_maybe_retry(keyspace, type)
672
+ futures << refresh_maybe_retry(:type, keyspace, type)
673
+ end
674
+ end
675
+
676
+ refresh_functions.each do |(keyspace, function_changes)|
677
+ function_changes.each do |change|
678
+ futures << refresh_maybe_retry(:function, keyspace, change.name, change.arguments)
679
+ end
680
+ end
681
+
682
+ refresh_aggregates.each do |(keyspace, aggregate_changes)|
683
+ aggregate_changes.each do |change|
684
+ futures << refresh_maybe_retry(:aggregate, keyspace, change.name, change.arguments)
769
685
  end
770
686
  end
771
687
 
772
688
  Ione::Future.all(*futures)
773
689
  end
774
690
 
691
+ def refresh_maybe_retry(what, *args)
692
+ send(:"refresh_#{what}_async", *args).fallback do |e|
693
+ case e
694
+ when Errors::HostError, Errors::TimeoutError
695
+ refresh_retry(what, e, @reconnection_policy.schedule, *args)
696
+ else
697
+ connection = @connection
698
+ connection && connection.close(e)
699
+
700
+ Ione::Future.failed(e)
701
+ end
702
+ end
703
+ end
704
+
705
+ def refresh_retry(what, error, schedule, *args)
706
+ timeout = schedule.next
707
+ @logger.info("Failed to refresh #{what} #{args.inspect} (#{error.class.name}: #{error.message}), retrying in #{timeout}")
708
+
709
+ timer = @io_reactor.schedule_timer(timeout)
710
+ timer.flat_map do
711
+ send(:"refresh_#{what}_async", *args).fallback do |e|
712
+ case e
713
+ when Errors::HostError, Errors::TimeoutError
714
+ refresh_retry(what, e, schedule, *args)
715
+ else
716
+ connection = @connection
717
+ connection && connection.close(e)
718
+
719
+ Ione::Future.failed(e)
720
+ end
721
+ end
722
+ end
723
+ end
724
+
775
725
  def handle_schema_change(change)
776
726
  timer = nil
777
727
  expiration_timer = nil
@@ -825,14 +775,17 @@ Control connection failed and is unlikely to recover.
825
775
  end
826
776
 
827
777
  def send_select_request(connection, request)
778
+ backtrace = caller
828
779
  connection.send_request(request).map do |r|
829
780
  case r
830
781
  when Protocol::RowsResultResponse
831
782
  r.rows
832
783
  when Protocol::ErrorResponse
833
- raise r.to_error(VOID_STATEMENT)
784
+ e = r.to_error(nil, VOID_STATEMENT, VOID_OPTIONS, EMPTY_LIST, :one, 0)
785
+ e.set_backtrace(backtrace)
786
+ raise e
834
787
  else
835
- raise Errors::InternalError, "Unexpected response #{r.inspect}"
788
+ raise Errors::InternalError, "Unexpected response #{r.inspect}", caller
836
789
  end
837
790
  end
838
791
  end