yugabyte-ycql-driver 3.2.3.1
Sign up to get free protection for your applications and to get access to all the features.
- 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,110 @@
|
|
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
|
+
# Represents a UUID value.
|
21
|
+
#
|
22
|
+
# This is a very basic implementation of UUIDs and exists more or less just
|
23
|
+
# to encode and decode UUIDs from and to Cassandra.
|
24
|
+
#
|
25
|
+
# If you want to generate UUIDs see {Cassandra::Uuid::Generator}.
|
26
|
+
#
|
27
|
+
class Uuid
|
28
|
+
# @private
|
29
|
+
RAW_FORMAT = '%032x'.force_encoding(Encoding::ASCII).freeze
|
30
|
+
# @private
|
31
|
+
HYPHEN = '-'.force_encoding(Encoding::ASCII).freeze
|
32
|
+
# @private
|
33
|
+
EMPTY_STRING = ''.freeze
|
34
|
+
|
35
|
+
# Creates a new UUID either from a string (expected to be on the standard 8-4-4-4-12
|
36
|
+
# form, or just 32 characters without hyphens), or from a 128 bit number.
|
37
|
+
#
|
38
|
+
# @param uuid [String] a 32 char uuid
|
39
|
+
#
|
40
|
+
# @raise [ArgumentError] if the string does not conform to the expected format
|
41
|
+
#
|
42
|
+
def initialize(uuid)
|
43
|
+
@n = case uuid
|
44
|
+
when String
|
45
|
+
from_s(uuid)
|
46
|
+
else
|
47
|
+
uuid
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns a string representation of this UUID in the standard 8-4-4-4-12 form.
|
52
|
+
#
|
53
|
+
def to_s
|
54
|
+
@s ||= begin
|
55
|
+
s = RAW_FORMAT % @n
|
56
|
+
s.insert(20, HYPHEN)
|
57
|
+
s.insert(16, HYPHEN)
|
58
|
+
s.insert(12, HYPHEN)
|
59
|
+
s.insert(8, HYPHEN)
|
60
|
+
s
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# @private
|
65
|
+
def hash
|
66
|
+
@h ||= begin
|
67
|
+
h = 17
|
68
|
+
h = 31 * h + @n.hash
|
69
|
+
h
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns the numerical representation of this UUID
|
74
|
+
#
|
75
|
+
# @return [Bignum] the 128 bit numerical representation
|
76
|
+
#
|
77
|
+
def value
|
78
|
+
@n
|
79
|
+
end
|
80
|
+
alias to_i value
|
81
|
+
|
82
|
+
# @private
|
83
|
+
def eql?(other)
|
84
|
+
other.respond_to?(:value) && value == other.value
|
85
|
+
end
|
86
|
+
alias == eql?
|
87
|
+
|
88
|
+
if RUBY_ENGINE == 'jruby'
|
89
|
+
# @private
|
90
|
+
HEX_RE = /^[A-Fa-f0-9]+$/
|
91
|
+
# See https://github.com/jruby/jruby/issues/1608
|
92
|
+
# @private
|
93
|
+
def from_s(str)
|
94
|
+
str = str.gsub(HYPHEN, EMPTY_STRING)
|
95
|
+
raise ::ArgumentError, "Expected 32 hexadecimal digits but got #{str.length}" unless str.length == 32
|
96
|
+
raise ::ArgumentError, "invalid value for Integer(): \"#{str}\"" unless str =~ HEX_RE
|
97
|
+
Integer(str, 16)
|
98
|
+
end
|
99
|
+
else
|
100
|
+
# @private
|
101
|
+
def from_s(str)
|
102
|
+
str = str.gsub(HYPHEN, EMPTY_STRING)
|
103
|
+
raise ::ArgumentError, "Expected 32 hexadecimal digits but got #{str.length}" unless str.length == 32
|
104
|
+
Integer(str, 16)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
require 'cassandra/uuid/generator'
|
@@ -0,0 +1,212 @@
|
|
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 Uuid
|
21
|
+
# A UUID generator.
|
22
|
+
#
|
23
|
+
# This class can be used to genereate Apache Cassandra timeuuid and uuid
|
24
|
+
# values.
|
25
|
+
#
|
26
|
+
# @see Cassandra::Uuid::Generator#now Generating a sequence time UUIDs
|
27
|
+
# with reasonable uniqueness guarantees.
|
28
|
+
# @see Cassandra::Uuid::Generator#at Generating a time UUID for a given
|
29
|
+
# time object or unix timestamp.
|
30
|
+
# @see Cassandra::Uuid::Generator#uuid Generating completely random v4
|
31
|
+
# UUIDs.
|
32
|
+
#
|
33
|
+
# @note Instances of this class are absolutely not threadsafe. You should
|
34
|
+
# never share instances between threads.
|
35
|
+
#
|
36
|
+
class Generator
|
37
|
+
# Create a new UUID generator.
|
38
|
+
#
|
39
|
+
# The clock ID and node ID components are set to random numbers when the
|
40
|
+
# generator is created. These are used for generation of time UUIDs only.
|
41
|
+
#
|
42
|
+
# @param [Integer] node_id an alternate node ID
|
43
|
+
# @param [Integer] clock_id an alternate clock ID
|
44
|
+
# @param [Object<#now>] clock used to generate timeuuid from current time
|
45
|
+
#
|
46
|
+
# @raise [ArgumentError] if clock doesn't respond to `now`
|
47
|
+
def initialize(node_id = (::SecureRandom.random_number(2**47) | 0x010000000000),
|
48
|
+
clock_id = ::SecureRandom.random_number(65536),
|
49
|
+
clock = ::Time)
|
50
|
+
raise ::ArgumentError, 'invalid clock' unless clock.respond_to?(:now)
|
51
|
+
|
52
|
+
@node_id = Integer(node_id)
|
53
|
+
@clock_id = Integer(clock_id)
|
54
|
+
@clock = clock
|
55
|
+
@last_usecs = nil
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns a new UUID with a time component that is the current time.
|
59
|
+
#
|
60
|
+
# If two calls to {#now} happen within the time afforded by the system
|
61
|
+
# clock resolution a counter is incremented and added to the time
|
62
|
+
# component.
|
63
|
+
#
|
64
|
+
# If the clock moves backwards the clock ID is reset to a new random
|
65
|
+
# number.
|
66
|
+
#
|
67
|
+
# @example Creating a sequential TimeUuids for the current time
|
68
|
+
# generator = Cassandra::Uuid::Generator.new
|
69
|
+
# timeuuids = 5.times.map { generator.now }
|
70
|
+
#
|
71
|
+
# puts timeuuids.zip(timeuuids.map(&:to_time)).map(&:inspect)
|
72
|
+
#
|
73
|
+
# # Outputs:
|
74
|
+
# # [8614b7d0-5646-11e4-8e54-6761d3995ef3, 2014-10-17 21:42:42 UTC]
|
75
|
+
# # [8614b91a-5646-11e4-8e54-6761d3995ef3, 2014-10-17 21:42:42 UTC]
|
76
|
+
# # [8614b960-5646-11e4-8e54-6761d3995ef3, 2014-10-17 21:42:42 UTC]
|
77
|
+
# # [8614b99c-5646-11e4-8e54-6761d3995ef3, 2014-10-17 21:42:42 UTC]
|
78
|
+
# # [8614b9ce-5646-11e4-8e54-6761d3995ef3, 2014-10-17 21:42:42 UTC]
|
79
|
+
#
|
80
|
+
# @see Time.now
|
81
|
+
#
|
82
|
+
# @return [Cassandra::TimeUuid] a new UUID
|
83
|
+
def now
|
84
|
+
now = @clock.now
|
85
|
+
usecs = now.to_i * 1_000_000 + now.usec
|
86
|
+
|
87
|
+
if @last_usecs && @last_usecs - @sequence <= usecs && usecs <= @last_usecs
|
88
|
+
@sequence += 1
|
89
|
+
elsif @last_usecs && @last_usecs > usecs
|
90
|
+
@sequence = 0
|
91
|
+
@clock_id = ::SecureRandom.random_number(65536)
|
92
|
+
else
|
93
|
+
@sequence = 0
|
94
|
+
end
|
95
|
+
|
96
|
+
@last_usecs = usecs + @sequence
|
97
|
+
from_usecs(@last_usecs)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Returns a new UUID with a time component based on the specified Time.
|
101
|
+
# A piece of jitter is added to ensure that multiple calls with the same
|
102
|
+
# time do not generate the same UUID (if you want determinism you can set
|
103
|
+
# the second parameter to zero).
|
104
|
+
#
|
105
|
+
# @overload at(time, jitter = SecureRandom.random_number(65536))
|
106
|
+
# @param [Time] time a Time instance
|
107
|
+
# @param [Integer] jitter a number of microseconds to add to the time
|
108
|
+
# @return [Cassandra::TimeUuid] a new UUID
|
109
|
+
# @overload at(seconds_with_frac, jitter = SecureRandom.random_number(65536))
|
110
|
+
# @param [Numeric] seconds_with_frac can be {Integer}, {Float},
|
111
|
+
# {Rational}, or other {Numeric}
|
112
|
+
# @param [Integer] jitter a number of microseconds to add to the time
|
113
|
+
# @return [Cassandra::TimeUuid] a new UUID
|
114
|
+
# @overload at(seconds, microseconds_with_frac, jitter = SecureRandom.random_number(65536))
|
115
|
+
# @param [Integer] seconds
|
116
|
+
# @param [Numeric] microseconds_with_frac can be {Integer}, {Float},
|
117
|
+
# {Rational}, or other {Numeric}
|
118
|
+
# @param [Integer] jitter a number of microseconds to add to the time
|
119
|
+
# @return [Cassandra::TimeUuid] a new UUID
|
120
|
+
#
|
121
|
+
# @note the `jitter` argument accepted by all variants of this method is
|
122
|
+
# required to add randomness to generated {Cassandra::TimeUuid} and
|
123
|
+
# might affect the order of generated timestamps. You should set
|
124
|
+
# `jitter` to 0 when the source time(stamp)s are unique.
|
125
|
+
#
|
126
|
+
# @example Creating a TimeUuid from a Time instance
|
127
|
+
# generator = Cassandra::Uuid::Generator.new
|
128
|
+
# timeuuid = generator.at(Time.at(1413582460))
|
129
|
+
#
|
130
|
+
# puts timeuuid.to_time
|
131
|
+
#
|
132
|
+
# # Outputs:
|
133
|
+
# # 2014-10-17 21:47:40 UTC
|
134
|
+
#
|
135
|
+
# @example Creating a TimeUuid from a timestamp
|
136
|
+
# generator = Cassandra::Uuid::Generator.new
|
137
|
+
# timeuuid = generator.at(1413582423)
|
138
|
+
#
|
139
|
+
# puts timeuuid.to_time
|
140
|
+
#
|
141
|
+
# # Outputs:
|
142
|
+
# # 2014-10-17 21:47:03 UTC
|
143
|
+
#
|
144
|
+
# @example Avoid jitter in generated TimeUuid
|
145
|
+
# timestamp = 1413582418
|
146
|
+
# generator = Cassandra::Uuid::Generator.new
|
147
|
+
# timeuuid = generator.at(timestamp, 0)
|
148
|
+
#
|
149
|
+
# puts timeuuid.to_time.to_i
|
150
|
+
#
|
151
|
+
# # Outputs:
|
152
|
+
# # 1413582418
|
153
|
+
#
|
154
|
+
# @raise [ArgumentError] when given no arguments or more than 3 arguments
|
155
|
+
#
|
156
|
+
# @see Time.at
|
157
|
+
def at(*args)
|
158
|
+
raise ::ArgumentError, 'not enough arguments' if args.empty?
|
159
|
+
raise ::ArgumentError, 'too many arguments' if args.size > 3
|
160
|
+
|
161
|
+
if args.first.is_a?(::Time)
|
162
|
+
time = args.shift
|
163
|
+
jitter = args.empty? ? ::SecureRandom.random_number(65536) : Integer(args.shift)
|
164
|
+
else
|
165
|
+
jitter = args.size > 2 ? Integer(args.pop) : ::SecureRandom.random_number(65536)
|
166
|
+
time = ::Time.at(*args)
|
167
|
+
end
|
168
|
+
|
169
|
+
from_usecs(time.to_i * 1_000_000 + time.usec + jitter)
|
170
|
+
end
|
171
|
+
|
172
|
+
# Returns a completely random version 4 UUID.
|
173
|
+
#
|
174
|
+
# @example Generating a random Uuid
|
175
|
+
# generator = Cassandra::Uuid::Generator.new
|
176
|
+
# uuid = generator.uuid
|
177
|
+
#
|
178
|
+
# puts uuid
|
179
|
+
#
|
180
|
+
# # Outputs:
|
181
|
+
# # 664dedae-e162-4bc0-9066-b9f1968252aa
|
182
|
+
#
|
183
|
+
# @see SecureRandom.uuid
|
184
|
+
#
|
185
|
+
# @return [Cassandra::Uuid] a new UUID
|
186
|
+
def uuid
|
187
|
+
Uuid.new(::SecureRandom.uuid)
|
188
|
+
end
|
189
|
+
|
190
|
+
private
|
191
|
+
|
192
|
+
# @private
|
193
|
+
def from_usecs(usecs)
|
194
|
+
t = TimeUuid::GREGORIAN_OFFSET + usecs * 10
|
195
|
+
time_hi = t & 0x0fff000000000000
|
196
|
+
time_mid = t & 0x0000ffff00000000
|
197
|
+
time_low = t & 0x00000000ffffffff
|
198
|
+
version = 1
|
199
|
+
clock_id = @clock_id & 0x3fff
|
200
|
+
node_id = @node_id & 0xffffffffffff
|
201
|
+
variant = 0x8000
|
202
|
+
|
203
|
+
n = (time_low << 96) | (time_mid << 48) | (time_hi << 16)
|
204
|
+
n |= version << 76
|
205
|
+
n |= (clock_id | variant) << 48
|
206
|
+
n |= node_id
|
207
|
+
|
208
|
+
TimeUuid.new(n)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
@@ -0,0 +1,21 @@
|
|
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
|
+
VERSION = '3.2.3.1'.freeze
|
21
|
+
end
|
@@ -0,0 +1,47 @@
|
|
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 DataStax
|
20
|
+
@base = __FILE__ + '/../..'
|
21
|
+
|
22
|
+
def self.require(path)
|
23
|
+
if path.start_with?('cassandra/')
|
24
|
+
include(path)
|
25
|
+
else
|
26
|
+
::Kernel.require(path)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.include(path)
|
31
|
+
path = File.expand_path(path + '.rb', @base)
|
32
|
+
class_eval(File.read(path), path, 1)
|
33
|
+
end
|
34
|
+
|
35
|
+
previous = nil
|
36
|
+
murmur3 = nil
|
37
|
+
if defined?(::Cassandra)
|
38
|
+
previous = ::Cassandra
|
39
|
+
murmur3 = ::Cassandra::Murmur3
|
40
|
+
Object.send(:remove_const, :Cassandra)
|
41
|
+
end
|
42
|
+
include 'cassandra'
|
43
|
+
murmur3 ||= ::Cassandra::Murmur3
|
44
|
+
DataStax::Cassandra::Murmur3 = murmur3
|
45
|
+
Object.send(:remove_const, :Cassandra) if defined?(::Cassandra)
|
46
|
+
::Cassandra = previous if previous
|
47
|
+
end
|
data/lib/ycql.rb
ADDED
@@ -0,0 +1,842 @@
|
|
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 'ione'
|
20
|
+
require 'json'
|
21
|
+
|
22
|
+
require 'monitor'
|
23
|
+
require 'ipaddr'
|
24
|
+
require 'set'
|
25
|
+
require 'bigdecimal'
|
26
|
+
require 'forwardable'
|
27
|
+
require 'digest'
|
28
|
+
require 'stringio'
|
29
|
+
require 'resolv'
|
30
|
+
require 'openssl'
|
31
|
+
require 'securerandom'
|
32
|
+
require 'time'
|
33
|
+
require 'date'
|
34
|
+
|
35
|
+
module Cassandra
|
36
|
+
# A list of all supported request consistencies
|
37
|
+
# @see http://www.datastax.com/documentation/cassandra/2.0/cassandra/dml/dml_config_consistency_c.html Consistency
|
38
|
+
# levels in Apache Cassandra 2.0
|
39
|
+
# @see http://www.datastax.com/documentation/cassandra/1.2/cassandra/dml/dml_config_consistency_c.html Consistency
|
40
|
+
# levels in Apache Cassandra 1.2
|
41
|
+
# @see Cassandra::Session#execute_async
|
42
|
+
CONSISTENCIES = [:any, :one, :two, :three, :quorum, :all, :local_quorum,
|
43
|
+
:each_quorum, :serial, :local_serial, :local_one].freeze
|
44
|
+
|
45
|
+
# A list of all supported serial consistencies
|
46
|
+
# @see Cassandra::Session#execute_async
|
47
|
+
SERIAL_CONSISTENCIES = [:serial, :local_serial].freeze
|
48
|
+
|
49
|
+
# A list of all possible write types that a
|
50
|
+
# {Cassandra::Errors::WriteTimeoutError} can have.
|
51
|
+
#
|
52
|
+
# @see https://github.com/apache/cassandra/blob/cassandra-2.0.16/doc/native_protocol_v2.spec#L872-L887 Description of
|
53
|
+
# possible types of writes in Apache Cassandra native protocol spec v1
|
54
|
+
WRITE_TYPES = [:simple, :batch, :unlogged_batch, :counter, :batch_log].freeze
|
55
|
+
|
56
|
+
CLUSTER_OPTIONS = [
|
57
|
+
:address_resolution,
|
58
|
+
:address_resolution_policy,
|
59
|
+
:allow_beta_protocol,
|
60
|
+
:auth_provider,
|
61
|
+
:client_cert,
|
62
|
+
:client_timestamps,
|
63
|
+
:compression,
|
64
|
+
:compressor,
|
65
|
+
:connect_timeout,
|
66
|
+
:connections_per_local_node,
|
67
|
+
:connections_per_remote_node,
|
68
|
+
:consistency,
|
69
|
+
:credentials,
|
70
|
+
:custom_types,
|
71
|
+
:datacenter,
|
72
|
+
:execution_profiles,
|
73
|
+
:futures_factory,
|
74
|
+
:heartbeat_interval,
|
75
|
+
:hosts,
|
76
|
+
:idle_timeout,
|
77
|
+
:listeners,
|
78
|
+
:load_balancing_policy,
|
79
|
+
:logger,
|
80
|
+
:nodelay,
|
81
|
+
:reconnection_policy,
|
82
|
+
:retry_policy,
|
83
|
+
:page_size,
|
84
|
+
:passphrase,
|
85
|
+
:password,
|
86
|
+
:port,
|
87
|
+
:private_key,
|
88
|
+
:protocol_version,
|
89
|
+
:requests_per_connection,
|
90
|
+
:schema_refresh_delay,
|
91
|
+
:schema_refresh_timeout,
|
92
|
+
:server_cert,
|
93
|
+
:shuffle_replicas,
|
94
|
+
:ssl,
|
95
|
+
:synchronize_schema,
|
96
|
+
:timeout,
|
97
|
+
:trace,
|
98
|
+
:username
|
99
|
+
].freeze
|
100
|
+
|
101
|
+
# Creates a {Cassandra::Cluster Cluster instance}.
|
102
|
+
#
|
103
|
+
# @option options [Array<String, IPAddr>] :hosts (['127.0.0.1']) a list of
|
104
|
+
# initial addresses. Note that the entire list of cluster members will be
|
105
|
+
# discovered automatically once a connection to any hosts from the original
|
106
|
+
# list is successful.
|
107
|
+
#
|
108
|
+
# @option options [Integer] :port (9042) cassandra native protocol port.
|
109
|
+
#
|
110
|
+
# @option options [Boolean] :nodelay (true) when set to `true`, disables
|
111
|
+
# nagle algorithm.
|
112
|
+
#
|
113
|
+
# @option options [String] :datacenter (nil) name of current datacenter.
|
114
|
+
# First datacenter found will be assumed current by default. Note that you
|
115
|
+
# can skip this option if you specify only hosts from the local datacenter
|
116
|
+
# in `:hosts` option.
|
117
|
+
#
|
118
|
+
# @option options [Boolean] :shuffle_replicas (true) whether replicas list
|
119
|
+
# found by the default Token-Aware Load Balancing Policy should be
|
120
|
+
# shuffled. See {Cassandra::LoadBalancing::Policies::TokenAware#initialize Token-Aware Load Balancing Policy}.
|
121
|
+
#
|
122
|
+
# @option options [Hash<String|Symbol, ExecutionProfile>] :execution_profiles (nil)
|
123
|
+
# Hash of {Cassandra::Execution::Profile}s that are available for client use (e.g.
|
124
|
+
# {Session#execute}, {Session#execute_async}, {Session#prepare}, and {Session#prepare_async}).
|
125
|
+
#
|
126
|
+
# @option options [Numeric] :connect_timeout (10) connection timeout in
|
127
|
+
# seconds. Setting value to `nil` will reset it to 5 seconds.
|
128
|
+
#
|
129
|
+
# @option options [Numeric] :timeout (12) request execution timeout in
|
130
|
+
# seconds. Setting value to `nil` will remove request timeout.
|
131
|
+
#
|
132
|
+
# @option options [Numeric] :heartbeat_interval (30) how often should a
|
133
|
+
# heartbeat be sent to determine if a connection is alive. Several things to
|
134
|
+
# note about this option. Only one heartbeat request will ever be
|
135
|
+
# outstanding on a given connection. Each heatbeat will be sent in at least
|
136
|
+
# `:heartbeat_interval` seconds after the last request has been sent on a
|
137
|
+
# given connection. Setting value to `nil` will remove connection timeout.
|
138
|
+
#
|
139
|
+
# @option options [Numeric] :idle_timeout (60) period of inactivity after
|
140
|
+
# which a connection is considered dead. Note that this value should be at
|
141
|
+
# least a few times larger than `:heartbeat_interval`. Setting value to
|
142
|
+
# `nil` will remove automatic connection termination.
|
143
|
+
#
|
144
|
+
# @option options [String] :username (none) username to use for
|
145
|
+
# authentication to cassandra. Note that you must also specify `:password`.
|
146
|
+
#
|
147
|
+
# @option options [String] :password (none) password to use for
|
148
|
+
# authentication to cassandra. Note that you must also specify `:username`.
|
149
|
+
#
|
150
|
+
# @option options [Boolean, OpenSSL::SSL::SSLContext] :ssl (false) enable
|
151
|
+
# default ssl authentication if `true` (not recommended). Also accepts an
|
152
|
+
# initialized {OpenSSL::SSL::SSLContext}. Note that this option should be
|
153
|
+
# ignored if `:server_cert`, `:client_cert`, `:private_key` or
|
154
|
+
# `:passphrase` are given.
|
155
|
+
#
|
156
|
+
# @option options [String] :server_cert (none) path to server certificate or
|
157
|
+
# certificate authority file.
|
158
|
+
#
|
159
|
+
# @option options [String] :client_cert (none) path to client certificate
|
160
|
+
# file. Note that this option is only required when encryption is
|
161
|
+
# configured to require client authentication.
|
162
|
+
#
|
163
|
+
# @option options [String] :private_key (none) path to client private key.
|
164
|
+
# Note that this option is only required when encryption is configured to
|
165
|
+
# require client authentication.
|
166
|
+
#
|
167
|
+
# @option options [String] :passphrase (none) passphrase for private key.
|
168
|
+
#
|
169
|
+
# @option options [Symbol] :compression (none) compression to use. Must be
|
170
|
+
# either `:snappy` or `:lz4`. Also note, that in order for compression to
|
171
|
+
# work, you must install 'snappy' or 'lz4-ruby' gems.
|
172
|
+
#
|
173
|
+
# @option options [Cassandra::LoadBalancing::Policy] :load_balancing_policy
|
174
|
+
# default: token aware data center aware round robin.
|
175
|
+
#
|
176
|
+
# @option options [Symbol] :address_resolution (:none) a pre-configured
|
177
|
+
# address resolver to use. Must be one of `:none` or
|
178
|
+
# `:ec2_multi_region`.
|
179
|
+
#
|
180
|
+
# @option options [Integer] :connections_per_local_node (nil) Number of connections to
|
181
|
+
# open to each local node; the value of this option directly correlates to the number
|
182
|
+
# of requests the client can make to the local node concurrently. When `nil`, the
|
183
|
+
# setting is `1` for nodes that use the v3 or later protocol, and `2` for nodes that
|
184
|
+
# use the v2 or earlier protocol.
|
185
|
+
#
|
186
|
+
# @option options [Integer] :connections_per_remote_node (1) Number of connections to
|
187
|
+
# open to each remote node; the value of this option directly correlates to the
|
188
|
+
# number of requests the client can make to the remote node concurrently.
|
189
|
+
#
|
190
|
+
# @option options [Integer] :requests_per_connection (nil) Number of outstanding
|
191
|
+
# requests to support on one connection. Depending on the types of requests, some may
|
192
|
+
# get processed in parallel in the Cassandra node. When `nil`, the setting is `1024`
|
193
|
+
# for nodes that use the v3 or later protocol, and `128` for nodes that use the
|
194
|
+
# v2 or earlier protocol.
|
195
|
+
#
|
196
|
+
# @option options [Integer] :protocol_version (nil) Version of protocol to speak to
|
197
|
+
# nodes. By default, this is auto-negotiated to the highest common protocol version
|
198
|
+
# that all nodes in `:hosts` speak.
|
199
|
+
#
|
200
|
+
# @option options [Boolean] :allow_beta_protocol (false) whether the driver should attempt to speak to nodes
|
201
|
+
# with a beta version of the newest protocol (which is still under development). USE WITH CAUTION!
|
202
|
+
#
|
203
|
+
# @option options [Boolean, Cassandra::TimestampGenerator] :client_timestamps (false) whether the driver
|
204
|
+
# should send timestamps for each executed statement and possibly which timestamp generator to use. Enabling this
|
205
|
+
# setting helps mitigate Cassandra cluster clock skew because the timestamp of the client machine will be used.
|
206
|
+
# This does not help mitigate application cluster clock skew. Also accepts an initialized
|
207
|
+
# {Cassandra::TimestampGenerator}, `:simple` (indicating an instance of {Cassandra::TimestampGenerator::Simple}),
|
208
|
+
# or `:monotonic` (indicating an instance of {Cassandra::TimestampGenerator::TickingOnDuplicate}). If set to true,
|
209
|
+
# it defaults to {Cassandra::TimestampGenerator::Simple} for all Ruby flavors except JRuby. On JRuby, it defaults to
|
210
|
+
# {Cassandra::TimestampGenerator::TickingOnDuplicate}.
|
211
|
+
#
|
212
|
+
# @option options [Boolean] :synchronize_schema (true) whether the driver
|
213
|
+
# should automatically keep schema metadata synchronized. When enabled, the
|
214
|
+
# driver updates schema metadata after receiving schema change
|
215
|
+
# notifications from Cassandra. Setting this setting to `false` disables
|
216
|
+
# automatic schema updates. Schema metadata is used by the driver to
|
217
|
+
# determine cluster partitioners as well as to find partition keys and
|
218
|
+
# replicas of prepared statements, this information makes token aware load
|
219
|
+
# balancing possible. One can still
|
220
|
+
# {Cassandra::Cluster#refresh_schema refresh schema manually}.
|
221
|
+
#
|
222
|
+
# @option options [Numeric] :schema_refresh_delay (1) the driver will wait
|
223
|
+
# for `:schema_refresh_delay` before fetching metadata after receiving a
|
224
|
+
# schema change event. This timer is restarted every time a new schema
|
225
|
+
# change event is received. Finally, when the timer expires or a maximum
|
226
|
+
# wait time of `:schema_refresh_timeout` has been reached, a schema refresh
|
227
|
+
# attempt will be made and the timeout is reset.
|
228
|
+
#
|
229
|
+
# @option options [Numeric] :schema_refresh_timeout (10) the maximum delay
|
230
|
+
# before automatically refreshing schema. Such delay can occur whenever
|
231
|
+
# multiple schema change events are continuously arriving within
|
232
|
+
# `:schema_refresh_delay` interval.
|
233
|
+
#
|
234
|
+
# @option options [Cassandra::Reconnection::Policy] :reconnection_policy
|
235
|
+
# default: {Cassandra::Reconnection::Policies::Exponential Exponential}.
|
236
|
+
# Note that the default policy is configured with `(0.5, 30, 2)`.
|
237
|
+
#
|
238
|
+
# @option options [Cassandra::Retry::Policy] :retry_policy default:
|
239
|
+
# {Cassandra::Retry::Policies::Default Default Retry Policy}.
|
240
|
+
#
|
241
|
+
# @option options [Logger] :logger (none) logger. a {Logger} instance from the
|
242
|
+
# standard library or any object responding to standard log methods
|
243
|
+
# (`#debug`, `#info`, `#warn`, `#error` and `#fatal`).
|
244
|
+
#
|
245
|
+
# @option options [Enumerable<Cassandra::Listener>] :listeners (none)
|
246
|
+
# initial listeners. A list of initial cluster state listeners. Note that a
|
247
|
+
# `:load_balancing` policy is automatically registered with the cluster.
|
248
|
+
#
|
249
|
+
# @option options [Symbol] :consistency (:local_quorum) default consistency
|
250
|
+
# to use for all requests. Must be one of {Cassandra::CONSISTENCIES}.
|
251
|
+
#
|
252
|
+
# @option options [Boolean] :trace (false) whether or not to trace all
|
253
|
+
# requests by default.
|
254
|
+
#
|
255
|
+
# @option options [Integer] :page_size (10000) default page size for all
|
256
|
+
# select queries. Set this value to `nil` to disable paging.
|
257
|
+
#
|
258
|
+
# @option options [Hash{String => String}] :credentials (none) a hash of credentials -
|
259
|
+
# to be used with [credentials authentication in cassandra 1.2](https://github.com/apache/cassandra/blob/cassandra-2.0.16/doc/native_protocol_v1.spec#L238-L250).
|
260
|
+
# Note that if you specified `:username` and `:password` options, those credentials
|
261
|
+
# are configured automatically.
|
262
|
+
#
|
263
|
+
# @option options [Cassandra::Auth::Provider] :auth_provider (none) a custom auth
|
264
|
+
# provider to be used with [SASL authentication in cassandra 2.0](https://github.com/apache/cassandra/blob/cassandra-2.0.16/doc/native_protocol_v2.spec#L257-L273).
|
265
|
+
# Note that if you have specified `:username` and `:password`, then a
|
266
|
+
# {Cassandra::Auth::Providers::Password Password Provider} will be used automatically.
|
267
|
+
#
|
268
|
+
# @option options [Cassandra::Compression::Compressor] :compressor (none) a
|
269
|
+
# custom compressor. Note that if you have specified `:compression`, an
|
270
|
+
# appropriate compressor will be provided automatically.
|
271
|
+
#
|
272
|
+
# @option options [Cassandra::AddressResolution::Policy]
|
273
|
+
# :address_resolution_policy default:
|
274
|
+
# {Cassandra::AddressResolution::Policies::None No Resolution Policy} a custom address
|
275
|
+
# resolution policy. Note that if you have specified `:address_resolution`, an
|
276
|
+
# appropriate address resolution policy will be provided automatically.
|
277
|
+
#
|
278
|
+
# @option options [Object<#all, #error, #value, #promise>] :futures_factory
|
279
|
+
# default: {Cassandra::Future} a futures factory to assist with integration
|
280
|
+
# into existing futures library. Note that promises returned by this object
|
281
|
+
# must conform to {Cassandra::Promise} api, which is not yet public. Things
|
282
|
+
# may change, use at your own risk.
|
283
|
+
#
|
284
|
+
# @example Connecting to localhost
|
285
|
+
# cluster = Cassandra.cluster
|
286
|
+
#
|
287
|
+
# @example Configuring {Cassandra::Cluster}
|
288
|
+
# cluster = Cassandra.cluster(
|
289
|
+
# username: username,
|
290
|
+
# password: password,
|
291
|
+
# hosts: ['10.0.1.1', '10.0.1.2', '10.0.1.3']
|
292
|
+
# )
|
293
|
+
#
|
294
|
+
# @return [Cassandra::Cluster] a cluster instance
|
295
|
+
def self.cluster(options = {})
|
296
|
+
cluster_async(options).get
|
297
|
+
end
|
298
|
+
|
299
|
+
# Creates a {Cassandra::Cluster Cluster instance}.
|
300
|
+
#
|
301
|
+
# @see Cassandra.cluster
|
302
|
+
#
|
303
|
+
# @return [Cassandra::Future<Cassandra::Cluster>] a future resolving to the
|
304
|
+
# cluster instance.
|
305
|
+
def self.cluster_async(options = {})
|
306
|
+
options, hosts = validate_and_massage_options(options)
|
307
|
+
rescue => e
|
308
|
+
futures = options.fetch(:futures_factory) { return Future::Error.new(e) }
|
309
|
+
futures.error(e)
|
310
|
+
else
|
311
|
+
driver = Driver.new(options)
|
312
|
+
driver.connect(hosts)
|
313
|
+
end
|
314
|
+
|
315
|
+
# @private
|
316
|
+
SSL_CLASSES = [::TrueClass, ::FalseClass, ::OpenSSL::SSL::SSLContext].freeze
|
317
|
+
|
318
|
+
# rubocop:disable Metrics/AbcSize
|
319
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
320
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
321
|
+
# @private
|
322
|
+
def self.validate_and_massage_options(options)
|
323
|
+
options = options.select do |key, _|
|
324
|
+
CLUSTER_OPTIONS.include?(key)
|
325
|
+
end
|
326
|
+
|
327
|
+
if options.key?(:execution_profiles)
|
328
|
+
[:load_balancing_policy, :retry_policy, :timeout, :consistency].each do |opt|
|
329
|
+
raise ::ArgumentError, "#{opt} is not allowed when execution profiles are used" if options.key?(opt)
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
has_username = options.key?(:username)
|
334
|
+
has_password = options.key?(:password)
|
335
|
+
if has_username || has_password
|
336
|
+
if has_username && !has_password
|
337
|
+
raise ::ArgumentError,
|
338
|
+
'both :username and :password options must be specified, ' \
|
339
|
+
'but only :username given'
|
340
|
+
end
|
341
|
+
|
342
|
+
if !has_username && has_password
|
343
|
+
raise ::ArgumentError,
|
344
|
+
'both :username and :password options must be specified, ' \
|
345
|
+
'but only :password given'
|
346
|
+
end
|
347
|
+
|
348
|
+
username = options.delete(:username)
|
349
|
+
password = options.delete(:password)
|
350
|
+
|
351
|
+
Util.assert_instance_of(::String, username) do
|
352
|
+
":username must be a String, #{username.inspect} given"
|
353
|
+
end
|
354
|
+
Util.assert_instance_of(::String, password) do
|
355
|
+
":password must be a String, #{password.inspect} given"
|
356
|
+
end
|
357
|
+
Util.assert_not_empty(username) { ':username cannot be empty' }
|
358
|
+
Util.assert_not_empty(password) { ':password cannot be empty' }
|
359
|
+
|
360
|
+
options[:credentials] = {username: username, password: password}
|
361
|
+
options[:auth_provider] = Auth::Providers::Password.new(username, password)
|
362
|
+
end
|
363
|
+
|
364
|
+
if options.key?(:credentials)
|
365
|
+
credentials = options[:credentials]
|
366
|
+
|
367
|
+
Util.assert_instance_of(::Hash, credentials) do
|
368
|
+
":credentials must be a hash, #{credentials.inspect} given"
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
if options.key?(:auth_provider)
|
373
|
+
auth_provider = options[:auth_provider]
|
374
|
+
|
375
|
+
Util.assert_responds_to(:create_authenticator, auth_provider) do
|
376
|
+
":auth_provider #{auth_provider.inspect} must respond to " \
|
377
|
+
":create_authenticator, but doesn't"
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
has_client_cert = options.key?(:client_cert)
|
382
|
+
has_private_key = options.key?(:private_key)
|
383
|
+
|
384
|
+
if has_client_cert || has_private_key
|
385
|
+
if has_client_cert && !has_private_key
|
386
|
+
raise ::ArgumentError,
|
387
|
+
'both :client_cert and :private_key options must be specified, ' \
|
388
|
+
'but only :client_cert given'
|
389
|
+
end
|
390
|
+
|
391
|
+
if !has_client_cert && has_private_key
|
392
|
+
raise ::ArgumentError,
|
393
|
+
'both :client_cert and :private_key options must be specified, ' \
|
394
|
+
'but only :private_key given'
|
395
|
+
end
|
396
|
+
|
397
|
+
Util.assert_instance_of(::String, options[:client_cert]) do
|
398
|
+
":client_cert must be a string, #{options[:client_cert].inspect} given"
|
399
|
+
end
|
400
|
+
Util.assert_instance_of(::String, options[:private_key]) do
|
401
|
+
":client_cert must be a string, #{options[:private_key].inspect} given"
|
402
|
+
end
|
403
|
+
client_cert = ::File.expand_path(options[:client_cert])
|
404
|
+
private_key = ::File.expand_path(options[:private_key])
|
405
|
+
|
406
|
+
Util.assert_file_exists(client_cert) do
|
407
|
+
":client_cert #{client_cert.inspect} doesn't exist"
|
408
|
+
end
|
409
|
+
Util.assert_file_exists(private_key) do
|
410
|
+
":private_key #{private_key.inspect} doesn't exist"
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
has_server_cert = options.key?(:server_cert)
|
415
|
+
|
416
|
+
if has_server_cert
|
417
|
+
Util.assert_instance_of(::String, options[:server_cert]) do
|
418
|
+
":server_cert must be a string, #{options[:server_cert].inspect} given"
|
419
|
+
end
|
420
|
+
server_cert = ::File.expand_path(options[:server_cert])
|
421
|
+
|
422
|
+
Util.assert_file_exists(server_cert) do
|
423
|
+
":server_cert #{server_cert.inspect} doesn't exist"
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
if has_client_cert || has_server_cert
|
428
|
+
context = ::OpenSSL::SSL::SSLContext.new
|
429
|
+
|
430
|
+
if has_server_cert
|
431
|
+
context.ca_file = server_cert
|
432
|
+
context.verify_mode = ::OpenSSL::SSL::VERIFY_PEER
|
433
|
+
end
|
434
|
+
|
435
|
+
if has_client_cert
|
436
|
+
context.cert = ::OpenSSL::X509::Certificate.new(File.read(client_cert))
|
437
|
+
|
438
|
+
context.key = if options.key?(:passphrase)
|
439
|
+
::OpenSSL::PKey::RSA.new(File.read(private_key),
|
440
|
+
options[:passphrase])
|
441
|
+
else
|
442
|
+
::OpenSSL::PKey::RSA.new(File.read(private_key))
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
options[:ssl] = context
|
447
|
+
end
|
448
|
+
|
449
|
+
if options.key?(:ssl)
|
450
|
+
ssl = options[:ssl]
|
451
|
+
|
452
|
+
Util.assert_instance_of_one_of(SSL_CLASSES, ssl) do
|
453
|
+
":ssl must be a boolean or an OpenSSL::SSL::SSLContext, #{ssl.inspect} given"
|
454
|
+
end
|
455
|
+
end
|
456
|
+
|
457
|
+
if options.key?(:compression)
|
458
|
+
compression = options.delete(:compression)
|
459
|
+
|
460
|
+
case compression
|
461
|
+
when :snappy
|
462
|
+
options[:compressor] = Compression::Compressors::Snappy.new
|
463
|
+
when :lz4
|
464
|
+
options[:compressor] = Compression::Compressors::Lz4.new
|
465
|
+
else
|
466
|
+
raise ::ArgumentError,
|
467
|
+
":compression must be either :snappy or :lz4, #{compression.inspect} given"
|
468
|
+
end
|
469
|
+
end
|
470
|
+
|
471
|
+
if options.key?(:compressor)
|
472
|
+
compressor = options[:compressor]
|
473
|
+
methods = [:algorithm, :compress?, :compress, :decompress]
|
474
|
+
|
475
|
+
Util.assert_responds_to_all(methods, compressor) do
|
476
|
+
":compressor #{compressor.inspect} must respond to #{methods.inspect}, " \
|
477
|
+
"but doesn't"
|
478
|
+
end
|
479
|
+
end
|
480
|
+
|
481
|
+
if options.key?(:logger)
|
482
|
+
if options[:logger].nil?
|
483
|
+
# Delete the key because we want to fallback to the default logger in Driver.
|
484
|
+
options.delete(:logger)
|
485
|
+
else
|
486
|
+
# Validate
|
487
|
+
logger = options[:logger]
|
488
|
+
methods = [:debug, :info, :warn, :error, :fatal]
|
489
|
+
|
490
|
+
Util.assert_responds_to_all(methods, logger) do
|
491
|
+
":logger #{logger.inspect} must respond to #{methods.inspect}, but doesn't"
|
492
|
+
end
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
496
|
+
if options.key?(:port)
|
497
|
+
unless options[:port].nil?
|
498
|
+
port = options[:port]
|
499
|
+
Util.assert_instance_of(::Integer, port)
|
500
|
+
Util.assert_one_of(1...2**16, port) do
|
501
|
+
":port must be a valid ip port, #{port} given"
|
502
|
+
end
|
503
|
+
end
|
504
|
+
end
|
505
|
+
|
506
|
+
options[:datacenter] = String(options[:datacenter]) if options.key?(:datacenter)
|
507
|
+
|
508
|
+
if options.key?(:connect_timeout)
|
509
|
+
timeout = options[:connect_timeout]
|
510
|
+
|
511
|
+
unless timeout.nil?
|
512
|
+
Util.assert_instance_of(::Numeric, timeout) do
|
513
|
+
":connect_timeout must be a number of seconds, #{timeout.inspect} given"
|
514
|
+
end
|
515
|
+
Util.assert(timeout > 0) do
|
516
|
+
":connect_timeout must be greater than 0, #{timeout} given"
|
517
|
+
end
|
518
|
+
end
|
519
|
+
end
|
520
|
+
|
521
|
+
if options.key?(:execution_profiles)
|
522
|
+
Util.assert_instance_of(::Hash, options[:execution_profiles],
|
523
|
+
':execution_profiles must be a hash of <name,ExecutionProfile> entries.')
|
524
|
+
end
|
525
|
+
|
526
|
+
if options.key?(:heartbeat_interval)
|
527
|
+
timeout = options[:heartbeat_interval]
|
528
|
+
|
529
|
+
unless timeout.nil?
|
530
|
+
Util.assert_instance_of(::Numeric, timeout) do
|
531
|
+
":heartbeat_interval must be a number of seconds, #{timeout.inspect} given"
|
532
|
+
end
|
533
|
+
Util.assert(timeout > 0) do
|
534
|
+
":heartbeat_interval must be greater than 0, #{timeout} given"
|
535
|
+
end
|
536
|
+
end
|
537
|
+
end
|
538
|
+
|
539
|
+
if options.key?(:idle_timeout)
|
540
|
+
timeout = options[:idle_timeout]
|
541
|
+
|
542
|
+
unless timeout.nil?
|
543
|
+
Util.assert_instance_of(::Numeric, timeout) do
|
544
|
+
":idle_timeout must be a number of seconds, #{timeout.inspect} given"
|
545
|
+
end
|
546
|
+
Util.assert(timeout > 0) do
|
547
|
+
":idle_timeout must be greater than 0, #{timeout} given"
|
548
|
+
end
|
549
|
+
end
|
550
|
+
end
|
551
|
+
|
552
|
+
if options.key?(:schema_refresh_delay)
|
553
|
+
timeout = options[:schema_refresh_delay]
|
554
|
+
|
555
|
+
Util.assert_instance_of(::Numeric, timeout) do
|
556
|
+
":schema_refresh_delay must be a number of seconds, #{timeout.inspect} given"
|
557
|
+
end
|
558
|
+
Util.assert(timeout > 0) do
|
559
|
+
":schema_refresh_delay must be greater than 0, #{timeout} given"
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
563
|
+
if options.key?(:schema_refresh_timeout)
|
564
|
+
timeout = options[:schema_refresh_timeout]
|
565
|
+
|
566
|
+
Util.assert_instance_of(::Numeric, timeout) do
|
567
|
+
":schema_refresh_timeout must be a number of seconds, #{timeout.inspect} given"
|
568
|
+
end
|
569
|
+
Util.assert(timeout > 0) do
|
570
|
+
":schema_refresh_timeout must be greater than 0, #{timeout} given"
|
571
|
+
end
|
572
|
+
end
|
573
|
+
|
574
|
+
if options.key?(:reconnection_policy)
|
575
|
+
reconnection_policy = options[:reconnection_policy]
|
576
|
+
|
577
|
+
Util.assert_responds_to(:schedule, reconnection_policy) do
|
578
|
+
":reconnection_policy #{reconnection_policy.inspect} must respond to " \
|
579
|
+
":schedule, but doesn't"
|
580
|
+
end
|
581
|
+
end
|
582
|
+
|
583
|
+
# Validate options that go in an execution profile. Instantiating one
|
584
|
+
# causes validation automatically.
|
585
|
+
Cassandra::Execution::Profile.new(options)
|
586
|
+
|
587
|
+
options[:listeners] = Array(options[:listeners]) if options.key?(:listeners)
|
588
|
+
options[:nodelay] = !!options[:nodelay] if options.key?(:nodelay)
|
589
|
+
options[:trace] = !!options[:trace] if options.key?(:trace)
|
590
|
+
options[:shuffle_replicas] = !!options[:shuffle_replicas] if options.key?(:shuffle_replicas)
|
591
|
+
options[:allow_beta_protocol] = !!options[:allow_beta_protocol] if options.key?(:allow_beta_protocol)
|
592
|
+
|
593
|
+
if options.key?(:page_size)
|
594
|
+
page_size = options[:page_size]
|
595
|
+
|
596
|
+
unless page_size.nil?
|
597
|
+
page_size = options[:page_size]
|
598
|
+
Util.assert_instance_of(::Integer, page_size)
|
599
|
+
Util.assert_one_of(1...2**32, page_size) do
|
600
|
+
":page_size must be a positive integer, #{page_size.inspect} given"
|
601
|
+
end
|
602
|
+
end
|
603
|
+
end
|
604
|
+
|
605
|
+
if options.key?(:protocol_version)
|
606
|
+
protocol_version = options[:protocol_version]
|
607
|
+
unless protocol_version.nil?
|
608
|
+
Util.assert_instance_of(::Integer, protocol_version)
|
609
|
+
Util.assert_one_of(1..Cassandra::Protocol::Versions::MAX_SUPPORTED_VERSION, protocol_version,
|
610
|
+
':protocol_version must be a positive integer between 1 and ' \
|
611
|
+
"#{Cassandra::Protocol::Versions::MAX_SUPPORTED_VERSION}, #{protocol_version.inspect} given")
|
612
|
+
end
|
613
|
+
end
|
614
|
+
|
615
|
+
Util.assert(!(options[:allow_beta_protocol] && options[:protocol_version]),
|
616
|
+
'only one of :allow_beta_protocol and :protocol_version may be specified, both given')
|
617
|
+
|
618
|
+
if options.key?(:futures_factory)
|
619
|
+
futures_factory = options[:futures_factory]
|
620
|
+
methods = [:error, :value, :promise, :all]
|
621
|
+
|
622
|
+
Util.assert_responds_to_all(methods, futures_factory) do
|
623
|
+
":futures_factory #{futures_factory.inspect} must respond to " \
|
624
|
+
"#{methods.inspect}, but doesn't"
|
625
|
+
end
|
626
|
+
end
|
627
|
+
|
628
|
+
if options.key?(:address_resolution)
|
629
|
+
address_resolution = options.delete(:address_resolution)
|
630
|
+
|
631
|
+
case address_resolution
|
632
|
+
when :none
|
633
|
+
# do nothing
|
634
|
+
when :ec2_multi_region
|
635
|
+
options[:address_resolution_policy] =
|
636
|
+
AddressResolution::Policies::EC2MultiRegion.new
|
637
|
+
else
|
638
|
+
raise ::ArgumentError,
|
639
|
+
':address_resolution must be either :none or :ec2_multi_region, ' \
|
640
|
+
"#{address_resolution.inspect} given"
|
641
|
+
end
|
642
|
+
end
|
643
|
+
|
644
|
+
if options.key?(:address_resolution_policy)
|
645
|
+
address_resolver = options[:address_resolution_policy]
|
646
|
+
|
647
|
+
Util.assert_responds_to(:resolve, address_resolver) do
|
648
|
+
':address_resolution_policy must respond to :resolve, ' \
|
649
|
+
"#{address_resolver.inspect} but doesn't"
|
650
|
+
end
|
651
|
+
end
|
652
|
+
|
653
|
+
options[:synchronize_schema] = !!options[:synchronize_schema] if options.key?(:synchronize_schema)
|
654
|
+
|
655
|
+
if options.key?(:client_timestamps)
|
656
|
+
timestamp_generator = case options[:client_timestamps]
|
657
|
+
when true
|
658
|
+
if RUBY_ENGINE == 'jruby'
|
659
|
+
Cassandra::TimestampGenerator::TickingOnDuplicate.new
|
660
|
+
else
|
661
|
+
Cassandra::TimestampGenerator::Simple.new
|
662
|
+
end
|
663
|
+
when false
|
664
|
+
nil
|
665
|
+
when :simple
|
666
|
+
Cassandra::TimestampGenerator::Simple.new
|
667
|
+
when :monotonic
|
668
|
+
Cassandra::TimestampGenerator::TickingOnDuplicate.new
|
669
|
+
else
|
670
|
+
# The value must be a generator instance.
|
671
|
+
options[:client_timestamps]
|
672
|
+
end
|
673
|
+
|
674
|
+
if timestamp_generator
|
675
|
+
Util.assert_responds_to(:next, timestamp_generator) do
|
676
|
+
":client_timestamps #{options[:client_timestamps].inspect} must be a boolean, :simple, :monotonic, or " \
|
677
|
+
'an object that responds to :next'
|
678
|
+
end
|
679
|
+
end
|
680
|
+
options.delete(:client_timestamps)
|
681
|
+
options[:timestamp_generator] = timestamp_generator
|
682
|
+
end
|
683
|
+
|
684
|
+
if options.key?(:connections_per_local_node)
|
685
|
+
connections_per_node = options[:connections_per_local_node]
|
686
|
+
|
687
|
+
unless connections_per_node.nil?
|
688
|
+
connections_per_node = options[:connections_per_local_node]
|
689
|
+
Util.assert_instance_of(::Integer, connections_per_node)
|
690
|
+
Util.assert_one_of(1...2**16, connections_per_node) do
|
691
|
+
':connections_per_local_node must be a positive integer between ' \
|
692
|
+
"1 and 65535, #{connections_per_node.inspect} given"
|
693
|
+
end
|
694
|
+
end
|
695
|
+
end
|
696
|
+
|
697
|
+
if options.key?(:connections_per_remote_node)
|
698
|
+
connections_per_node = options[:connections_per_remote_node]
|
699
|
+
|
700
|
+
unless connections_per_node.nil?
|
701
|
+
connections_per_node = options[:connections_per_remote_node]
|
702
|
+
Util.assert_instance_of(::Integer, connections_per_node)
|
703
|
+
Util.assert_one_of(1...2**16, connections_per_node) do
|
704
|
+
':connections_per_remote_node must be a positive integer between ' \
|
705
|
+
"1 and 65535, #{connections_per_node.inspect} given"
|
706
|
+
end
|
707
|
+
end
|
708
|
+
end
|
709
|
+
|
710
|
+
if options.key?(:requests_per_connection)
|
711
|
+
requests_per_connection = options[:requests_per_connection]
|
712
|
+
|
713
|
+
unless requests_per_connection.nil?
|
714
|
+
requests_per_connection = options[:requests_per_connection]
|
715
|
+
Util.assert_instance_of(::Integer, requests_per_connection)
|
716
|
+
|
717
|
+
# v3 protocol says that max stream-id is 32767 (2^15-1). This setting might be
|
718
|
+
# used to talk to a v2 (or less) node, but then we'll adjust it down.
|
719
|
+
|
720
|
+
Util.assert_one_of(1...2**15, requests_per_connection) do
|
721
|
+
':requests_per_connection must be a positive integer, ' \
|
722
|
+
"#{requests_per_connection.inspect} given"
|
723
|
+
end
|
724
|
+
end
|
725
|
+
end
|
726
|
+
|
727
|
+
# Get host addresses.
|
728
|
+
hosts = []
|
729
|
+
|
730
|
+
Array(options.fetch(:hosts, '127.0.0.1')).each do |host|
|
731
|
+
case host
|
732
|
+
when ::IPAddr
|
733
|
+
hosts << host
|
734
|
+
when ::String # ip address or hostname
|
735
|
+
Resolv.each_address(host) do |ip|
|
736
|
+
hosts << ::IPAddr.new(ip)
|
737
|
+
end
|
738
|
+
else
|
739
|
+
raise ::ArgumentError, ":hosts must be String or IPAddr, #{host.inspect} given"
|
740
|
+
end
|
741
|
+
end
|
742
|
+
|
743
|
+
if hosts.empty?
|
744
|
+
raise ::ArgumentError,
|
745
|
+
":hosts #{options[:hosts].inspect} could not be resolved to any ip address"
|
746
|
+
end
|
747
|
+
|
748
|
+
hosts.shuffle!
|
749
|
+
|
750
|
+
[options, hosts]
|
751
|
+
end
|
752
|
+
|
753
|
+
# @private
|
754
|
+
EMPTY_LIST = [].freeze
|
755
|
+
# @private
|
756
|
+
NOT_SET = ::Object.new
|
757
|
+
# @private
|
758
|
+
NULL_BYTE = "\x00".freeze
|
759
|
+
|
760
|
+
# @private
|
761
|
+
# ensures that:
|
762
|
+
# ::Date.jd(DATE_OFFSET, ::Date::GREGORIAN)
|
763
|
+
# => -5877641-06-23
|
764
|
+
# ::Date.jd(DATE_OFFSET + 2 ** 31, ::Date::GREGORIAN)
|
765
|
+
# => 1970-1-1
|
766
|
+
# ::Date.jd(DATE_OFFSET + 2 ** 32, ::Date::GREGORIAN)
|
767
|
+
# => 5881580-07-12
|
768
|
+
DATE_OFFSET = (::Time.utc(1970, 1, 1).to_date.jd - 2**31)
|
769
|
+
end
|
770
|
+
|
771
|
+
require 'cassandra/attr_boolean'
|
772
|
+
require 'cassandra/version'
|
773
|
+
require 'cassandra/uuid'
|
774
|
+
require 'cassandra/time_uuid'
|
775
|
+
require 'cassandra/tuple'
|
776
|
+
require 'cassandra/udt'
|
777
|
+
require 'cassandra/time'
|
778
|
+
|
779
|
+
require 'cassandra/types'
|
780
|
+
|
781
|
+
require 'cassandra/errors'
|
782
|
+
require 'cassandra/compression'
|
783
|
+
require 'cassandra/protocol'
|
784
|
+
require 'cassandra/auth'
|
785
|
+
require 'cassandra/cassandra_logger'
|
786
|
+
require 'cassandra/null_logger'
|
787
|
+
|
788
|
+
require 'cassandra/executors'
|
789
|
+
require 'cassandra/future'
|
790
|
+
require 'cassandra/cluster'
|
791
|
+
require 'cassandra/custom_data'
|
792
|
+
require 'cassandra/driver'
|
793
|
+
require 'cassandra/host'
|
794
|
+
require 'cassandra/session'
|
795
|
+
require 'cassandra/result'
|
796
|
+
require 'cassandra/statement'
|
797
|
+
require 'cassandra/statements'
|
798
|
+
|
799
|
+
require 'cassandra/aggregate'
|
800
|
+
require 'cassandra/argument'
|
801
|
+
require 'cassandra/function'
|
802
|
+
require 'cassandra/function_collection'
|
803
|
+
require 'cassandra/column'
|
804
|
+
require 'cassandra/column_container'
|
805
|
+
require 'cassandra/table'
|
806
|
+
require 'cassandra/materialized_view'
|
807
|
+
require 'cassandra/keyspace'
|
808
|
+
require 'cassandra/index'
|
809
|
+
require 'cassandra/trigger'
|
810
|
+
|
811
|
+
require 'cassandra/execution/info'
|
812
|
+
require 'cassandra/execution/options'
|
813
|
+
require 'cassandra/execution/profile_manager'
|
814
|
+
require 'cassandra/execution/profile'
|
815
|
+
require 'cassandra/execution/trace'
|
816
|
+
|
817
|
+
require 'cassandra/load_balancing'
|
818
|
+
require 'cassandra/reconnection'
|
819
|
+
require 'cassandra/retry'
|
820
|
+
require 'cassandra/address_resolution'
|
821
|
+
require 'cassandra/timestamp_generator'
|
822
|
+
|
823
|
+
require 'cassandra/util'
|
824
|
+
|
825
|
+
# murmur3 hash extension
|
826
|
+
require 'cassandra_murmur3'
|
827
|
+
|
828
|
+
# SortedSet has a race condition where it does some class/global initialization when the first instance is created.
|
829
|
+
# If this is done in a multi-threaded environment, bad things can happen. So force the initialization here,
|
830
|
+
# when loading the C* module.
|
831
|
+
::SortedSet.new
|
832
|
+
|
833
|
+
module Cassandra
|
834
|
+
# @private
|
835
|
+
VOID_STATEMENT = Statements::Void.new
|
836
|
+
# @private
|
837
|
+
VOID_OPTIONS = Execution::Options.new(consistency: :quorum,
|
838
|
+
load_balancing_policy: LoadBalancing::Policies::RoundRobin.new,
|
839
|
+
retry_policy: Retry::Policies::Default.new)
|
840
|
+
# @private
|
841
|
+
NO_HOSTS = Errors::NoHostsAvailable.new
|
842
|
+
end
|