yugabyte-ycql-driver 3.2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.yardopts +13 -0
- data/README.md +242 -0
- data/ext/cassandra_murmur3/cassandra_murmur3.c +178 -0
- data/ext/cassandra_murmur3/extconf.rb +2 -0
- data/lib/cassandra/address_resolution.rb +36 -0
- data/lib/cassandra/address_resolution/policies.rb +2 -0
- data/lib/cassandra/address_resolution/policies/ec2_multi_region.rb +56 -0
- data/lib/cassandra/address_resolution/policies/none.rb +35 -0
- data/lib/cassandra/aggregate.rb +123 -0
- data/lib/cassandra/argument.rb +51 -0
- data/lib/cassandra/attr_boolean.rb +33 -0
- data/lib/cassandra/auth.rb +100 -0
- data/lib/cassandra/auth/providers.rb +17 -0
- data/lib/cassandra/auth/providers/password.rb +65 -0
- data/lib/cassandra/cassandra_logger.rb +80 -0
- data/lib/cassandra/cluster.rb +331 -0
- data/lib/cassandra/cluster/client.rb +1612 -0
- data/lib/cassandra/cluster/connection_pool.rb +78 -0
- data/lib/cassandra/cluster/connector.rb +372 -0
- data/lib/cassandra/cluster/control_connection.rb +962 -0
- data/lib/cassandra/cluster/failed_connection.rb +35 -0
- data/lib/cassandra/cluster/metadata.rb +142 -0
- data/lib/cassandra/cluster/options.rb +145 -0
- data/lib/cassandra/cluster/registry.rb +284 -0
- data/lib/cassandra/cluster/schema.rb +405 -0
- data/lib/cassandra/cluster/schema/cql_type_parser.rb +112 -0
- data/lib/cassandra/cluster/schema/fetchers.rb +1627 -0
- data/lib/cassandra/cluster/schema/fqcn_type_parser.rb +175 -0
- data/lib/cassandra/cluster/schema/partitioners.rb +21 -0
- data/lib/cassandra/cluster/schema/partitioners/murmur3.rb +45 -0
- data/lib/cassandra/cluster/schema/partitioners/ordered.rb +37 -0
- data/lib/cassandra/cluster/schema/partitioners/random.rb +37 -0
- data/lib/cassandra/cluster/schema/replication_strategies.rb +21 -0
- data/lib/cassandra/cluster/schema/replication_strategies/network_topology.rb +102 -0
- data/lib/cassandra/cluster/schema/replication_strategies/none.rb +39 -0
- data/lib/cassandra/cluster/schema/replication_strategies/simple.rb +44 -0
- data/lib/cassandra/column.rb +66 -0
- data/lib/cassandra/column_container.rb +326 -0
- data/lib/cassandra/compression.rb +69 -0
- data/lib/cassandra/compression/compressors/lz4.rb +73 -0
- data/lib/cassandra/compression/compressors/snappy.rb +69 -0
- data/lib/cassandra/custom_data.rb +53 -0
- data/lib/cassandra/driver.rb +260 -0
- data/lib/cassandra/errors.rb +784 -0
- data/lib/cassandra/execution/info.rb +69 -0
- data/lib/cassandra/execution/options.rb +267 -0
- data/lib/cassandra/execution/profile.rb +153 -0
- data/lib/cassandra/execution/profile_manager.rb +71 -0
- data/lib/cassandra/execution/trace.rb +192 -0
- data/lib/cassandra/executors.rb +113 -0
- data/lib/cassandra/function.rb +156 -0
- data/lib/cassandra/function_collection.rb +85 -0
- data/lib/cassandra/future.rb +794 -0
- data/lib/cassandra/host.rb +102 -0
- data/lib/cassandra/index.rb +118 -0
- data/lib/cassandra/keyspace.rb +473 -0
- data/lib/cassandra/listener.rb +87 -0
- data/lib/cassandra/load_balancing.rb +121 -0
- data/lib/cassandra/load_balancing/policies.rb +20 -0
- data/lib/cassandra/load_balancing/policies/dc_aware_round_robin.rb +172 -0
- data/lib/cassandra/load_balancing/policies/round_robin.rb +141 -0
- data/lib/cassandra/load_balancing/policies/token_aware.rb +149 -0
- data/lib/cassandra/load_balancing/policies/white_list.rb +100 -0
- data/lib/cassandra/materialized_view.rb +92 -0
- data/lib/cassandra/null_logger.rb +56 -0
- data/lib/cassandra/protocol.rb +102 -0
- data/lib/cassandra/protocol/coder.rb +1085 -0
- data/lib/cassandra/protocol/cql_byte_buffer.rb +418 -0
- data/lib/cassandra/protocol/cql_protocol_handler.rb +448 -0
- data/lib/cassandra/protocol/request.rb +41 -0
- data/lib/cassandra/protocol/requests/auth_response_request.rb +51 -0
- data/lib/cassandra/protocol/requests/batch_request.rb +117 -0
- data/lib/cassandra/protocol/requests/credentials_request.rb +51 -0
- data/lib/cassandra/protocol/requests/execute_request.rb +122 -0
- data/lib/cassandra/protocol/requests/options_request.rb +39 -0
- data/lib/cassandra/protocol/requests/prepare_request.rb +59 -0
- data/lib/cassandra/protocol/requests/query_request.rb +112 -0
- data/lib/cassandra/protocol/requests/register_request.rb +38 -0
- data/lib/cassandra/protocol/requests/startup_request.rb +49 -0
- data/lib/cassandra/protocol/requests/void_query_request.rb +24 -0
- data/lib/cassandra/protocol/response.rb +28 -0
- data/lib/cassandra/protocol/responses/already_exists_error_response.rb +50 -0
- data/lib/cassandra/protocol/responses/auth_challenge_response.rb +36 -0
- data/lib/cassandra/protocol/responses/auth_success_response.rb +36 -0
- data/lib/cassandra/protocol/responses/authenticate_response.rb +36 -0
- data/lib/cassandra/protocol/responses/error_response.rb +142 -0
- data/lib/cassandra/protocol/responses/event_response.rb +30 -0
- data/lib/cassandra/protocol/responses/function_failure_error_response.rb +52 -0
- data/lib/cassandra/protocol/responses/prepared_result_response.rb +62 -0
- data/lib/cassandra/protocol/responses/raw_rows_result_response.rb +59 -0
- data/lib/cassandra/protocol/responses/read_failure_error_response.rb +71 -0
- data/lib/cassandra/protocol/responses/read_timeout_error_response.rb +61 -0
- data/lib/cassandra/protocol/responses/ready_response.rb +43 -0
- data/lib/cassandra/protocol/responses/result_response.rb +42 -0
- data/lib/cassandra/protocol/responses/rows_result_response.rb +39 -0
- data/lib/cassandra/protocol/responses/schema_change_event_response.rb +73 -0
- data/lib/cassandra/protocol/responses/schema_change_result_response.rb +70 -0
- data/lib/cassandra/protocol/responses/set_keyspace_result_response.rb +37 -0
- data/lib/cassandra/protocol/responses/status_change_event_response.rb +39 -0
- data/lib/cassandra/protocol/responses/supported_response.rb +36 -0
- data/lib/cassandra/protocol/responses/topology_change_event_response.rb +33 -0
- data/lib/cassandra/protocol/responses/unavailable_error_response.rb +58 -0
- data/lib/cassandra/protocol/responses/unprepared_error_response.rb +48 -0
- data/lib/cassandra/protocol/responses/void_result_response.rb +34 -0
- data/lib/cassandra/protocol/responses/write_failure_error_response.rb +73 -0
- data/lib/cassandra/protocol/responses/write_timeout_error_response.rb +63 -0
- data/lib/cassandra/protocol/v1.rb +326 -0
- data/lib/cassandra/protocol/v3.rb +358 -0
- data/lib/cassandra/protocol/v4.rb +478 -0
- data/lib/cassandra/reconnection.rb +49 -0
- data/lib/cassandra/reconnection/policies.rb +20 -0
- data/lib/cassandra/reconnection/policies/constant.rb +46 -0
- data/lib/cassandra/reconnection/policies/exponential.rb +79 -0
- data/lib/cassandra/result.rb +276 -0
- data/lib/cassandra/retry.rb +154 -0
- data/lib/cassandra/retry/policies.rb +21 -0
- data/lib/cassandra/retry/policies/default.rb +53 -0
- data/lib/cassandra/retry/policies/downgrading_consistency.rb +73 -0
- data/lib/cassandra/retry/policies/fallthrough.rb +39 -0
- data/lib/cassandra/session.rb +270 -0
- data/lib/cassandra/statement.rb +32 -0
- data/lib/cassandra/statements.rb +23 -0
- data/lib/cassandra/statements/batch.rb +146 -0
- data/lib/cassandra/statements/bound.rb +65 -0
- data/lib/cassandra/statements/prepared.rb +235 -0
- data/lib/cassandra/statements/simple.rb +118 -0
- data/lib/cassandra/statements/void.rb +38 -0
- data/lib/cassandra/table.rb +240 -0
- data/lib/cassandra/time.rb +103 -0
- data/lib/cassandra/time_uuid.rb +78 -0
- data/lib/cassandra/timestamp_generator.rb +37 -0
- data/lib/cassandra/timestamp_generator/simple.rb +38 -0
- data/lib/cassandra/timestamp_generator/ticking_on_duplicate.rb +58 -0
- data/lib/cassandra/trigger.rb +67 -0
- data/lib/cassandra/tuple.rb +131 -0
- data/lib/cassandra/types.rb +1704 -0
- data/lib/cassandra/udt.rb +443 -0
- data/lib/cassandra/util.rb +464 -0
- data/lib/cassandra/uuid.rb +110 -0
- data/lib/cassandra/uuid/generator.rb +212 -0
- data/lib/cassandra/version.rb +21 -0
- data/lib/datastax/cassandra.rb +47 -0
- data/lib/ycql.rb +842 -0
- metadata +243 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
#--
|
|
4
|
+
# Copyright DataStax, Inc.
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
#++
|
|
18
|
+
|
|
19
|
+
require 'logger'
|
|
20
|
+
module Cassandra
|
|
21
|
+
# This class is a logger that may be used by the client to log the driver's actions.
|
|
22
|
+
# It is a subclass of the standard Ruby Logger class, so it is instantiated the
|
|
23
|
+
# same way.
|
|
24
|
+
#
|
|
25
|
+
# The format of log output is set to include the timestamp, thread-id, log severity,
|
|
26
|
+
# and message. <b>This format may change in newer versions of the driver to account
|
|
27
|
+
# for new/deprecated metadata.</b>
|
|
28
|
+
#
|
|
29
|
+
# @example Configuring {Cassandra::Cluster} to use a logger.
|
|
30
|
+
# cluster = Cassandra.cluster(logger: Cassandra::Logger.new($stderr))
|
|
31
|
+
#
|
|
32
|
+
# @example The log format may be changed the same way as in the standard Ruby Logger class
|
|
33
|
+
# logger = Cassandra::Logger.new($stderr)
|
|
34
|
+
# logger.formatter = proc { |severity, time, program_name, message|
|
|
35
|
+
# "[%s]: %s\n" % [severity, message]
|
|
36
|
+
# }
|
|
37
|
+
#
|
|
38
|
+
# @example Create a logger and use it in your own business logic
|
|
39
|
+
# logger = Cassandra::Logger.new($stderr)
|
|
40
|
+
# cluster = Cassandra.cluster(logger: logger)
|
|
41
|
+
# <various logic>
|
|
42
|
+
# logger.debug("something interesting happened.")
|
|
43
|
+
|
|
44
|
+
class Logger < ::Logger
|
|
45
|
+
# @private
|
|
46
|
+
# This class is mostly copied from the Ruby Logger::Format class.
|
|
47
|
+
class Formatter
|
|
48
|
+
Format = "[%s#%d] %5s: %s\n".freeze
|
|
49
|
+
|
|
50
|
+
def call(severity, time, _, msg)
|
|
51
|
+
format(Format,
|
|
52
|
+
format_datetime(time),
|
|
53
|
+
Thread.current.object_id,
|
|
54
|
+
severity,
|
|
55
|
+
msg2str(msg))
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def format_datetime(time)
|
|
59
|
+
time.strftime('%H:%M:%S.') << format('%06d ', time.usec)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def msg2str(msg)
|
|
63
|
+
case msg
|
|
64
|
+
when ::String
|
|
65
|
+
msg
|
|
66
|
+
when ::Exception
|
|
67
|
+
"#{msg.message} (#{msg.class})\n" <<
|
|
68
|
+
(msg.backtrace || []).join("\n")
|
|
69
|
+
else
|
|
70
|
+
msg.inspect
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def initialize(*args)
|
|
76
|
+
super(*args)
|
|
77
|
+
self.formatter = Formatter.new
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
#--
|
|
4
|
+
# Copyright DataStax, Inc.
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
#++
|
|
18
|
+
|
|
19
|
+
module Cassandra
|
|
20
|
+
# Cluster represents a cassandra cluster. It serves as a
|
|
21
|
+
# {Cassandra::Session session factory} factory and a collection of metadata.
|
|
22
|
+
#
|
|
23
|
+
# @see Cassandra::Cluster#connect Creating a new session
|
|
24
|
+
# @see Cassandra::Cluster#each_host Getting all peers in the cluster
|
|
25
|
+
# @see Cassandra::Cluster#each_keyspace Getting all existing keyspaces
|
|
26
|
+
class Cluster
|
|
27
|
+
extend Forwardable
|
|
28
|
+
|
|
29
|
+
# @private
|
|
30
|
+
def initialize(logger,
|
|
31
|
+
io_reactor,
|
|
32
|
+
executor,
|
|
33
|
+
control_connection,
|
|
34
|
+
cluster_registry,
|
|
35
|
+
cluster_schema,
|
|
36
|
+
cluster_metadata,
|
|
37
|
+
execution_options,
|
|
38
|
+
connection_options,
|
|
39
|
+
profile_manager,
|
|
40
|
+
reconnection_policy,
|
|
41
|
+
address_resolution_policy,
|
|
42
|
+
connector,
|
|
43
|
+
futures_factory,
|
|
44
|
+
timestamp_generator)
|
|
45
|
+
@logger = logger
|
|
46
|
+
@io_reactor = io_reactor
|
|
47
|
+
@executor = executor
|
|
48
|
+
@control_connection = control_connection
|
|
49
|
+
@registry = cluster_registry
|
|
50
|
+
@schema = cluster_schema
|
|
51
|
+
@metadata = cluster_metadata
|
|
52
|
+
@execution_options = execution_options
|
|
53
|
+
@connection_options = connection_options
|
|
54
|
+
@profile_manager = profile_manager
|
|
55
|
+
@reconnection_policy = reconnection_policy
|
|
56
|
+
@address_resolver = address_resolution_policy
|
|
57
|
+
@connector = connector
|
|
58
|
+
@futures = futures_factory
|
|
59
|
+
@timestamp_generator = timestamp_generator
|
|
60
|
+
|
|
61
|
+
@control_connection.on_close do |_cause|
|
|
62
|
+
begin
|
|
63
|
+
@profile_manager.teardown(self)
|
|
64
|
+
rescue
|
|
65
|
+
nil
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# @!method name
|
|
71
|
+
# Return cluster's name
|
|
72
|
+
# @return [String] cluster's name
|
|
73
|
+
#
|
|
74
|
+
# @!method find_replicas(keyspace, statement)
|
|
75
|
+
# Return replicas for a given statement and keyspace
|
|
76
|
+
# @note an empty list is returned when statement/keyspace information is
|
|
77
|
+
# not enough to determine replica list.
|
|
78
|
+
# @param keyspace [String] keyspace name
|
|
79
|
+
# @param statement [Cassandra::Statement] statement for which to find
|
|
80
|
+
# replicas
|
|
81
|
+
# @return [Array<Cassandra::Host>] a list of replicas
|
|
82
|
+
def_delegators :@metadata, :name, :find_replicas
|
|
83
|
+
|
|
84
|
+
# Register a cluster state listener. State listener will start receiving
|
|
85
|
+
# notifications about topology and schema changes
|
|
86
|
+
#
|
|
87
|
+
# @param listener [Cassandra::Listener] cluster state listener
|
|
88
|
+
# @return [self]
|
|
89
|
+
def register(listener)
|
|
90
|
+
@registry.add_listener(listener)
|
|
91
|
+
@schema.add_listener(listener)
|
|
92
|
+
self
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Unregister a cluster state listener. State listener will stop receiving
|
|
96
|
+
# notifications about topology and schema changes
|
|
97
|
+
#
|
|
98
|
+
# @param listener [Cassandra::Listener] cluster state listener
|
|
99
|
+
# @return [self]
|
|
100
|
+
def unregister(listener)
|
|
101
|
+
@registry.remove_listener(listener)
|
|
102
|
+
@schema.remove_listener(listener)
|
|
103
|
+
self
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Yield or enumerate each member of this cluster
|
|
107
|
+
# @overload each_host
|
|
108
|
+
# @yieldparam host [Cassandra::Host] current host
|
|
109
|
+
# @return [Cassandra::Cluster] self
|
|
110
|
+
# @overload each_host
|
|
111
|
+
# @return [Array<Cassandra::Host>] a list of hosts
|
|
112
|
+
def each_host(&block)
|
|
113
|
+
r = @registry.each_host(&block)
|
|
114
|
+
return self if r == @registry
|
|
115
|
+
r
|
|
116
|
+
end
|
|
117
|
+
alias hosts each_host
|
|
118
|
+
|
|
119
|
+
# @!method host(address)
|
|
120
|
+
# Find a host by its address
|
|
121
|
+
# @param address [IPAddr, String] ip address
|
|
122
|
+
# @return [Cassandra::Host, nil] host or nil
|
|
123
|
+
#
|
|
124
|
+
# @!method has_host?(address)
|
|
125
|
+
# Determine if a host by a given address exists
|
|
126
|
+
# @param address [IPAddr, String] ip address
|
|
127
|
+
# @return [Boolean] true or false
|
|
128
|
+
def_delegators :@registry, :host, :has_host?
|
|
129
|
+
|
|
130
|
+
# Yield or enumerate each keyspace defined in this cluster
|
|
131
|
+
# @overload each_keyspace
|
|
132
|
+
# @yieldparam keyspace [Cassandra::Keyspace] current keyspace
|
|
133
|
+
# @return [Cassandra::Cluster] self
|
|
134
|
+
# @overload each_keyspace
|
|
135
|
+
# @return [Array<Cassandra::Keyspace>] a list of keyspaces
|
|
136
|
+
def each_keyspace(&block)
|
|
137
|
+
r = @schema.each_keyspace(&block)
|
|
138
|
+
return self if r == @schema
|
|
139
|
+
r
|
|
140
|
+
end
|
|
141
|
+
alias keyspaces each_keyspace
|
|
142
|
+
|
|
143
|
+
# @!method keyspace(name)
|
|
144
|
+
# Find a keyspace by name
|
|
145
|
+
# @param name [String] keyspace name
|
|
146
|
+
# @return [Cassandra::Keyspace, nil] keyspace or nil
|
|
147
|
+
#
|
|
148
|
+
# @!method has_keyspace?(name)
|
|
149
|
+
# Determine if a keyspace by a given name exists
|
|
150
|
+
# @param name [String] keyspace name
|
|
151
|
+
# @return [Boolean] true or false
|
|
152
|
+
def_delegators :@schema, :keyspace, :has_keyspace?
|
|
153
|
+
|
|
154
|
+
# @return [Integer] Cassandra native protocol port
|
|
155
|
+
def port
|
|
156
|
+
@connection_options.port
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# @return [Integer] the version of the native protocol used in communication with nodes
|
|
160
|
+
def protocol_version
|
|
161
|
+
@connection_options.protocol_version
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# @param name [String] Name of profile to retrieve
|
|
165
|
+
# @return [Cassandra::Execution::Profile] execution profile of the given name
|
|
166
|
+
def execution_profile(name)
|
|
167
|
+
@profile_manager.profiles[name]
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Yield or enumerate each execution profile defined in this cluster
|
|
171
|
+
# @overload each_execution_profile
|
|
172
|
+
# @yieldparam name [String, Symbol] name of current profile
|
|
173
|
+
# @yieldparam profile [Cassandra::Execution::Profile] current profile
|
|
174
|
+
# @return [Cassandra::Cluster] self
|
|
175
|
+
# @overload each_execution_profile
|
|
176
|
+
# @return [Hash<String, Cassandra::Execution::Profile>] a hash of profiles keyed on name
|
|
177
|
+
def each_execution_profile(&block)
|
|
178
|
+
if block_given?
|
|
179
|
+
@profile_manager.profiles.each_pair(&block)
|
|
180
|
+
self
|
|
181
|
+
else
|
|
182
|
+
# Return a dup of the hash to prevent the user from adding/removing profiles from the profile-manager.
|
|
183
|
+
@profile_manager.profiles.dup
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
alias execution_profiles each_execution_profile
|
|
187
|
+
|
|
188
|
+
# @!method refresh_schema_async
|
|
189
|
+
# Trigger an asynchronous schema metadata refresh
|
|
190
|
+
# @return [Cassandra::Future<nil>] a future that will be fulfilled when
|
|
191
|
+
# schema metadata has been refreshed
|
|
192
|
+
def refresh_schema_async
|
|
193
|
+
promise = @futures.promise
|
|
194
|
+
@control_connection.send(:refresh_maybe_retry, :schema).on_complete do |f|
|
|
195
|
+
if f.resolved?
|
|
196
|
+
promise.fulfill(nil)
|
|
197
|
+
else
|
|
198
|
+
f.on_failure do |e|
|
|
199
|
+
promise.break(e)
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
promise.future
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# Synchronously refresh schema metadata
|
|
207
|
+
#
|
|
208
|
+
# @return [nil] nothing
|
|
209
|
+
# @raise [Cassandra::Errors::ClientError] when cluster is disconnected
|
|
210
|
+
# @raise [Cassandra::Error] other unexpected errors
|
|
211
|
+
#
|
|
212
|
+
# @see Cassandra::Cluster#refresh_schema_async
|
|
213
|
+
def refresh_schema
|
|
214
|
+
refresh_schema_async.get
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# Asynchronously create a new session, optionally scoped to a keyspace
|
|
218
|
+
#
|
|
219
|
+
# @param keyspace [String] optional keyspace to scope session to
|
|
220
|
+
#
|
|
221
|
+
# @return [Cassandra::Future<Cassandra::Session>] a future new session that
|
|
222
|
+
# can prepare and execute statements
|
|
223
|
+
#
|
|
224
|
+
# @see Cassandra::Cluster#connect A list of possible exceptions that this
|
|
225
|
+
# future can be resolved with
|
|
226
|
+
def connect_async(keyspace = nil)
|
|
227
|
+
if !keyspace.nil? && !keyspace.is_a?(::String)
|
|
228
|
+
return @futures.error(::ArgumentError.new("keyspace must be a string, #{keyspace.inspect} given"))
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
client = Client.new(@logger,
|
|
232
|
+
@registry,
|
|
233
|
+
@schema,
|
|
234
|
+
@io_reactor,
|
|
235
|
+
@connector,
|
|
236
|
+
@profile_manager,
|
|
237
|
+
@reconnection_policy,
|
|
238
|
+
@address_resolver,
|
|
239
|
+
@connection_options,
|
|
240
|
+
@futures,
|
|
241
|
+
@timestamp_generator)
|
|
242
|
+
session = Session.new(client, @execution_options, @futures, @profile_manager)
|
|
243
|
+
promise = @futures.promise
|
|
244
|
+
|
|
245
|
+
client.connect.on_complete do |f|
|
|
246
|
+
if f.resolved?
|
|
247
|
+
if keyspace
|
|
248
|
+
f = session.execute_async("USE #{Util.escape_name(keyspace)}")
|
|
249
|
+
|
|
250
|
+
f.on_success {promise.fulfill(session)}
|
|
251
|
+
f.on_failure {|e| promise.break(e)}
|
|
252
|
+
else
|
|
253
|
+
promise.fulfill(session)
|
|
254
|
+
end
|
|
255
|
+
else
|
|
256
|
+
f.on_failure {|e| promise.break(e)}
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
promise.future
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
# Synchronously create a new session, optionally scoped to a keyspace
|
|
264
|
+
#
|
|
265
|
+
# @param keyspace [String] optional keyspace to scope the session to
|
|
266
|
+
#
|
|
267
|
+
# @raise [ArgumentError] if keyspace is not a String
|
|
268
|
+
# @raise [Cassandra::Errors::NoHostsAvailable] when all hosts failed
|
|
269
|
+
# @raise [Cassandra::Errors::AuthenticationError] when authentication fails
|
|
270
|
+
# @raise [Cassandra::Errors::ProtocolError] when protocol negotiation fails
|
|
271
|
+
# @raise [Cassandra::Error] other unexpected errors
|
|
272
|
+
#
|
|
273
|
+
# @return [Cassandra::Session] a new session that can prepare and execute
|
|
274
|
+
# statements
|
|
275
|
+
#
|
|
276
|
+
# @see Cassandra::Cluster#connect_async
|
|
277
|
+
def connect(keyspace = nil)
|
|
278
|
+
connect_async(keyspace).get
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# Asynchronously closes all sessions managed by this cluster
|
|
282
|
+
#
|
|
283
|
+
# @return [Cassandra::Future<Cassandra::Cluster>] a future that resolves to
|
|
284
|
+
# self once closed
|
|
285
|
+
def close_async
|
|
286
|
+
promise = @futures.promise
|
|
287
|
+
|
|
288
|
+
@control_connection.close_async.on_complete do |f|
|
|
289
|
+
if f.resolved?
|
|
290
|
+
promise.fulfill(self)
|
|
291
|
+
else
|
|
292
|
+
f.on_failure {|e| promise.break(e)}
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
@executor.shutdown
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
promise.future
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
# Synchronously closes all sessions managed by this cluster
|
|
302
|
+
#
|
|
303
|
+
# @return [self] this cluster
|
|
304
|
+
#
|
|
305
|
+
# @see Cassandra::Cluster#close_async
|
|
306
|
+
def close
|
|
307
|
+
close_async.get
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
# @private
|
|
311
|
+
def inspect
|
|
312
|
+
"#<#{self.class.name}:0x#{object_id.to_s(16)} " \
|
|
313
|
+
"name=#{name.inspect}, " \
|
|
314
|
+
"port=#{@connection_options.port}, " \
|
|
315
|
+
"protocol_version=#{@connection_options.protocol_version}, " \
|
|
316
|
+
"execution_profiles=#{@profile_manager.profiles.inspect}, " \
|
|
317
|
+
"hosts=#{hosts.inspect}, " \
|
|
318
|
+
"keyspaces=#{keyspaces.inspect}>"
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
require 'cassandra/cluster/client'
|
|
324
|
+
require 'cassandra/cluster/connection_pool'
|
|
325
|
+
require 'cassandra/cluster/connector'
|
|
326
|
+
require 'cassandra/cluster/control_connection'
|
|
327
|
+
require 'cassandra/cluster/failed_connection'
|
|
328
|
+
require 'cassandra/cluster/metadata'
|
|
329
|
+
require 'cassandra/cluster/options'
|
|
330
|
+
require 'cassandra/cluster/registry'
|
|
331
|
+
require 'cassandra/cluster/schema'
|
|
@@ -0,0 +1,1612 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
#--
|
|
4
|
+
# Copyright DataStax, Inc.
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
#++
|
|
18
|
+
|
|
19
|
+
module Cassandra
|
|
20
|
+
class Cluster
|
|
21
|
+
# @private
|
|
22
|
+
class Client
|
|
23
|
+
include MonitorMixin
|
|
24
|
+
|
|
25
|
+
attr_reader :keyspace
|
|
26
|
+
|
|
27
|
+
def initialize(logger,
|
|
28
|
+
cluster_registry,
|
|
29
|
+
cluster_schema,
|
|
30
|
+
io_reactor,
|
|
31
|
+
connector,
|
|
32
|
+
profile_manager,
|
|
33
|
+
reconnection_policy,
|
|
34
|
+
address_resolution_policy,
|
|
35
|
+
connection_options,
|
|
36
|
+
futures_factory,
|
|
37
|
+
timestamp_generator)
|
|
38
|
+
@logger = logger
|
|
39
|
+
@registry = cluster_registry
|
|
40
|
+
@schema = cluster_schema
|
|
41
|
+
@reactor = io_reactor
|
|
42
|
+
@connector = connector
|
|
43
|
+
@profile_manager = profile_manager
|
|
44
|
+
@reconnection_policy = reconnection_policy
|
|
45
|
+
@address_resolver = address_resolution_policy
|
|
46
|
+
@connection_options = connection_options
|
|
47
|
+
@futures = futures_factory
|
|
48
|
+
@connections = ::Hash.new
|
|
49
|
+
@prepared_statements = ::Hash.new
|
|
50
|
+
@preparing_statements = ::Hash.new {|hash, host| hash[host] = {}}
|
|
51
|
+
@pending_connections = ::Hash.new
|
|
52
|
+
@keyspace = nil
|
|
53
|
+
@state = :idle
|
|
54
|
+
@timestamp_generator = timestamp_generator
|
|
55
|
+
|
|
56
|
+
mon_initialize
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def connect
|
|
60
|
+
connecting_hosts = ::Hash.new
|
|
61
|
+
|
|
62
|
+
synchronize do
|
|
63
|
+
return CLIENT_CLOSED if @state == :closed || @state == :closing
|
|
64
|
+
return @connected_future if @state == :connecting || @state == :connected
|
|
65
|
+
|
|
66
|
+
@state = :connecting
|
|
67
|
+
@registry.each_host do |host|
|
|
68
|
+
distance = @profile_manager.distance(host)
|
|
69
|
+
|
|
70
|
+
case distance
|
|
71
|
+
when :ignore
|
|
72
|
+
next
|
|
73
|
+
when :local
|
|
74
|
+
pool_size = @connection_options.connections_per_local_node
|
|
75
|
+
when :remote
|
|
76
|
+
pool_size = @connection_options.connections_per_remote_node
|
|
77
|
+
else
|
|
78
|
+
@logger.error("Not connecting to #{host.ip} - invalid load balancing " \
|
|
79
|
+
'distance. Distance must be one of ' \
|
|
80
|
+
"#{LoadBalancing::DISTANCES.inspect}, #{distance.inspect} given")
|
|
81
|
+
next
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
connecting_hosts[host] = pool_size
|
|
85
|
+
@pending_connections[host] = 0
|
|
86
|
+
@preparing_statements[host] = {}
|
|
87
|
+
@connections[host] = ConnectionPool.new
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
@connected_future = begin
|
|
92
|
+
@logger.info('Creating session')
|
|
93
|
+
@registry.add_listener(self)
|
|
94
|
+
@schema.add_listener(self)
|
|
95
|
+
|
|
96
|
+
futures = connecting_hosts.map do |(host, pool_size)|
|
|
97
|
+
f = connect_to_host(host, pool_size)
|
|
98
|
+
f.recover do |error|
|
|
99
|
+
FailedConnection.new(error, host)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
Ione::Future.all(*futures).map do |connections|
|
|
104
|
+
connections.flatten!
|
|
105
|
+
raise NO_HOSTS if connections.empty?
|
|
106
|
+
|
|
107
|
+
failed_connections = connections.reject(&:connected?)
|
|
108
|
+
|
|
109
|
+
# convert Cassandra::Protocol::CqlProtocolHandler to something with a real host
|
|
110
|
+
failed_connections.map! do |c|
|
|
111
|
+
if c.host.is_a?(String)
|
|
112
|
+
host = @registry.each_host.detect { |h| h.ip.to_s == c.host } || raise("Unable to find host #{c.host}")
|
|
113
|
+
FailedConnection.new(c.error, host)
|
|
114
|
+
else
|
|
115
|
+
c
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
if failed_connections.size == connections.size
|
|
120
|
+
errors = {}
|
|
121
|
+
connections.each {|c| errors[c.host] = c.error unless c.error.nil?}
|
|
122
|
+
raise Errors::NoHostsAvailable.new(errors)
|
|
123
|
+
else
|
|
124
|
+
failed_connections.each do |f|
|
|
125
|
+
connect_to_host_with_retry(f.host,
|
|
126
|
+
connecting_hosts[f.host],
|
|
127
|
+
@reconnection_policy.schedule)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
self
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
@connected_future.on_complete(&method(:connected))
|
|
135
|
+
@connected_future
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def close
|
|
139
|
+
state = nil
|
|
140
|
+
|
|
141
|
+
synchronize do
|
|
142
|
+
return CLIENT_NOT_CONNECTED if @state == :idle
|
|
143
|
+
return @closed_future if @state == :closed || @state == :closing
|
|
144
|
+
|
|
145
|
+
state = @state
|
|
146
|
+
@state = :closing
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
@closed_future = begin
|
|
150
|
+
@registry.remove_listener(self)
|
|
151
|
+
@schema.remove_listener(self)
|
|
152
|
+
|
|
153
|
+
f = if state == :connecting
|
|
154
|
+
@connected_future.recover.flat_map { close_connections }
|
|
155
|
+
else
|
|
156
|
+
close_connections
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
f.map(self)
|
|
160
|
+
end
|
|
161
|
+
@closed_future.on_complete(&method(:closed))
|
|
162
|
+
@closed_future
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# These methods shall be called from inside reactor thread only
|
|
166
|
+
def host_found(host)
|
|
167
|
+
nil
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def host_lost(host)
|
|
171
|
+
nil
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def host_up(host)
|
|
175
|
+
pool_size = 0
|
|
176
|
+
|
|
177
|
+
synchronize do
|
|
178
|
+
distance = @profile_manager.distance(host)
|
|
179
|
+
case distance
|
|
180
|
+
when :ignore
|
|
181
|
+
return Ione::Future.resolved
|
|
182
|
+
when :local
|
|
183
|
+
pool_size = @connection_options.connections_per_local_node
|
|
184
|
+
when :remote
|
|
185
|
+
pool_size = @connection_options.connections_per_remote_node
|
|
186
|
+
else
|
|
187
|
+
@logger.error("Not connecting to #{host.ip} - " \
|
|
188
|
+
'invalid load balancing distance. Distance must be one of ' \
|
|
189
|
+
"#{LoadBalancing::DISTANCES.inspect}, #{distance.inspect} given")
|
|
190
|
+
return Ione::Future.resolved
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
@pending_connections[host] ||= 0
|
|
194
|
+
@preparing_statements[host] = {}
|
|
195
|
+
@connections[host] = ConnectionPool.new
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
connect_to_host_maybe_retry(host, pool_size)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def host_down(host)
|
|
202
|
+
pool = nil
|
|
203
|
+
|
|
204
|
+
synchronize do
|
|
205
|
+
return Ione::Future.resolved unless @connections.key?(host)
|
|
206
|
+
|
|
207
|
+
@pending_connections.delete(host) unless @pending_connections[host] > 0
|
|
208
|
+
@preparing_statements.delete(host)
|
|
209
|
+
pool = @connections.delete(host)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
if pool
|
|
213
|
+
Ione::Future.all(*pool.snapshot.map!(&:close)).map(nil)
|
|
214
|
+
else
|
|
215
|
+
Ione::Future.resolved
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def keyspace_created(keyspace)
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def keyspace_changed(keyspace)
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def keyspace_dropped(keyspace)
|
|
226
|
+
@keyspace = nil if @keyspace == keyspace.name
|
|
227
|
+
nil
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def query(statement, options)
|
|
231
|
+
if !statement.params.empty? && @connection_options.protocol_version == 1
|
|
232
|
+
return @futures.error(
|
|
233
|
+
Errors::ClientError.new(
|
|
234
|
+
'Positional arguments are not supported by the current version of ' \
|
|
235
|
+
'Apache Cassandra'
|
|
236
|
+
)
|
|
237
|
+
)
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
timestamp = @timestamp_generator.next if @timestamp_generator && @connection_options.protocol_version > 2
|
|
241
|
+
payload = nil
|
|
242
|
+
payload = options.payload if @connection_options.protocol_version >= 4
|
|
243
|
+
request = Protocol::QueryRequest.new(statement.cql,
|
|
244
|
+
statement.params,
|
|
245
|
+
statement.params_types,
|
|
246
|
+
options.consistency,
|
|
247
|
+
options.serial_consistency,
|
|
248
|
+
options.page_size,
|
|
249
|
+
options.paging_state,
|
|
250
|
+
options.trace?,
|
|
251
|
+
statement.params_names,
|
|
252
|
+
timestamp,
|
|
253
|
+
payload)
|
|
254
|
+
timeout = options.timeout
|
|
255
|
+
promise = @futures.promise
|
|
256
|
+
|
|
257
|
+
keyspace = @keyspace
|
|
258
|
+
plan = options.load_balancing_policy.plan(keyspace, statement, options)
|
|
259
|
+
|
|
260
|
+
send_request_by_plan(promise,
|
|
261
|
+
keyspace,
|
|
262
|
+
statement,
|
|
263
|
+
options,
|
|
264
|
+
request,
|
|
265
|
+
plan,
|
|
266
|
+
timeout)
|
|
267
|
+
|
|
268
|
+
promise.future
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def prepare(cql, options)
|
|
272
|
+
payload = nil
|
|
273
|
+
payload = options.payload if @connection_options.protocol_version >= 4
|
|
274
|
+
request = Protocol::PrepareRequest.new(cql, options.trace?, payload)
|
|
275
|
+
timeout = options.timeout
|
|
276
|
+
promise = @futures.promise
|
|
277
|
+
|
|
278
|
+
keyspace = @keyspace
|
|
279
|
+
statement = VOID_STATEMENT
|
|
280
|
+
plan = options.load_balancing_policy.plan(keyspace, statement, options)
|
|
281
|
+
|
|
282
|
+
send_request_by_plan(promise,
|
|
283
|
+
keyspace,
|
|
284
|
+
statement,
|
|
285
|
+
options,
|
|
286
|
+
request,
|
|
287
|
+
plan,
|
|
288
|
+
timeout)
|
|
289
|
+
|
|
290
|
+
promise.future
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
def execute(statement, options)
|
|
294
|
+
timestamp = @timestamp_generator.next if @timestamp_generator && @connection_options.protocol_version > 2
|
|
295
|
+
payload = nil
|
|
296
|
+
payload = options.payload if @connection_options.protocol_version >= 4
|
|
297
|
+
timeout = options.timeout
|
|
298
|
+
result_metadata = statement.result_metadata
|
|
299
|
+
request = Protocol::ExecuteRequest.new(nil,
|
|
300
|
+
statement.params_types,
|
|
301
|
+
statement.params,
|
|
302
|
+
result_metadata.nil?,
|
|
303
|
+
options.consistency,
|
|
304
|
+
options.serial_consistency,
|
|
305
|
+
options.page_size,
|
|
306
|
+
options.paging_state,
|
|
307
|
+
options.trace?,
|
|
308
|
+
timestamp,
|
|
309
|
+
payload)
|
|
310
|
+
promise = @futures.promise
|
|
311
|
+
|
|
312
|
+
keyspace = @keyspace
|
|
313
|
+
plan = options.load_balancing_policy.plan(keyspace, statement, options)
|
|
314
|
+
|
|
315
|
+
execute_by_plan(promise, keyspace, statement, options, request, plan, timeout)
|
|
316
|
+
|
|
317
|
+
promise.future
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
def batch(statement, options)
|
|
321
|
+
if @connection_options.protocol_version < 2
|
|
322
|
+
return @futures.error(
|
|
323
|
+
Errors::ClientError.new(
|
|
324
|
+
'Batch statements are not supported by the current version of ' \
|
|
325
|
+
'Apache Cassandra'
|
|
326
|
+
)
|
|
327
|
+
)
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
timestamp = @timestamp_generator.next if @timestamp_generator && @connection_options.protocol_version > 2
|
|
331
|
+
payload = nil
|
|
332
|
+
payload = options.payload if @connection_options.protocol_version >= 4
|
|
333
|
+
timeout = options.timeout
|
|
334
|
+
request = Protocol::BatchRequest.new(BATCH_TYPES[statement.type],
|
|
335
|
+
options.consistency,
|
|
336
|
+
options.trace?,
|
|
337
|
+
options.serial_consistency,
|
|
338
|
+
timestamp,
|
|
339
|
+
payload)
|
|
340
|
+
keyspace = @keyspace
|
|
341
|
+
plan = options.load_balancing_policy.plan(keyspace, statement, options)
|
|
342
|
+
promise = @futures.promise
|
|
343
|
+
|
|
344
|
+
batch_by_plan(promise, keyspace, statement, options, request, plan, timeout)
|
|
345
|
+
|
|
346
|
+
promise.future
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
def inspect
|
|
350
|
+
"#<#{self.class.name}:0x#{object_id.to_s(16)}>"
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
private
|
|
354
|
+
|
|
355
|
+
NO_CONNECTIONS = Ione::Future.resolved([])
|
|
356
|
+
BATCH_TYPES = {
|
|
357
|
+
logged: Protocol::BatchRequest::LOGGED_TYPE,
|
|
358
|
+
unlogged: Protocol::BatchRequest::UNLOGGED_TYPE,
|
|
359
|
+
counter: Protocol::BatchRequest::COUNTER_TYPE
|
|
360
|
+
}.freeze
|
|
361
|
+
CLIENT_CLOSED = Ione::Future.failed(Errors::ClientError.new('Client closed'))
|
|
362
|
+
NOT_CONNECTED = Errors::ClientError.new('Client not connected')
|
|
363
|
+
CLIENT_NOT_CONNECTED = Ione::Future.failed(NOT_CONNECTED)
|
|
364
|
+
|
|
365
|
+
UNAVAILABLE_ERROR_CODE = 0x1000
|
|
366
|
+
WRITE_TIMEOUT_ERROR_CODE = 0x1100
|
|
367
|
+
READ_TIMEOUT_ERROR_CODE = 0x1200
|
|
368
|
+
OVERLOADED_ERROR_CODE = 0x1001
|
|
369
|
+
SERVER_ERROR_CODE = 0x0000
|
|
370
|
+
BOOTSTRAPPING_ERROR_CODE = 0x1002
|
|
371
|
+
UNPREPARED_ERROR_CODE = 0x2500
|
|
372
|
+
|
|
373
|
+
SELECT_SCHEMA_PEERS =
|
|
374
|
+
Protocol::QueryRequest.new(
|
|
375
|
+
'SELECT peer, rpc_address, schema_version FROM system.peers',
|
|
376
|
+
EMPTY_LIST,
|
|
377
|
+
EMPTY_LIST,
|
|
378
|
+
:quorum
|
|
379
|
+
)
|
|
380
|
+
SELECT_SCHEMA_LOCAL =
|
|
381
|
+
Protocol::QueryRequest.new(
|
|
382
|
+
"SELECT schema_version FROM system.local WHERE key='local'",
|
|
383
|
+
EMPTY_LIST,
|
|
384
|
+
EMPTY_LIST,
|
|
385
|
+
:quorum
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
def connected(f)
|
|
389
|
+
if f.resolved?
|
|
390
|
+
synchronize do
|
|
391
|
+
@state = :connected
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
@logger.info('Session created')
|
|
395
|
+
else
|
|
396
|
+
synchronize do
|
|
397
|
+
@state = :defunct
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
f.on_failure do |e|
|
|
401
|
+
@logger.error("Session failed to connect (#{e.class.name}: #{e.message})")
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
close
|
|
405
|
+
end
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
def closed(f)
|
|
409
|
+
synchronize do
|
|
410
|
+
@state = :closed
|
|
411
|
+
|
|
412
|
+
if f.resolved?
|
|
413
|
+
@logger.info('Session closed')
|
|
414
|
+
else
|
|
415
|
+
f.on_failure do |e|
|
|
416
|
+
@logger.error("Session failed to close (#{e.class.name}: #{e.message})")
|
|
417
|
+
end
|
|
418
|
+
end
|
|
419
|
+
end
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
def close_connections
|
|
423
|
+
futures = []
|
|
424
|
+
synchronize do
|
|
425
|
+
@connections.each do |_host, connections|
|
|
426
|
+
connections.snapshot.each do |c|
|
|
427
|
+
futures << c.close
|
|
428
|
+
end
|
|
429
|
+
end.clear
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
Ione::Future.all(*futures).map(self)
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
def connect_to_host_maybe_retry(host, pool_size)
|
|
436
|
+
connect_to_host(host, pool_size).fallback do |e|
|
|
437
|
+
@logger.error('Scheduling initial connection retry to ' \
|
|
438
|
+
"#{host.ip} (#{e.class.name}: #{e.message})")
|
|
439
|
+
connect_to_host_with_retry(host, pool_size, @reconnection_policy.schedule)
|
|
440
|
+
end.map(nil)
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
def connect_to_host_with_retry(host, pool_size, schedule)
|
|
444
|
+
interval = schedule.next
|
|
445
|
+
|
|
446
|
+
@logger.debug("Reconnecting to #{host.ip} in #{interval} seconds")
|
|
447
|
+
|
|
448
|
+
f = @reactor.schedule_timer(interval)
|
|
449
|
+
f.flat_map do
|
|
450
|
+
connect_to_host(host, pool_size).fallback do |e|
|
|
451
|
+
@logger.error('Scheduling connection retry to ' \
|
|
452
|
+
"#{host.ip} (#{e.class.name}: #{e.message})")
|
|
453
|
+
connect_to_host_with_retry(host, pool_size, schedule)
|
|
454
|
+
end
|
|
455
|
+
end
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
def connect_to_host(host, pool_size)
|
|
459
|
+
size = 0
|
|
460
|
+
|
|
461
|
+
synchronize do
|
|
462
|
+
unless @connections.include?(host)
|
|
463
|
+
@logger.info("Not connecting to #{host.ip} - host is currently down")
|
|
464
|
+
return NO_CONNECTIONS
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
pool = @connections[host]
|
|
468
|
+
size = pool_size - pool.size
|
|
469
|
+
|
|
470
|
+
if size <= 0
|
|
471
|
+
@logger.info("Not connecting to #{host.ip} - host is already fully connected")
|
|
472
|
+
return NO_CONNECTIONS
|
|
473
|
+
end
|
|
474
|
+
|
|
475
|
+
size -= @pending_connections[host]
|
|
476
|
+
|
|
477
|
+
if size <= 0
|
|
478
|
+
@logger.info("Not connecting to #{host.ip} - " \
|
|
479
|
+
'host is already pending connections')
|
|
480
|
+
return NO_CONNECTIONS
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
@pending_connections[host] += size
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
@logger.debug("Creating #{size} request connections to #{host.ip}")
|
|
487
|
+
futures = size.times.map do
|
|
488
|
+
@connector.connect(host).recover do |e|
|
|
489
|
+
FailedConnection.new(e, host)
|
|
490
|
+
end
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
Ione::Future.all(*futures).flat_map do |connections|
|
|
494
|
+
error = nil
|
|
495
|
+
|
|
496
|
+
connections.reject! do |connection|
|
|
497
|
+
if connection.connected?
|
|
498
|
+
false
|
|
499
|
+
else
|
|
500
|
+
error = connection.error
|
|
501
|
+
true
|
|
502
|
+
end
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
@logger.debug("Created #{connections.size} request connections to #{host.ip}")
|
|
506
|
+
|
|
507
|
+
pool = nil
|
|
508
|
+
|
|
509
|
+
synchronize do
|
|
510
|
+
@pending_connections[host] -= size
|
|
511
|
+
|
|
512
|
+
if @connections.include?(host)
|
|
513
|
+
pool = @connections[host]
|
|
514
|
+
else
|
|
515
|
+
@pending_connections.delete(host) unless @pending_connections[host] > 0
|
|
516
|
+
end
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
if pool
|
|
520
|
+
pool.add_connections(connections)
|
|
521
|
+
|
|
522
|
+
connections.each do |connection|
|
|
523
|
+
connection.on_closed do |cause|
|
|
524
|
+
if cause
|
|
525
|
+
@logger.info('Request connection closed ' \
|
|
526
|
+
"(#{cause.class.name}: #{cause.message})")
|
|
527
|
+
else
|
|
528
|
+
@logger.info('Request connection closed')
|
|
529
|
+
end
|
|
530
|
+
connect_to_host_maybe_retry(host, pool_size) if cause
|
|
531
|
+
end
|
|
532
|
+
end
|
|
533
|
+
else
|
|
534
|
+
connections.each(&:close)
|
|
535
|
+
end
|
|
536
|
+
|
|
537
|
+
if error
|
|
538
|
+
Ione::Future.failed(error)
|
|
539
|
+
else
|
|
540
|
+
Ione::Future.resolved(connections)
|
|
541
|
+
end
|
|
542
|
+
end
|
|
543
|
+
end
|
|
544
|
+
|
|
545
|
+
def execute_by_plan(promise,
|
|
546
|
+
keyspace,
|
|
547
|
+
statement,
|
|
548
|
+
options,
|
|
549
|
+
request,
|
|
550
|
+
plan,
|
|
551
|
+
timeout,
|
|
552
|
+
errors = nil,
|
|
553
|
+
hosts = [],
|
|
554
|
+
retries = -1)
|
|
555
|
+
unless plan.has_next?
|
|
556
|
+
promise.break(Errors::NoHostsAvailable.new(errors))
|
|
557
|
+
return
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
hosts << host = plan.next
|
|
561
|
+
retries += 1
|
|
562
|
+
|
|
563
|
+
pool = nil
|
|
564
|
+
synchronize { pool = @connections[host] }
|
|
565
|
+
|
|
566
|
+
unless pool
|
|
567
|
+
errors ||= {}
|
|
568
|
+
errors[host] = NOT_CONNECTED
|
|
569
|
+
return execute_by_plan(promise,
|
|
570
|
+
keyspace,
|
|
571
|
+
statement,
|
|
572
|
+
options,
|
|
573
|
+
request,
|
|
574
|
+
plan,
|
|
575
|
+
timeout,
|
|
576
|
+
errors,
|
|
577
|
+
hosts,
|
|
578
|
+
retries)
|
|
579
|
+
end
|
|
580
|
+
|
|
581
|
+
connection = pool.random_connection
|
|
582
|
+
|
|
583
|
+
if keyspace && connection.keyspace != keyspace
|
|
584
|
+
switch = switch_keyspace(connection, keyspace, timeout)
|
|
585
|
+
switch.on_complete do |s|
|
|
586
|
+
if s.resolved?
|
|
587
|
+
prepare_and_send_request_by_plan(host,
|
|
588
|
+
connection,
|
|
589
|
+
promise,
|
|
590
|
+
keyspace,
|
|
591
|
+
statement,
|
|
592
|
+
options,
|
|
593
|
+
request,
|
|
594
|
+
plan,
|
|
595
|
+
timeout,
|
|
596
|
+
errors,
|
|
597
|
+
hosts,
|
|
598
|
+
retries)
|
|
599
|
+
else
|
|
600
|
+
s.on_failure do |e|
|
|
601
|
+
if e.is_a?(Errors::HostError) ||
|
|
602
|
+
(e.is_a?(Errors::TimeoutError) && statement.idempotent?)
|
|
603
|
+
errors ||= {}
|
|
604
|
+
errors[host] = e
|
|
605
|
+
execute_by_plan(promise,
|
|
606
|
+
keyspace,
|
|
607
|
+
statement,
|
|
608
|
+
options,
|
|
609
|
+
request,
|
|
610
|
+
plan,
|
|
611
|
+
timeout,
|
|
612
|
+
errors,
|
|
613
|
+
hosts,
|
|
614
|
+
retries)
|
|
615
|
+
else
|
|
616
|
+
promise.break(e)
|
|
617
|
+
end
|
|
618
|
+
end
|
|
619
|
+
end
|
|
620
|
+
end
|
|
621
|
+
else
|
|
622
|
+
prepare_and_send_request_by_plan(host,
|
|
623
|
+
connection,
|
|
624
|
+
promise,
|
|
625
|
+
keyspace,
|
|
626
|
+
statement,
|
|
627
|
+
options,
|
|
628
|
+
request,
|
|
629
|
+
plan,
|
|
630
|
+
timeout,
|
|
631
|
+
errors,
|
|
632
|
+
hosts,
|
|
633
|
+
retries)
|
|
634
|
+
end
|
|
635
|
+
rescue => e
|
|
636
|
+
errors ||= {}
|
|
637
|
+
errors[host] = e
|
|
638
|
+
execute_by_plan(promise,
|
|
639
|
+
keyspace,
|
|
640
|
+
statement,
|
|
641
|
+
options,
|
|
642
|
+
request,
|
|
643
|
+
plan,
|
|
644
|
+
timeout,
|
|
645
|
+
errors,
|
|
646
|
+
hosts,
|
|
647
|
+
retries)
|
|
648
|
+
end
|
|
649
|
+
|
|
650
|
+
def prepare_and_send_request_by_plan(host,
|
|
651
|
+
connection,
|
|
652
|
+
promise,
|
|
653
|
+
keyspace,
|
|
654
|
+
statement,
|
|
655
|
+
options,
|
|
656
|
+
request,
|
|
657
|
+
plan,
|
|
658
|
+
timeout,
|
|
659
|
+
errors,
|
|
660
|
+
hosts,
|
|
661
|
+
retries)
|
|
662
|
+
cql = statement.cql
|
|
663
|
+
|
|
664
|
+
# Get the prepared statement id for this statement from our cache if possible. We are optimistic
|
|
665
|
+
# that the statement has previously been prepared on all hosts, so the id will be valid. However, if
|
|
666
|
+
# we're in the midst of preparing the statement on the given host, we know that executing with the id
|
|
667
|
+
# will fail. So, act like we don't have the prepared-statement id in that case.
|
|
668
|
+
|
|
669
|
+
id = synchronize { @preparing_statements[host][cql] ? nil : @prepared_statements[cql] }
|
|
670
|
+
|
|
671
|
+
if id
|
|
672
|
+
request.id = id
|
|
673
|
+
do_send_request_by_plan(host,
|
|
674
|
+
connection,
|
|
675
|
+
promise,
|
|
676
|
+
keyspace,
|
|
677
|
+
statement,
|
|
678
|
+
options,
|
|
679
|
+
request,
|
|
680
|
+
plan,
|
|
681
|
+
timeout,
|
|
682
|
+
errors,
|
|
683
|
+
hosts,
|
|
684
|
+
retries)
|
|
685
|
+
else
|
|
686
|
+
prepare = prepare_statement(host, connection, cql, timeout)
|
|
687
|
+
prepare.on_complete do |_|
|
|
688
|
+
if prepare.resolved?
|
|
689
|
+
request.id = prepare.value
|
|
690
|
+
do_send_request_by_plan(host,
|
|
691
|
+
connection,
|
|
692
|
+
promise,
|
|
693
|
+
keyspace,
|
|
694
|
+
statement,
|
|
695
|
+
options,
|
|
696
|
+
request,
|
|
697
|
+
plan,
|
|
698
|
+
timeout,
|
|
699
|
+
errors,
|
|
700
|
+
hosts,
|
|
701
|
+
retries)
|
|
702
|
+
else
|
|
703
|
+
prepare.on_failure do |e|
|
|
704
|
+
if e.is_a?(Errors::HostError) ||
|
|
705
|
+
(e.is_a?(Errors::TimeoutError) && statement.idempotent?)
|
|
706
|
+
errors ||= {}
|
|
707
|
+
errors[host] = e
|
|
708
|
+
execute_by_plan(promise,
|
|
709
|
+
keyspace,
|
|
710
|
+
statement,
|
|
711
|
+
options,
|
|
712
|
+
request,
|
|
713
|
+
plan,
|
|
714
|
+
timeout,
|
|
715
|
+
errors,
|
|
716
|
+
hosts,
|
|
717
|
+
retries)
|
|
718
|
+
else
|
|
719
|
+
promise.break(e)
|
|
720
|
+
end
|
|
721
|
+
end
|
|
722
|
+
end
|
|
723
|
+
end
|
|
724
|
+
end
|
|
725
|
+
rescue => e
|
|
726
|
+
promise.break(e)
|
|
727
|
+
end
|
|
728
|
+
|
|
729
|
+
def batch_by_plan(promise,
|
|
730
|
+
keyspace,
|
|
731
|
+
statement,
|
|
732
|
+
options,
|
|
733
|
+
request,
|
|
734
|
+
plan,
|
|
735
|
+
timeout,
|
|
736
|
+
errors = nil,
|
|
737
|
+
hosts = [],
|
|
738
|
+
retries = -1)
|
|
739
|
+
unless plan.has_next?
|
|
740
|
+
promise.break(Errors::NoHostsAvailable.new(errors))
|
|
741
|
+
return
|
|
742
|
+
end
|
|
743
|
+
|
|
744
|
+
hosts << host = plan.next
|
|
745
|
+
retries += 1
|
|
746
|
+
pool = nil
|
|
747
|
+
synchronize { pool = @connections[host] }
|
|
748
|
+
|
|
749
|
+
unless pool
|
|
750
|
+
errors ||= {}
|
|
751
|
+
errors[host] = NOT_CONNECTED
|
|
752
|
+
return batch_by_plan(promise,
|
|
753
|
+
keyspace,
|
|
754
|
+
statement,
|
|
755
|
+
options,
|
|
756
|
+
request,
|
|
757
|
+
plan,
|
|
758
|
+
timeout,
|
|
759
|
+
errors,
|
|
760
|
+
hosts,
|
|
761
|
+
retries)
|
|
762
|
+
end
|
|
763
|
+
|
|
764
|
+
connection = pool.random_connection
|
|
765
|
+
|
|
766
|
+
if keyspace && connection.keyspace != keyspace
|
|
767
|
+
switch = switch_keyspace(connection, keyspace, timeout)
|
|
768
|
+
switch.on_complete do |s|
|
|
769
|
+
if s.resolved?
|
|
770
|
+
batch_and_send_request_by_plan(host,
|
|
771
|
+
connection,
|
|
772
|
+
promise,
|
|
773
|
+
keyspace,
|
|
774
|
+
statement,
|
|
775
|
+
request,
|
|
776
|
+
options,
|
|
777
|
+
plan,
|
|
778
|
+
timeout,
|
|
779
|
+
errors,
|
|
780
|
+
hosts,
|
|
781
|
+
retries)
|
|
782
|
+
else
|
|
783
|
+
s.on_failure do |e|
|
|
784
|
+
if e.is_a?(Errors::HostError) ||
|
|
785
|
+
(e.is_a?(Errors::TimeoutError) && statement.idempotent?)
|
|
786
|
+
errors ||= {}
|
|
787
|
+
errors[host] = e
|
|
788
|
+
batch_by_plan(promise,
|
|
789
|
+
keyspace,
|
|
790
|
+
statement,
|
|
791
|
+
options,
|
|
792
|
+
request,
|
|
793
|
+
plan,
|
|
794
|
+
timeout,
|
|
795
|
+
errors,
|
|
796
|
+
hosts,
|
|
797
|
+
retries)
|
|
798
|
+
else
|
|
799
|
+
promise.break(e)
|
|
800
|
+
end
|
|
801
|
+
end
|
|
802
|
+
end
|
|
803
|
+
end
|
|
804
|
+
else
|
|
805
|
+
batch_and_send_request_by_plan(host,
|
|
806
|
+
connection,
|
|
807
|
+
promise,
|
|
808
|
+
keyspace,
|
|
809
|
+
statement,
|
|
810
|
+
request,
|
|
811
|
+
options,
|
|
812
|
+
plan,
|
|
813
|
+
timeout,
|
|
814
|
+
errors,
|
|
815
|
+
hosts,
|
|
816
|
+
retries)
|
|
817
|
+
end
|
|
818
|
+
rescue => e
|
|
819
|
+
errors ||= {}
|
|
820
|
+
errors[host] = e
|
|
821
|
+
batch_by_plan(promise,
|
|
822
|
+
keyspace,
|
|
823
|
+
statement,
|
|
824
|
+
options,
|
|
825
|
+
request,
|
|
826
|
+
plan,
|
|
827
|
+
timeout,
|
|
828
|
+
errors,
|
|
829
|
+
hosts,
|
|
830
|
+
retries)
|
|
831
|
+
end
|
|
832
|
+
|
|
833
|
+
def batch_and_send_request_by_plan(host,
|
|
834
|
+
connection,
|
|
835
|
+
promise,
|
|
836
|
+
keyspace,
|
|
837
|
+
batch_statement,
|
|
838
|
+
request,
|
|
839
|
+
options,
|
|
840
|
+
plan,
|
|
841
|
+
timeout,
|
|
842
|
+
errors,
|
|
843
|
+
hosts,
|
|
844
|
+
retries)
|
|
845
|
+
request.clear
|
|
846
|
+
unprepared = Hash.new {|hash, cql| hash[cql] = []}
|
|
847
|
+
|
|
848
|
+
batch_statement.statements.each do |statement|
|
|
849
|
+
cql = statement.cql
|
|
850
|
+
|
|
851
|
+
if statement.is_a?(Statements::Bound)
|
|
852
|
+
# Get the prepared statement id for this statement from our cache if possible. We are optimistic
|
|
853
|
+
# that the statement has previously been prepared on all hosts, so the id will be valid. However, if
|
|
854
|
+
# we're in the midst of preparing the statement on the given host, we know that executing with the id
|
|
855
|
+
# will fail. So, act like we don't have the prepared-statement id in that case.
|
|
856
|
+
|
|
857
|
+
id = synchronize { @preparing_statements[host][cql] ? nil : @prepared_statements[cql] }
|
|
858
|
+
|
|
859
|
+
if id
|
|
860
|
+
request.add_prepared(id, statement.params, statement.params_types)
|
|
861
|
+
else
|
|
862
|
+
unprepared[cql] << statement
|
|
863
|
+
end
|
|
864
|
+
else
|
|
865
|
+
request.add_query(cql, statement.params, statement.params_types)
|
|
866
|
+
end
|
|
867
|
+
end
|
|
868
|
+
|
|
869
|
+
if unprepared.empty?
|
|
870
|
+
do_send_request_by_plan(host,
|
|
871
|
+
connection,
|
|
872
|
+
promise,
|
|
873
|
+
keyspace,
|
|
874
|
+
batch_statement,
|
|
875
|
+
options,
|
|
876
|
+
request,
|
|
877
|
+
plan,
|
|
878
|
+
timeout,
|
|
879
|
+
errors,
|
|
880
|
+
hosts,
|
|
881
|
+
retries)
|
|
882
|
+
else
|
|
883
|
+
to_prepare = unprepared.to_a
|
|
884
|
+
futures = to_prepare.map do |cql, _|
|
|
885
|
+
prepare_statement(host, connection, cql, timeout)
|
|
886
|
+
end
|
|
887
|
+
|
|
888
|
+
Ione::Future.all(*futures).on_complete do |f|
|
|
889
|
+
if f.resolved?
|
|
890
|
+
prepared_ids = f.value
|
|
891
|
+
to_prepare.each_with_index do |(_, statements), i|
|
|
892
|
+
statements.each do |statement|
|
|
893
|
+
request.add_prepared(prepared_ids[i],
|
|
894
|
+
statement.params,
|
|
895
|
+
statement.params_types)
|
|
896
|
+
end
|
|
897
|
+
end
|
|
898
|
+
|
|
899
|
+
do_send_request_by_plan(host,
|
|
900
|
+
connection,
|
|
901
|
+
promise,
|
|
902
|
+
keyspace,
|
|
903
|
+
batch_statement,
|
|
904
|
+
options,
|
|
905
|
+
request,
|
|
906
|
+
plan,
|
|
907
|
+
timeout,
|
|
908
|
+
errors,
|
|
909
|
+
hosts,
|
|
910
|
+
retries)
|
|
911
|
+
else
|
|
912
|
+
f.on_failure do |e|
|
|
913
|
+
if e.is_a?(Errors::HostError) ||
|
|
914
|
+
(e.is_a?(Errors::TimeoutError) && batch_statement.idempotent?)
|
|
915
|
+
errors ||= {}
|
|
916
|
+
errors[host] = e
|
|
917
|
+
batch_by_plan(promise,
|
|
918
|
+
keyspace,
|
|
919
|
+
batch_statement,
|
|
920
|
+
options,
|
|
921
|
+
request,
|
|
922
|
+
plan,
|
|
923
|
+
timeout,
|
|
924
|
+
errors,
|
|
925
|
+
hosts,
|
|
926
|
+
retries)
|
|
927
|
+
else
|
|
928
|
+
promise.break(e)
|
|
929
|
+
end
|
|
930
|
+
end
|
|
931
|
+
end
|
|
932
|
+
end
|
|
933
|
+
end
|
|
934
|
+
end
|
|
935
|
+
|
|
936
|
+
def send_request_by_plan(promise,
|
|
937
|
+
keyspace,
|
|
938
|
+
statement,
|
|
939
|
+
options,
|
|
940
|
+
request,
|
|
941
|
+
plan,
|
|
942
|
+
timeout,
|
|
943
|
+
errors = nil,
|
|
944
|
+
hosts = [],
|
|
945
|
+
retries = -1)
|
|
946
|
+
unless plan.has_next?
|
|
947
|
+
promise.break(Errors::NoHostsAvailable.new(errors))
|
|
948
|
+
return
|
|
949
|
+
end
|
|
950
|
+
|
|
951
|
+
hosts << host = plan.next
|
|
952
|
+
retries += 1
|
|
953
|
+
pool = nil
|
|
954
|
+
synchronize { pool = @connections[host] }
|
|
955
|
+
|
|
956
|
+
unless pool
|
|
957
|
+
errors ||= {}
|
|
958
|
+
errors[host] = NOT_CONNECTED
|
|
959
|
+
return send_request_by_plan(promise,
|
|
960
|
+
keyspace,
|
|
961
|
+
statement,
|
|
962
|
+
options,
|
|
963
|
+
request,
|
|
964
|
+
plan,
|
|
965
|
+
timeout,
|
|
966
|
+
errors,
|
|
967
|
+
hosts,
|
|
968
|
+
retries)
|
|
969
|
+
end
|
|
970
|
+
|
|
971
|
+
connection = pool.random_connection
|
|
972
|
+
|
|
973
|
+
if keyspace && connection.keyspace != keyspace
|
|
974
|
+
switch = switch_keyspace(connection, keyspace, timeout)
|
|
975
|
+
switch.on_complete do |s|
|
|
976
|
+
if s.resolved?
|
|
977
|
+
do_send_request_by_plan(host,
|
|
978
|
+
connection,
|
|
979
|
+
promise,
|
|
980
|
+
keyspace,
|
|
981
|
+
statement,
|
|
982
|
+
options,
|
|
983
|
+
request,
|
|
984
|
+
plan,
|
|
985
|
+
timeout,
|
|
986
|
+
errors,
|
|
987
|
+
hosts,
|
|
988
|
+
retries)
|
|
989
|
+
else
|
|
990
|
+
s.on_failure do |e|
|
|
991
|
+
if e.is_a?(Errors::HostError) ||
|
|
992
|
+
(e.is_a?(Errors::TimeoutError) && statement.idempotent?)
|
|
993
|
+
errors ||= {}
|
|
994
|
+
errors[host] = e
|
|
995
|
+
send_request_by_plan(promise,
|
|
996
|
+
keyspace,
|
|
997
|
+
statement,
|
|
998
|
+
options,
|
|
999
|
+
request,
|
|
1000
|
+
plan,
|
|
1001
|
+
timeout,
|
|
1002
|
+
errors,
|
|
1003
|
+
hosts,
|
|
1004
|
+
retries)
|
|
1005
|
+
else
|
|
1006
|
+
promise.break(e)
|
|
1007
|
+
end
|
|
1008
|
+
end
|
|
1009
|
+
end
|
|
1010
|
+
end
|
|
1011
|
+
else
|
|
1012
|
+
do_send_request_by_plan(host,
|
|
1013
|
+
connection,
|
|
1014
|
+
promise,
|
|
1015
|
+
keyspace,
|
|
1016
|
+
statement,
|
|
1017
|
+
options,
|
|
1018
|
+
request,
|
|
1019
|
+
plan,
|
|
1020
|
+
timeout,
|
|
1021
|
+
errors,
|
|
1022
|
+
hosts,
|
|
1023
|
+
retries)
|
|
1024
|
+
end
|
|
1025
|
+
rescue => e
|
|
1026
|
+
errors ||= {}
|
|
1027
|
+
errors[host] = e
|
|
1028
|
+
send_request_by_plan(promise,
|
|
1029
|
+
keyspace,
|
|
1030
|
+
statement,
|
|
1031
|
+
options,
|
|
1032
|
+
request,
|
|
1033
|
+
plan,
|
|
1034
|
+
timeout,
|
|
1035
|
+
errors,
|
|
1036
|
+
hosts,
|
|
1037
|
+
retries)
|
|
1038
|
+
end
|
|
1039
|
+
|
|
1040
|
+
def do_send_request_by_plan(host,
|
|
1041
|
+
connection,
|
|
1042
|
+
promise,
|
|
1043
|
+
keyspace,
|
|
1044
|
+
statement,
|
|
1045
|
+
options,
|
|
1046
|
+
request,
|
|
1047
|
+
plan,
|
|
1048
|
+
timeout,
|
|
1049
|
+
errors,
|
|
1050
|
+
hosts,
|
|
1051
|
+
retries)
|
|
1052
|
+
request.retries = retries
|
|
1053
|
+
|
|
1054
|
+
f = connection.send_request(request, timeout)
|
|
1055
|
+
f.on_complete do |response_future|
|
|
1056
|
+
errors ||= {}
|
|
1057
|
+
handle_response(response_future,
|
|
1058
|
+
host,
|
|
1059
|
+
connection,
|
|
1060
|
+
promise,
|
|
1061
|
+
keyspace,
|
|
1062
|
+
statement,
|
|
1063
|
+
options,
|
|
1064
|
+
request,
|
|
1065
|
+
plan,
|
|
1066
|
+
timeout,
|
|
1067
|
+
errors,
|
|
1068
|
+
hosts,
|
|
1069
|
+
retries)
|
|
1070
|
+
end
|
|
1071
|
+
rescue => e
|
|
1072
|
+
promise.break(e)
|
|
1073
|
+
end
|
|
1074
|
+
|
|
1075
|
+
def handle_response(response_future,
|
|
1076
|
+
host,
|
|
1077
|
+
connection,
|
|
1078
|
+
promise,
|
|
1079
|
+
keyspace,
|
|
1080
|
+
statement,
|
|
1081
|
+
options,
|
|
1082
|
+
request,
|
|
1083
|
+
plan,
|
|
1084
|
+
timeout,
|
|
1085
|
+
errors,
|
|
1086
|
+
hosts,
|
|
1087
|
+
retries)
|
|
1088
|
+
if response_future.resolved?
|
|
1089
|
+
r = response_future.value
|
|
1090
|
+
|
|
1091
|
+
begin
|
|
1092
|
+
decision = nil
|
|
1093
|
+
|
|
1094
|
+
case r
|
|
1095
|
+
when Protocol::UnavailableErrorResponse
|
|
1096
|
+
decision = options.retry_policy.unavailable(statement,
|
|
1097
|
+
r.consistency,
|
|
1098
|
+
r.required,
|
|
1099
|
+
r.alive,
|
|
1100
|
+
retries)
|
|
1101
|
+
when Protocol::WriteTimeoutErrorResponse
|
|
1102
|
+
decision = options.retry_policy.write_timeout(statement,
|
|
1103
|
+
r.consistency,
|
|
1104
|
+
r.write_type,
|
|
1105
|
+
r.blockfor,
|
|
1106
|
+
r.received,
|
|
1107
|
+
retries)
|
|
1108
|
+
when Protocol::ReadTimeoutErrorResponse
|
|
1109
|
+
decision = options.retry_policy.read_timeout(statement,
|
|
1110
|
+
r.consistency,
|
|
1111
|
+
r.blockfor,
|
|
1112
|
+
r.received,
|
|
1113
|
+
r.data_present,
|
|
1114
|
+
retries)
|
|
1115
|
+
when Protocol::UnpreparedErrorResponse
|
|
1116
|
+
cql = nil
|
|
1117
|
+
if statement.is_a?(Cassandra::Statements::Batch)
|
|
1118
|
+
# Find the prepared statement with the prepared-statement-id reported by the node.
|
|
1119
|
+
unprepared_child = statement.statements.select do |s|
|
|
1120
|
+
(s.is_a?(Cassandra::Statements::Prepared) || s.is_a?(Cassandra::Statements::Bound)) && s.id == r.id
|
|
1121
|
+
end.first
|
|
1122
|
+
cql = unprepared_child ? unprepared_child.cql : nil
|
|
1123
|
+
else
|
|
1124
|
+
# This is a normal statement, so we have everything we need.
|
|
1125
|
+
cql = statement.cql
|
|
1126
|
+
synchronize { @preparing_statements[host].delete(cql) }
|
|
1127
|
+
end
|
|
1128
|
+
|
|
1129
|
+
prepare = prepare_statement(host, connection, cql, timeout)
|
|
1130
|
+
prepare.on_complete do |_|
|
|
1131
|
+
if prepare.resolved?
|
|
1132
|
+
request.id = prepare.value unless request.is_a?(Cassandra::Protocol::BatchRequest)
|
|
1133
|
+
do_send_request_by_plan(host,
|
|
1134
|
+
connection,
|
|
1135
|
+
promise,
|
|
1136
|
+
keyspace,
|
|
1137
|
+
statement,
|
|
1138
|
+
options,
|
|
1139
|
+
request,
|
|
1140
|
+
plan,
|
|
1141
|
+
timeout,
|
|
1142
|
+
errors,
|
|
1143
|
+
hosts,
|
|
1144
|
+
retries)
|
|
1145
|
+
else
|
|
1146
|
+
prepare.on_failure do |e|
|
|
1147
|
+
if e.is_a?(Errors::HostError) ||
|
|
1148
|
+
(e.is_a?(Errors::TimeoutError) && statement.idempotent?)
|
|
1149
|
+
errors[host] = e
|
|
1150
|
+
execute_by_plan(promise,
|
|
1151
|
+
keyspace,
|
|
1152
|
+
statement,
|
|
1153
|
+
options,
|
|
1154
|
+
request,
|
|
1155
|
+
plan,
|
|
1156
|
+
timeout,
|
|
1157
|
+
errors,
|
|
1158
|
+
hosts,
|
|
1159
|
+
retries)
|
|
1160
|
+
else
|
|
1161
|
+
promise.break(e)
|
|
1162
|
+
end
|
|
1163
|
+
end
|
|
1164
|
+
end
|
|
1165
|
+
end
|
|
1166
|
+
when Protocol::ErrorResponse
|
|
1167
|
+
error = r.to_error(keyspace,
|
|
1168
|
+
statement,
|
|
1169
|
+
options,
|
|
1170
|
+
hosts,
|
|
1171
|
+
request.consistency,
|
|
1172
|
+
retries)
|
|
1173
|
+
|
|
1174
|
+
if error.is_a?(Errors::HostError) ||
|
|
1175
|
+
(error.is_a?(Errors::TimeoutError) && statement.idempotent?)
|
|
1176
|
+
errors[host] = error
|
|
1177
|
+
|
|
1178
|
+
case request
|
|
1179
|
+
when Protocol::QueryRequest, Protocol::PrepareRequest
|
|
1180
|
+
send_request_by_plan(promise,
|
|
1181
|
+
keyspace,
|
|
1182
|
+
statement,
|
|
1183
|
+
options,
|
|
1184
|
+
request,
|
|
1185
|
+
plan,
|
|
1186
|
+
timeout,
|
|
1187
|
+
errors,
|
|
1188
|
+
hosts,
|
|
1189
|
+
retries)
|
|
1190
|
+
when Protocol::ExecuteRequest
|
|
1191
|
+
execute_by_plan(promise,
|
|
1192
|
+
keyspace,
|
|
1193
|
+
statement,
|
|
1194
|
+
options,
|
|
1195
|
+
request,
|
|
1196
|
+
plan,
|
|
1197
|
+
timeout,
|
|
1198
|
+
errors,
|
|
1199
|
+
hosts,
|
|
1200
|
+
retries)
|
|
1201
|
+
when Protocol::BatchRequest
|
|
1202
|
+
batch_by_plan(promise,
|
|
1203
|
+
keyspace,
|
|
1204
|
+
statement,
|
|
1205
|
+
options,
|
|
1206
|
+
request,
|
|
1207
|
+
plan,
|
|
1208
|
+
timeout,
|
|
1209
|
+
errors,
|
|
1210
|
+
hosts,
|
|
1211
|
+
retries)
|
|
1212
|
+
end
|
|
1213
|
+
else
|
|
1214
|
+
promise.break(error)
|
|
1215
|
+
end
|
|
1216
|
+
when Protocol::SetKeyspaceResultResponse
|
|
1217
|
+
@keyspace = r.keyspace
|
|
1218
|
+
promise.fulfill(Cassandra::Results::Void.new(r.custom_payload,
|
|
1219
|
+
r.warnings,
|
|
1220
|
+
r.trace_id,
|
|
1221
|
+
keyspace,
|
|
1222
|
+
statement,
|
|
1223
|
+
options,
|
|
1224
|
+
hosts,
|
|
1225
|
+
request.consistency,
|
|
1226
|
+
retries,
|
|
1227
|
+
self,
|
|
1228
|
+
@futures))
|
|
1229
|
+
when Protocol::PreparedResultResponse
|
|
1230
|
+
cql = request.cql
|
|
1231
|
+
synchronize do
|
|
1232
|
+
@prepared_statements[cql] = r.id
|
|
1233
|
+
@preparing_statements[host].delete(cql)
|
|
1234
|
+
end
|
|
1235
|
+
|
|
1236
|
+
metadata = r.metadata
|
|
1237
|
+
pk_idx = r.pk_idx
|
|
1238
|
+
pk_idx ||= @schema.get_pk_idx(metadata)
|
|
1239
|
+
|
|
1240
|
+
promise.fulfill(
|
|
1241
|
+
Statements::Prepared.new(r.id,
|
|
1242
|
+
r.custom_payload,
|
|
1243
|
+
r.warnings,
|
|
1244
|
+
cql,
|
|
1245
|
+
metadata,
|
|
1246
|
+
r.result_metadata,
|
|
1247
|
+
pk_idx,
|
|
1248
|
+
r.trace_id,
|
|
1249
|
+
keyspace,
|
|
1250
|
+
statement,
|
|
1251
|
+
options,
|
|
1252
|
+
hosts,
|
|
1253
|
+
request.consistency,
|
|
1254
|
+
retries,
|
|
1255
|
+
self,
|
|
1256
|
+
@connection_options)
|
|
1257
|
+
)
|
|
1258
|
+
when Protocol::RawRowsResultResponse
|
|
1259
|
+
r.materialize(statement.result_metadata)
|
|
1260
|
+
promise.fulfill(
|
|
1261
|
+
Results::Paged.new(r.custom_payload,
|
|
1262
|
+
r.warnings,
|
|
1263
|
+
r.rows,
|
|
1264
|
+
r.paging_state,
|
|
1265
|
+
r.trace_id,
|
|
1266
|
+
keyspace,
|
|
1267
|
+
statement,
|
|
1268
|
+
options,
|
|
1269
|
+
hosts,
|
|
1270
|
+
request.consistency,
|
|
1271
|
+
retries,
|
|
1272
|
+
self,
|
|
1273
|
+
@futures)
|
|
1274
|
+
)
|
|
1275
|
+
when Protocol::RowsResultResponse
|
|
1276
|
+
promise.fulfill(
|
|
1277
|
+
Results::Paged.new(r.custom_payload,
|
|
1278
|
+
r.warnings,
|
|
1279
|
+
r.rows,
|
|
1280
|
+
r.paging_state,
|
|
1281
|
+
r.trace_id,
|
|
1282
|
+
keyspace,
|
|
1283
|
+
statement,
|
|
1284
|
+
options,
|
|
1285
|
+
hosts,
|
|
1286
|
+
request.consistency,
|
|
1287
|
+
retries,
|
|
1288
|
+
self,
|
|
1289
|
+
@futures)
|
|
1290
|
+
)
|
|
1291
|
+
when Protocol::SchemaChangeResultResponse
|
|
1292
|
+
if r.change == 'DROPPED' &&
|
|
1293
|
+
r.target == Protocol::Constants::SCHEMA_CHANGE_TARGET_KEYSPACE
|
|
1294
|
+
@schema.delete_keyspace(r.keyspace)
|
|
1295
|
+
end
|
|
1296
|
+
|
|
1297
|
+
@logger.debug('Waiting for schema to propagate to all hosts after a change')
|
|
1298
|
+
wait_for_schema_agreement(connection,
|
|
1299
|
+
@reconnection_policy.schedule).on_complete do |f|
|
|
1300
|
+
unless f.resolved?
|
|
1301
|
+
f.on_failure do |e|
|
|
1302
|
+
@logger.error(
|
|
1303
|
+
"Schema agreement failure (#{e.class.name}: #{e.message})"
|
|
1304
|
+
)
|
|
1305
|
+
end
|
|
1306
|
+
end
|
|
1307
|
+
promise.fulfill(
|
|
1308
|
+
Results::Void.new(r.custom_payload,
|
|
1309
|
+
r.warnings,
|
|
1310
|
+
r.trace_id,
|
|
1311
|
+
keyspace,
|
|
1312
|
+
statement,
|
|
1313
|
+
options,
|
|
1314
|
+
hosts,
|
|
1315
|
+
request.consistency,
|
|
1316
|
+
retries,
|
|
1317
|
+
self,
|
|
1318
|
+
@futures)
|
|
1319
|
+
)
|
|
1320
|
+
end
|
|
1321
|
+
else
|
|
1322
|
+
promise.fulfill(Results::Void.new(r.custom_payload,
|
|
1323
|
+
r.warnings,
|
|
1324
|
+
r.trace_id,
|
|
1325
|
+
keyspace,
|
|
1326
|
+
statement,
|
|
1327
|
+
options,
|
|
1328
|
+
hosts,
|
|
1329
|
+
request.consistency,
|
|
1330
|
+
retries,
|
|
1331
|
+
self,
|
|
1332
|
+
@futures))
|
|
1333
|
+
end
|
|
1334
|
+
|
|
1335
|
+
if decision
|
|
1336
|
+
case decision
|
|
1337
|
+
when Retry::Decisions::Retry
|
|
1338
|
+
request.consistency = decision.consistency
|
|
1339
|
+
do_send_request_by_plan(host,
|
|
1340
|
+
connection,
|
|
1341
|
+
promise,
|
|
1342
|
+
keyspace,
|
|
1343
|
+
statement,
|
|
1344
|
+
options,
|
|
1345
|
+
request,
|
|
1346
|
+
plan,
|
|
1347
|
+
timeout,
|
|
1348
|
+
errors,
|
|
1349
|
+
hosts,
|
|
1350
|
+
retries + 1)
|
|
1351
|
+
when Retry::Decisions::TryNextHost
|
|
1352
|
+
errors[host] = r.to_error(keyspace,
|
|
1353
|
+
statement,
|
|
1354
|
+
options,
|
|
1355
|
+
hosts,
|
|
1356
|
+
request.consistency,
|
|
1357
|
+
retries)
|
|
1358
|
+
case request
|
|
1359
|
+
when Protocol::QueryRequest, Protocol::PrepareRequest
|
|
1360
|
+
send_request_by_plan(promise,
|
|
1361
|
+
keyspace,
|
|
1362
|
+
statement,
|
|
1363
|
+
options,
|
|
1364
|
+
request,
|
|
1365
|
+
plan,
|
|
1366
|
+
timeout,
|
|
1367
|
+
errors,
|
|
1368
|
+
hosts,
|
|
1369
|
+
retries)
|
|
1370
|
+
when Protocol::ExecuteRequest
|
|
1371
|
+
execute_by_plan(promise,
|
|
1372
|
+
keyspace,
|
|
1373
|
+
statement,
|
|
1374
|
+
options,
|
|
1375
|
+
request,
|
|
1376
|
+
plan,
|
|
1377
|
+
timeout,
|
|
1378
|
+
errors,
|
|
1379
|
+
hosts,
|
|
1380
|
+
retries)
|
|
1381
|
+
when Protocol::BatchRequest
|
|
1382
|
+
batch_by_plan(promise,
|
|
1383
|
+
keyspace,
|
|
1384
|
+
statement,
|
|
1385
|
+
options,
|
|
1386
|
+
request,
|
|
1387
|
+
plan,
|
|
1388
|
+
timeout,
|
|
1389
|
+
errors,
|
|
1390
|
+
hosts,
|
|
1391
|
+
retries)
|
|
1392
|
+
else
|
|
1393
|
+
promise.break(e)
|
|
1394
|
+
end
|
|
1395
|
+
when Retry::Decisions::Ignore
|
|
1396
|
+
promise.fulfill(
|
|
1397
|
+
Results::Void.new(r.custom_payload,
|
|
1398
|
+
r.warnings,
|
|
1399
|
+
nil,
|
|
1400
|
+
keyspace,
|
|
1401
|
+
statement,
|
|
1402
|
+
options,
|
|
1403
|
+
hosts,
|
|
1404
|
+
request.consistency,
|
|
1405
|
+
retries,
|
|
1406
|
+
self,
|
|
1407
|
+
@futures)
|
|
1408
|
+
)
|
|
1409
|
+
when Retry::Decisions::Reraise
|
|
1410
|
+
promise.break(
|
|
1411
|
+
r.to_error(keyspace,
|
|
1412
|
+
statement,
|
|
1413
|
+
options,
|
|
1414
|
+
hosts,
|
|
1415
|
+
request.consistency,
|
|
1416
|
+
retries)
|
|
1417
|
+
)
|
|
1418
|
+
else
|
|
1419
|
+
promise.break(
|
|
1420
|
+
r.to_error(keyspace,
|
|
1421
|
+
statement,
|
|
1422
|
+
options,
|
|
1423
|
+
hosts,
|
|
1424
|
+
request.consistency,
|
|
1425
|
+
retries)
|
|
1426
|
+
)
|
|
1427
|
+
end
|
|
1428
|
+
end
|
|
1429
|
+
rescue => e
|
|
1430
|
+
promise.break(e)
|
|
1431
|
+
end
|
|
1432
|
+
else
|
|
1433
|
+
response_future.on_failure do |ex|
|
|
1434
|
+
if ex.is_a?(Errors::HostError) ||
|
|
1435
|
+
(ex.is_a?(Errors::TimeoutError) && statement.idempotent?)
|
|
1436
|
+
|
|
1437
|
+
errors[host] = ex
|
|
1438
|
+
case request
|
|
1439
|
+
when Protocol::QueryRequest, Protocol::PrepareRequest
|
|
1440
|
+
send_request_by_plan(promise,
|
|
1441
|
+
keyspace,
|
|
1442
|
+
statement,
|
|
1443
|
+
options,
|
|
1444
|
+
request,
|
|
1445
|
+
plan,
|
|
1446
|
+
timeout,
|
|
1447
|
+
errors,
|
|
1448
|
+
hosts,
|
|
1449
|
+
retries)
|
|
1450
|
+
when Protocol::ExecuteRequest
|
|
1451
|
+
execute_by_plan(promise,
|
|
1452
|
+
keyspace,
|
|
1453
|
+
statement,
|
|
1454
|
+
options,
|
|
1455
|
+
request,
|
|
1456
|
+
plan,
|
|
1457
|
+
timeout,
|
|
1458
|
+
errors,
|
|
1459
|
+
hosts,
|
|
1460
|
+
retries)
|
|
1461
|
+
when Protocol::BatchRequest
|
|
1462
|
+
batch_by_plan(promise,
|
|
1463
|
+
keyspace,
|
|
1464
|
+
statement,
|
|
1465
|
+
options,
|
|
1466
|
+
request,
|
|
1467
|
+
plan,
|
|
1468
|
+
timeout,
|
|
1469
|
+
errors,
|
|
1470
|
+
hosts,
|
|
1471
|
+
retries)
|
|
1472
|
+
else
|
|
1473
|
+
promise.break(ex)
|
|
1474
|
+
end
|
|
1475
|
+
else
|
|
1476
|
+
promise.break(ex)
|
|
1477
|
+
end
|
|
1478
|
+
end
|
|
1479
|
+
end
|
|
1480
|
+
end
|
|
1481
|
+
|
|
1482
|
+
def wait_for_schema_agreement(connection, schedule)
|
|
1483
|
+
peers_future = send_select_request(connection, SELECT_SCHEMA_PEERS)
|
|
1484
|
+
local_future = send_select_request(connection, SELECT_SCHEMA_LOCAL)
|
|
1485
|
+
|
|
1486
|
+
Ione::Future.all(peers_future, local_future).flat_map do |(peers, local)|
|
|
1487
|
+
versions = ::Set.new
|
|
1488
|
+
|
|
1489
|
+
unless local.empty?
|
|
1490
|
+
host = @registry.host(connection.host)
|
|
1491
|
+
|
|
1492
|
+
if host && @profile_manager.distance(host) != :ignore
|
|
1493
|
+
versions << version = local.first['schema_version']
|
|
1494
|
+
@logger.debug("Host #{host.ip} schema version is #{version}")
|
|
1495
|
+
end
|
|
1496
|
+
end
|
|
1497
|
+
|
|
1498
|
+
peers.each do |row|
|
|
1499
|
+
host = @registry.host(peer_ip(row))
|
|
1500
|
+
next unless host && @profile_manager.distance(host) != :ignore
|
|
1501
|
+
|
|
1502
|
+
versions << version = row['schema_version']
|
|
1503
|
+
@logger.debug("Host #{host.ip} schema version is #{version}")
|
|
1504
|
+
end
|
|
1505
|
+
|
|
1506
|
+
if versions.one?
|
|
1507
|
+
@logger.debug('All hosts have the same schema')
|
|
1508
|
+
Ione::Future.resolved
|
|
1509
|
+
else
|
|
1510
|
+
interval = schedule.next
|
|
1511
|
+
@logger.info('Hosts have different schema versions: ' \
|
|
1512
|
+
"#{versions.to_a.inspect}, retrying in #{interval} seconds")
|
|
1513
|
+
@reactor.schedule_timer(interval).flat_map do
|
|
1514
|
+
wait_for_schema_agreement(connection, schedule)
|
|
1515
|
+
end
|
|
1516
|
+
end
|
|
1517
|
+
end
|
|
1518
|
+
end
|
|
1519
|
+
|
|
1520
|
+
def peer_ip(data)
|
|
1521
|
+
ip = data['rpc_address']
|
|
1522
|
+
ip = data['peer'] if ip == '0.0.0.0'
|
|
1523
|
+
|
|
1524
|
+
@address_resolver.resolve(ip)
|
|
1525
|
+
end
|
|
1526
|
+
|
|
1527
|
+
def switch_keyspace(connection, keyspace, timeout)
|
|
1528
|
+
pending_keyspace = connection[:pending_keyspace]
|
|
1529
|
+
pending_switch = connection[:pending_switch]
|
|
1530
|
+
|
|
1531
|
+
return pending_switch || Ione::Future.resolved if pending_keyspace == keyspace
|
|
1532
|
+
|
|
1533
|
+
request = Protocol::QueryRequest.new("USE #{Util.escape_name(keyspace)}",
|
|
1534
|
+
EMPTY_LIST,
|
|
1535
|
+
EMPTY_LIST,
|
|
1536
|
+
:quorum)
|
|
1537
|
+
|
|
1538
|
+
f = connection.send_request(request, timeout).map do |r|
|
|
1539
|
+
case r
|
|
1540
|
+
when Protocol::SetKeyspaceResultResponse
|
|
1541
|
+
@keyspace = r.keyspace
|
|
1542
|
+
nil
|
|
1543
|
+
when Protocol::ErrorResponse
|
|
1544
|
+
raise r.to_error(nil,
|
|
1545
|
+
Statements::Simple.new("USE #{Util.escape_name(keyspace)}"),
|
|
1546
|
+
VOID_OPTIONS,
|
|
1547
|
+
EMPTY_LIST,
|
|
1548
|
+
:quorum,
|
|
1549
|
+
0)
|
|
1550
|
+
else
|
|
1551
|
+
raise Errors::InternalError, "Unexpected response #{r.inspect}"
|
|
1552
|
+
end
|
|
1553
|
+
end
|
|
1554
|
+
|
|
1555
|
+
connection[:pending_keyspace] = keyspace
|
|
1556
|
+
connection[:pending_switch] = f
|
|
1557
|
+
|
|
1558
|
+
f.on_complete do |_f|
|
|
1559
|
+
connection[:pending_switch] = nil
|
|
1560
|
+
connection[:pending_keyspace] = nil
|
|
1561
|
+
end
|
|
1562
|
+
|
|
1563
|
+
f
|
|
1564
|
+
end
|
|
1565
|
+
|
|
1566
|
+
def prepare_statement(host, connection, cql, timeout)
|
|
1567
|
+
synchronize do
|
|
1568
|
+
pending = @preparing_statements[host]
|
|
1569
|
+
|
|
1570
|
+
return pending[cql] if pending.key?(cql)
|
|
1571
|
+
end
|
|
1572
|
+
|
|
1573
|
+
request = Protocol::PrepareRequest.new(cql, false)
|
|
1574
|
+
|
|
1575
|
+
f = connection.send_request(request, timeout).map do |r|
|
|
1576
|
+
case r
|
|
1577
|
+
when Protocol::PreparedResultResponse
|
|
1578
|
+
id = r.id
|
|
1579
|
+
synchronize do
|
|
1580
|
+
@prepared_statements[cql] = id
|
|
1581
|
+
@preparing_statements[host].delete(cql)
|
|
1582
|
+
end
|
|
1583
|
+
id
|
|
1584
|
+
when Protocol::ErrorResponse
|
|
1585
|
+
raise r.to_error(nil, VOID_STATEMENT, VOID_OPTIONS, EMPTY_LIST, :quorum, 0)
|
|
1586
|
+
else
|
|
1587
|
+
raise Errors::InternalError, "Unexpected response #{r.inspect}"
|
|
1588
|
+
end
|
|
1589
|
+
end
|
|
1590
|
+
|
|
1591
|
+
synchronize do
|
|
1592
|
+
@preparing_statements[host][cql] = f
|
|
1593
|
+
end
|
|
1594
|
+
|
|
1595
|
+
f
|
|
1596
|
+
end
|
|
1597
|
+
|
|
1598
|
+
def send_select_request(connection, request)
|
|
1599
|
+
connection.send_request(request).map do |r|
|
|
1600
|
+
case r
|
|
1601
|
+
when Protocol::RowsResultResponse
|
|
1602
|
+
r.rows
|
|
1603
|
+
when Protocol::ErrorResponse
|
|
1604
|
+
raise r.to_error(nil, VOID_STATEMENT, VOID_OPTIONS, EMPTY_LIST, :quorum, 0)
|
|
1605
|
+
else
|
|
1606
|
+
raise Errors::InternalError, "Unexpected response #{r.inspect}"
|
|
1607
|
+
end
|
|
1608
|
+
end
|
|
1609
|
+
end
|
|
1610
|
+
end
|
|
1611
|
+
end
|
|
1612
|
+
end
|