cassandra-driver 1.0.0.beta.3-java → 1.0.0-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +51 -14
- data/lib/cassandra.rb +164 -78
- 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/auth.rb +1 -1
- data/lib/cassandra/auth/providers/password.rb +1 -1
- data/lib/cassandra/cluster.rb +18 -5
- data/lib/cassandra/cluster/client.rb +175 -101
- data/lib/cassandra/{client/connection_manager.rb → cluster/connection_pool.rb} +5 -5
- data/lib/cassandra/cluster/connector.rb +142 -56
- data/lib/cassandra/cluster/control_connection.rb +385 -134
- data/lib/cassandra/{client/null_logger.rb → cluster/failed_connection.rb} +12 -14
- data/lib/cassandra/cluster/options.rb +13 -2
- data/lib/cassandra/cluster/registry.rb +19 -9
- data/lib/cassandra/column.rb +5 -0
- data/lib/cassandra/compression.rb +1 -1
- data/lib/cassandra/compression/compressors/lz4.rb +1 -1
- data/lib/cassandra/compression/compressors/snappy.rb +1 -1
- data/lib/cassandra/driver.rb +29 -21
- data/lib/cassandra/errors.rb +325 -35
- data/lib/cassandra/execution/options.rb +13 -6
- data/lib/cassandra/execution/trace.rb +4 -4
- data/lib/cassandra/future.rb +7 -3
- data/lib/cassandra/keyspace.rb +5 -0
- data/lib/cassandra/load_balancing/policies/dc_aware_round_robin.rb +13 -4
- data/lib/cassandra/load_balancing/policies/token_aware.rb +2 -4
- data/lib/cassandra/load_balancing/policies/white_list.rb +3 -6
- data/lib/cassandra/null_logger.rb +35 -0
- data/lib/cassandra/protocol.rb +0 -16
- data/lib/cassandra/protocol/cql_byte_buffer.rb +18 -18
- data/lib/cassandra/protocol/cql_protocol_handler.rb +78 -8
- data/lib/cassandra/protocol/frame_decoder.rb +2 -2
- data/lib/cassandra/protocol/frame_encoder.rb +1 -1
- data/lib/cassandra/protocol/requests/query_request.rb +1 -11
- data/lib/cassandra/protocol/response.rb +1 -1
- data/lib/cassandra/protocol/responses/detailed_error_response.rb +16 -1
- data/lib/cassandra/protocol/responses/error_response.rb +17 -0
- data/lib/cassandra/protocol/responses/event_response.rb +1 -1
- data/lib/cassandra/protocol/responses/raw_rows_result_response.rb +1 -1
- data/lib/cassandra/protocol/responses/result_response.rb +1 -1
- data/lib/cassandra/protocol/responses/rows_result_response.rb +1 -1
- data/lib/cassandra/protocol/type_converter.rb +4 -3
- data/lib/cassandra/reconnection.rb +1 -1
- data/lib/cassandra/result.rb +4 -6
- data/lib/cassandra/retry.rb +3 -5
- data/lib/cassandra/session.rb +14 -5
- data/lib/cassandra/statements/prepared.rb +5 -1
- data/lib/cassandra/table.rb +6 -1
- data/lib/cassandra/time_uuid.rb +21 -83
- data/lib/cassandra/util.rb +131 -1
- data/lib/cassandra/uuid.rb +6 -4
- data/lib/cassandra/uuid/generator.rb +207 -0
- data/lib/cassandra/version.rb +1 -1
- data/lib/cassandra_murmur3.jar +0 -0
- metadata +43 -49
- data/lib/cassandra/client.rb +0 -144
- data/lib/cassandra/client/batch.rb +0 -212
- data/lib/cassandra/client/client.rb +0 -591
- data/lib/cassandra/client/column_metadata.rb +0 -54
- data/lib/cassandra/client/connector.rb +0 -277
- data/lib/cassandra/client/execute_options_decoder.rb +0 -59
- data/lib/cassandra/client/peer_discovery.rb +0 -50
- data/lib/cassandra/client/prepared_statement.rb +0 -314
- data/lib/cassandra/client/query_result.rb +0 -230
- data/lib/cassandra/client/request_runner.rb +0 -71
- data/lib/cassandra/client/result_metadata.rb +0 -48
- data/lib/cassandra/client/void_result.rb +0 -78
@@ -39,14 +39,21 @@ module Cassandra
|
|
39
39
|
timeout = options[:timeout]
|
40
40
|
serial_consistency = options[:serial_consistency]
|
41
41
|
|
42
|
-
|
43
|
-
raise ::ArgumentError, ":serial_consistency must be one of #{SERIAL_CONSISTENCIES.inspect}, #{serial_consistency.inspect} given" if serial_consistency && !SERIAL_CONSISTENCIES.include?(serial_consistency)
|
42
|
+
Util.assert_one_of(CONSISTENCIES, consistency) { ":consistency must be one of #{CONSISTENCIES.inspect}, #{consistency.inspect} given" }
|
44
43
|
|
45
|
-
|
46
|
-
|
44
|
+
unless serial_consistency.nil?
|
45
|
+
Util.assert_one_of(SERIAL_CONSISTENCIES, serial_consistency) { ":serial_consistency must be one of #{SERIAL_CONSISTENCIES.inspect}, #{serial_consistency.inspect} given" }
|
46
|
+
end
|
47
47
|
|
48
|
-
|
49
|
-
|
48
|
+
unless page_size.nil?
|
49
|
+
page_size = options[:page_size] = Integer(page_size)
|
50
|
+
Util.assert(page_size > 0) { ":page_size must be a positive integer, #{page_size.inspect} given" }
|
51
|
+
end
|
52
|
+
|
53
|
+
unless timeout.nil?
|
54
|
+
Util.assert_instance_of(::Numeric, timeout) { ":timeout must be a number of seconds, #{timeout} given" }
|
55
|
+
Util.assert(timeout > 0) { ":timeout must be greater than 0, #{timeout} given" }
|
56
|
+
end
|
50
57
|
|
51
58
|
@consistency = consistency
|
52
59
|
@page_size = page_size
|
@@ -108,16 +108,16 @@ module Cassandra
|
|
108
108
|
private
|
109
109
|
|
110
110
|
# @private
|
111
|
-
SELECT_SESSION = "SELECT * FROM system_traces.sessions WHERE session_id =
|
111
|
+
SELECT_SESSION = "SELECT * FROM system_traces.sessions WHERE session_id = %s"
|
112
112
|
# @private
|
113
|
-
SELECT_EVENTS = "SELECT * FROM system_traces.events WHERE session_id =
|
113
|
+
SELECT_EVENTS = "SELECT * FROM system_traces.events WHERE session_id = %s"
|
114
114
|
|
115
115
|
# @private
|
116
116
|
def load
|
117
117
|
synchronize do
|
118
118
|
return if @loaded
|
119
119
|
|
120
|
-
data = @client.query(Statements::Simple.new(SELECT_SESSION
|
120
|
+
data = @client.query(Statements::Simple.new(SELECT_SESSION % @id), VOID_OPTIONS).get.first
|
121
121
|
raise ::RuntimeError, "unable to load trace #{@id}" if data.nil?
|
122
122
|
|
123
123
|
@coordinator = data['coordinator']
|
@@ -138,7 +138,7 @@ module Cassandra
|
|
138
138
|
|
139
139
|
@events = []
|
140
140
|
|
141
|
-
@client.query(Statements::Simple.new(SELECT_EVENTS
|
141
|
+
@client.query(Statements::Simple.new(SELECT_EVENTS % @id), VOID_OPTIONS).get.each do |row|
|
142
142
|
@events << Event.new(row['event_id'], row['activity'], row['source'], row['source_elapsed'], row['thread'])
|
143
143
|
end
|
144
144
|
|
data/lib/cassandra/future.rb
CHANGED
@@ -48,6 +48,8 @@ module Cassandra
|
|
48
48
|
def initialize(error)
|
49
49
|
raise ::ArgumentError, "error must be an exception, #{error.inspect} given" unless error.is_a?(::Exception)
|
50
50
|
|
51
|
+
error.set_backtrace(caller) unless error.backtrace
|
52
|
+
|
51
53
|
@error = error
|
52
54
|
end
|
53
55
|
|
@@ -188,7 +190,7 @@ module Cassandra
|
|
188
190
|
monitor = Monitor.new
|
189
191
|
promise = Promise.new
|
190
192
|
remaining = futures.length
|
191
|
-
values = Array.new(
|
193
|
+
values = Array.new(remaining)
|
192
194
|
|
193
195
|
futures.each_with_index do |future, i|
|
194
196
|
future.on_complete do |v, e|
|
@@ -390,11 +392,11 @@ module Cassandra
|
|
390
392
|
end
|
391
393
|
|
392
394
|
def success(value)
|
393
|
-
@block.call(
|
395
|
+
@block.call(value, nil)
|
394
396
|
end
|
395
397
|
|
396
398
|
def failure(error)
|
397
|
-
@block.call(
|
399
|
+
@block.call(nil, error)
|
398
400
|
end
|
399
401
|
end
|
400
402
|
|
@@ -471,6 +473,8 @@ module Cassandra
|
|
471
473
|
raise ::ArgumentError, "error must be an exception, #{error.inspect} given"
|
472
474
|
end
|
473
475
|
|
476
|
+
error.set_backtrace(caller) unless error.backtrace
|
477
|
+
|
474
478
|
return unless @state == :pending
|
475
479
|
|
476
480
|
listeners = nil
|
data/lib/cassandra/keyspace.rb
CHANGED
@@ -106,6 +106,11 @@ module Cassandra
|
|
106
106
|
end
|
107
107
|
alias :== :eql?
|
108
108
|
|
109
|
+
# @return [String] a CLI-friendly keyspace representation
|
110
|
+
def inspect
|
111
|
+
"#<#{self.class.name}:0x#{self.object_id.to_s(16)} @name=#{@name}>"
|
112
|
+
end
|
113
|
+
|
109
114
|
# @private
|
110
115
|
def update_table(table)
|
111
116
|
tables = @tables.dup
|
@@ -56,12 +56,17 @@ module Cassandra
|
|
56
56
|
|
57
57
|
include MonitorMixin
|
58
58
|
|
59
|
-
def initialize(datacenter, max_remote_hosts_to_use = nil, use_remote_hosts_for_local_consistency = false)
|
60
|
-
datacenter = String(datacenter)
|
59
|
+
def initialize(datacenter = nil, max_remote_hosts_to_use = nil, use_remote_hosts_for_local_consistency = false)
|
60
|
+
datacenter = datacenter && String(datacenter)
|
61
61
|
max_remote_hosts_to_use = max_remote_hosts_to_use && Integer(max_remote_hosts_to_use)
|
62
62
|
|
63
|
-
|
64
|
-
|
63
|
+
unless datacenter.nil?
|
64
|
+
Util.assert_not_empty(datacenter) { "datacenter cannot be empty" }
|
65
|
+
end
|
66
|
+
|
67
|
+
unless max_remote_hosts_to_use.nil?
|
68
|
+
Util.assert(max_remote_hosts_to_use >= 0) { "max_remote_hosts_to_use must be nil or >= 0" }
|
69
|
+
end
|
65
70
|
|
66
71
|
@datacenter = datacenter
|
67
72
|
@max_remote = max_remote_hosts_to_use
|
@@ -75,6 +80,10 @@ module Cassandra
|
|
75
80
|
end
|
76
81
|
|
77
82
|
def host_up(host)
|
83
|
+
if !@datacenter && host.datacenter
|
84
|
+
@datacenter = host.datacenter
|
85
|
+
end
|
86
|
+
|
78
87
|
if host.datacenter.nil? || host.datacenter == @datacenter
|
79
88
|
synchronize { @local = @local.dup.push(host) }
|
80
89
|
else
|
@@ -92,9 +92,7 @@ module Cassandra
|
|
92
92
|
def initialize(wrapped_policy)
|
93
93
|
methods = [:host_up, :host_down, :host_found, :host_lost, :distance, :plan]
|
94
94
|
|
95
|
-
|
96
|
-
raise ::ArgumentError, "supplied policy must be a Cassandra::LoadBalancing::Policy, #{wrapped_policy.inspect} given"
|
97
|
-
end
|
95
|
+
Util.assert_responds_to_all(methods, wrapped_policy) { "supplied policy must respond to #{methods.inspect}, but doesn't" }
|
98
96
|
|
99
97
|
@policy = wrapped_policy
|
100
98
|
end
|
@@ -111,7 +109,7 @@ module Cassandra
|
|
111
109
|
replicas = @cluster.find_replicas(keyspace, statement)
|
112
110
|
return @policy.plan(keyspace, statement, options) if replicas.empty?
|
113
111
|
|
114
|
-
Plan.new(replicas.
|
112
|
+
Plan.new(replicas.shuffle, @policy, keyspace, statement, options)
|
115
113
|
end
|
116
114
|
end
|
117
115
|
end
|
@@ -35,12 +35,9 @@ module Cassandra
|
|
35
35
|
# @param wrapped_policy [Cassandra::LoadBalancing::Policy] actual policy to filter
|
36
36
|
# @raise [ArgumentError] if arguments are of unexpected types
|
37
37
|
def initialize(ips, wrapped_policy)
|
38
|
-
|
38
|
+
Util.assert_instance_of(::Enumerable, ips) { "ips must be an Enumerable, #{ips.inspect} given" }
|
39
39
|
methods = [:host_up, :host_down, :host_found, :host_lost, :distance, :plan]
|
40
|
-
|
41
|
-
unless methods.all? {|method| wrapped_policy.respond_to?(method)}
|
42
|
-
raise ::ArgumentError, "supplied policy must be a Cassandra::LoadBalancing::Policy, #{wrapped_policy.inspect} given"
|
43
|
-
end
|
40
|
+
Util.assert_responds_to_all(methods, wrapped_policy) { "supplied policy must respond to #{methods.inspect}, but doesn't" }
|
44
41
|
|
45
42
|
@ips = ::Set.new
|
46
43
|
@policy = wrapped_policy
|
@@ -52,7 +49,7 @@ module Cassandra
|
|
52
49
|
when ::String
|
53
50
|
@ips << ::IPAddr.new(ip)
|
54
51
|
else
|
55
|
-
raise ::ArgumentError, "
|
52
|
+
raise ::ArgumentError, "each ip must be a String or IPAddr, #{ip.inspect} given"
|
56
53
|
end
|
57
54
|
end
|
58
55
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
#--
|
4
|
+
# Copyright 2013-2014 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
|
+
# @private
|
21
|
+
class NullLogger
|
22
|
+
def close(*); end
|
23
|
+
def debug(*); end
|
24
|
+
def debug?; false end
|
25
|
+
def error(*); end
|
26
|
+
def error?; false end
|
27
|
+
def fatal(*); end
|
28
|
+
def fatal?; false end
|
29
|
+
def info(*); end
|
30
|
+
def info?; false end
|
31
|
+
def unknown(*); end
|
32
|
+
def warn(*); end
|
33
|
+
def warn?; false end
|
34
|
+
end
|
35
|
+
end
|
data/lib/cassandra/protocol.rb
CHANGED
@@ -17,24 +17,8 @@
|
|
17
17
|
#++
|
18
18
|
|
19
19
|
module Cassandra
|
20
|
-
# @private
|
21
|
-
ProtocolError = Class.new(Error)
|
22
|
-
|
23
20
|
# @private
|
24
21
|
module Protocol
|
25
|
-
DecodingError = Class.new(ProtocolError)
|
26
|
-
EncodingError = Class.new(ProtocolError)
|
27
|
-
InvalidStreamIdError = Class.new(ProtocolError)
|
28
|
-
InvalidValueError = Class.new(ProtocolError)
|
29
|
-
UnsupportedOperationError = Class.new(ProtocolError)
|
30
|
-
UnsupportedFrameTypeError = Class.new(ProtocolError)
|
31
|
-
UnsupportedResultKindError = Class.new(ProtocolError)
|
32
|
-
UnsupportedColumnTypeError = Class.new(ProtocolError)
|
33
|
-
UnsupportedEventTypeError = Class.new(ProtocolError)
|
34
|
-
UnsupportedFeatureError = Class.new(ProtocolError)
|
35
|
-
UnexpectedCompressionError = Class.new(ProtocolError)
|
36
|
-
UnmaterializedRowsError = Class.new(ProtocolError)
|
37
|
-
|
38
22
|
module Formats
|
39
23
|
CHAR_FORMAT = 'c'.freeze
|
40
24
|
DOUBLE_FORMAT = 'G'.freeze
|
@@ -22,7 +22,7 @@ module Cassandra
|
|
22
22
|
def read_unsigned_byte
|
23
23
|
read_byte
|
24
24
|
rescue RangeError => e
|
25
|
-
raise DecodingError, e.message, e.backtrace
|
25
|
+
raise Errors::DecodingError, e.message, e.backtrace
|
26
26
|
end
|
27
27
|
|
28
28
|
def read_varint(len=bytesize, signed=true)
|
@@ -36,7 +36,7 @@ module Cassandra
|
|
36
36
|
end
|
37
37
|
n
|
38
38
|
rescue RangeError => e
|
39
|
-
raise DecodingError, e.message, e.backtrace
|
39
|
+
raise Errors::DecodingError, e.message, e.backtrace
|
40
40
|
end
|
41
41
|
|
42
42
|
def read_decimal(len=bytesize)
|
@@ -57,8 +57,8 @@ module Cassandra
|
|
57
57
|
fraction_string << number_string[number_string.length - size, number_string.length]
|
58
58
|
end
|
59
59
|
BigDecimal.new(fraction_string)
|
60
|
-
rescue DecodingError => e
|
61
|
-
raise DecodingError, e.message, e.backtrace
|
60
|
+
rescue Errors::DecodingError => e
|
61
|
+
raise Errors::DecodingError, e.message, e.backtrace
|
62
62
|
end
|
63
63
|
|
64
64
|
def read_long
|
@@ -68,19 +68,19 @@ module Cassandra
|
|
68
68
|
bottom ^= 0xffffffff
|
69
69
|
-((top << 32) | bottom) - 1
|
70
70
|
rescue RangeError => e
|
71
|
-
raise DecodingError, e.message, e.backtrace
|
71
|
+
raise Errors::DecodingError, e.message, e.backtrace
|
72
72
|
end
|
73
73
|
|
74
74
|
def read_double
|
75
75
|
read(8).unpack(Formats::DOUBLE_FORMAT).first
|
76
76
|
rescue RangeError => e
|
77
|
-
raise DecodingError, "Not enough bytes available to decode a double: #{e.message}", e.backtrace
|
77
|
+
raise Errors::DecodingError, "Not enough bytes available to decode a double: #{e.message}", e.backtrace
|
78
78
|
end
|
79
79
|
|
80
80
|
def read_float
|
81
81
|
read(4).unpack(Formats::FLOAT_FORMAT).first
|
82
82
|
rescue RangeError => e
|
83
|
-
raise DecodingError, "Not enough bytes available to decode a float: #{e.message}", e.backtrace
|
83
|
+
raise Errors::DecodingError, "Not enough bytes available to decode a float: #{e.message}", e.backtrace
|
84
84
|
end
|
85
85
|
|
86
86
|
def read_signed_int
|
@@ -88,13 +88,13 @@ module Cassandra
|
|
88
88
|
return n if n <= 0x7fffffff
|
89
89
|
n - 0xffffffff - 1
|
90
90
|
rescue RangeError => e
|
91
|
-
raise DecodingError, "Not enough bytes available to decode an int: #{e.message}", e.backtrace
|
91
|
+
raise Errors::DecodingError, "Not enough bytes available to decode an int: #{e.message}", e.backtrace
|
92
92
|
end
|
93
93
|
|
94
94
|
def read_unsigned_short
|
95
95
|
read_short
|
96
96
|
rescue RangeError => e
|
97
|
-
raise DecodingError, "Not enough bytes available to decode a short: #{e.message}", e.backtrace
|
97
|
+
raise Errors::DecodingError, "Not enough bytes available to decode a short: #{e.message}", e.backtrace
|
98
98
|
end
|
99
99
|
|
100
100
|
def read_string
|
@@ -103,7 +103,7 @@ module Cassandra
|
|
103
103
|
string.force_encoding(::Encoding::UTF_8)
|
104
104
|
string
|
105
105
|
rescue RangeError => e
|
106
|
-
raise DecodingError, "Not enough bytes available to decode a string: #{e.message}", e.backtrace
|
106
|
+
raise Errors::DecodingError, "Not enough bytes available to decode a string: #{e.message}", e.backtrace
|
107
107
|
end
|
108
108
|
|
109
109
|
def read_long_string
|
@@ -112,13 +112,13 @@ module Cassandra
|
|
112
112
|
string.force_encoding(::Encoding::UTF_8)
|
113
113
|
string
|
114
114
|
rescue RangeError => e
|
115
|
-
raise DecodingError, "Not enough bytes available to decode a long string: #{e.message}", e.backtrace
|
115
|
+
raise Errors::DecodingError, "Not enough bytes available to decode a long string: #{e.message}", e.backtrace
|
116
116
|
end
|
117
117
|
|
118
118
|
def read_uuid(impl=Uuid)
|
119
119
|
impl.new(read_varint(16, false))
|
120
|
-
rescue DecodingError => e
|
121
|
-
raise DecodingError, "Not enough bytes available to decode a UUID: #{e.message}", e.backtrace
|
120
|
+
rescue Errors::DecodingError => e
|
121
|
+
raise Errors::DecodingError, "Not enough bytes available to decode a UUID: #{e.message}", e.backtrace
|
122
122
|
end
|
123
123
|
|
124
124
|
def read_string_list
|
@@ -131,7 +131,7 @@ module Cassandra
|
|
131
131
|
return nil if size & 0x80000000 == 0x80000000
|
132
132
|
read(size)
|
133
133
|
rescue RangeError => e
|
134
|
-
raise DecodingError, "Not enough bytes available to decode a bytes: #{e.message}", e.backtrace
|
134
|
+
raise Errors::DecodingError, "Not enough bytes available to decode a bytes: #{e.message}", e.backtrace
|
135
135
|
end
|
136
136
|
|
137
137
|
def read_short_bytes
|
@@ -139,7 +139,7 @@ module Cassandra
|
|
139
139
|
return nil if size & 0x8000 == 0x8000
|
140
140
|
read(size)
|
141
141
|
rescue RangeError => e
|
142
|
-
raise DecodingError, "Not enough bytes available to decode a short bytes: #{e.message}", e.backtrace
|
142
|
+
raise Errors::DecodingError, "Not enough bytes available to decode a short bytes: #{e.message}", e.backtrace
|
143
143
|
end
|
144
144
|
|
145
145
|
def read_option
|
@@ -157,12 +157,12 @@ module Cassandra
|
|
157
157
|
port = read_int
|
158
158
|
[ip_addr, port]
|
159
159
|
rescue RangeError => e
|
160
|
-
raise DecodingError, "Not enough bytes available to decode an INET: #{e.message}", e.backtrace
|
160
|
+
raise Errors::DecodingError, "Not enough bytes available to decode an INET: #{e.message}", e.backtrace
|
161
161
|
end
|
162
162
|
|
163
163
|
def read_consistency
|
164
164
|
index = read_unsigned_short
|
165
|
-
raise DecodingError, "Unknown consistency index #{index}" if index >= CONSISTENCIES.size || CONSISTENCIES[index].nil?
|
165
|
+
raise Errors::DecodingError, "Unknown consistency index #{index}" if index >= CONSISTENCIES.size || CONSISTENCIES[index].nil?
|
166
166
|
CONSISTENCIES[index]
|
167
167
|
end
|
168
168
|
|
@@ -241,7 +241,7 @@ module Cassandra
|
|
241
241
|
|
242
242
|
def append_consistency(consistency)
|
243
243
|
index = CONSISTENCIES.index(consistency)
|
244
|
-
raise EncodingError, %(Unknown consistency "#{consistency}") if index.nil? || CONSISTENCIES[index].nil?
|
244
|
+
raise Errors::EncodingError, %(Unknown consistency "#{consistency}") if index.nil? || CONSISTENCIES[index].nil?
|
245
245
|
append_short(index)
|
246
246
|
end
|
247
247
|
|
@@ -35,7 +35,7 @@ module Cassandra
|
|
35
35
|
# @return [String] the current keyspace for the underlying connection
|
36
36
|
attr_reader :keyspace
|
37
37
|
|
38
|
-
def initialize(connection, scheduler, protocol_version, compressor=nil)
|
38
|
+
def initialize(connection, scheduler, protocol_version, compressor=nil, heartbeat_interval = 30, idle_timeout = 60)
|
39
39
|
@connection = connection
|
40
40
|
@scheduler = scheduler
|
41
41
|
@compressor = compressor
|
@@ -53,6 +53,10 @@ module Cassandra
|
|
53
53
|
@lock = Mutex.new
|
54
54
|
@closed_promise = Ione::Promise.new
|
55
55
|
@keyspace = nil
|
56
|
+
@heartbeat = nil
|
57
|
+
@terminate = nil
|
58
|
+
@heartbeat_interval = heartbeat_interval
|
59
|
+
@idle_timeout = idle_timeout
|
56
60
|
end
|
57
61
|
|
58
62
|
# Returns the hostname of the underlying connection
|
@@ -128,7 +132,7 @@ module Cassandra
|
|
128
132
|
# closes the futures of all active requests will be failed with the error
|
129
133
|
# that caused the connection to close, or nil.
|
130
134
|
#
|
131
|
-
# When `timeout` is specified the future will fail with {Cassandra::TimeoutError}
|
135
|
+
# When `timeout` is specified the future will fail with {Cassandra::Errors::TimeoutError}
|
132
136
|
# after that many seconds have passed. If a response arrives after that
|
133
137
|
# time it will be lost. If a response never arrives for the request the
|
134
138
|
# channel occupied by the request will _not_ be reused.
|
@@ -138,8 +142,9 @@ module Cassandra
|
|
138
142
|
# failing the request
|
139
143
|
# @return [Ione::Future<Cassandra::Protocol::Response>] a future that resolves to
|
140
144
|
# the response
|
141
|
-
def send_request(request, timeout=nil)
|
142
|
-
return Ione::Future.failed(Errors::
|
145
|
+
def send_request(request, timeout=nil, with_heartbeat = true)
|
146
|
+
return Ione::Future.failed(Errors::IOError.new('Connection closed')) if closed?
|
147
|
+
schedule_heartbeat if with_heartbeat
|
143
148
|
promise = RequestPromise.new(request, @frame_encoder)
|
144
149
|
id = nil
|
145
150
|
@lock.lock
|
@@ -174,8 +179,18 @@ module Cassandra
|
|
174
179
|
# Closes the underlying connection.
|
175
180
|
#
|
176
181
|
# @return [Ione::Future] a future that completes when the connection has closed
|
177
|
-
def close
|
178
|
-
@
|
182
|
+
def close(cause = nil)
|
183
|
+
if @heartbeat
|
184
|
+
@scheduler.cancel_timer(@heartbeat)
|
185
|
+
@heartbeat = nil
|
186
|
+
end
|
187
|
+
|
188
|
+
if @terminate
|
189
|
+
@scheduler.cancel_timer(@terminate)
|
190
|
+
@terminate = nil
|
191
|
+
end
|
192
|
+
|
193
|
+
@connection.close(cause)
|
179
194
|
@closed_promise.future
|
180
195
|
end
|
181
196
|
|
@@ -199,7 +214,7 @@ module Cassandra
|
|
199
214
|
def time_out!
|
200
215
|
unless future.completed?
|
201
216
|
@timed_out = true
|
202
|
-
fail(TimeoutError.new)
|
217
|
+
fail(Errors::TimeoutError.new('Timed out'))
|
203
218
|
end
|
204
219
|
end
|
205
220
|
|
@@ -209,6 +224,7 @@ module Cassandra
|
|
209
224
|
end
|
210
225
|
|
211
226
|
def receive_data(data)
|
227
|
+
reschedule_termination
|
212
228
|
@read_buffer << data
|
213
229
|
@current_frame = @frame_decoder.decode_frame(@read_buffer, @current_frame)
|
214
230
|
while @current_frame.complete?
|
@@ -248,6 +264,9 @@ module Cassandra
|
|
248
264
|
if response.is_a?(Protocol::SetKeyspaceResultResponse)
|
249
265
|
@keyspace = response.keyspace
|
250
266
|
end
|
267
|
+
if response.is_a?(Protocol::SchemaChangeResultResponse) && response.change == 'DROPPED' && response.keyspace == @keyspace && response.table.empty?
|
268
|
+
@keyspace = nil
|
269
|
+
end
|
251
270
|
flush_request_queue
|
252
271
|
unless promise.timed_out?
|
253
272
|
promise.fulfill(response)
|
@@ -291,9 +310,22 @@ module Cassandra
|
|
291
310
|
end
|
292
311
|
|
293
312
|
def socket_closed(cause)
|
294
|
-
|
313
|
+
if cause
|
314
|
+
e = Errors::IOError.new(cause.message)
|
315
|
+
e.set_backtrace(cause.backtrace)
|
316
|
+
|
317
|
+
cause = e
|
318
|
+
end
|
319
|
+
|
320
|
+
request_failure_cause = cause || Errors::IOError.new('Connection closed')
|
295
321
|
promises_to_fail = nil
|
296
322
|
@lock.synchronize do
|
323
|
+
@scheduler.cancel_timer(@heartbeat) if @heartbeat
|
324
|
+
@scheduler.cancel_timer(@terminate) if @terminate
|
325
|
+
|
326
|
+
@heartbeat = nil
|
327
|
+
@terminate = nil
|
328
|
+
|
297
329
|
promises_to_fail = @promises.compact
|
298
330
|
promises_to_fail.concat(@request_queue_in)
|
299
331
|
promises_to_fail.concat(@request_queue_out)
|
@@ -311,6 +343,41 @@ module Cassandra
|
|
311
343
|
end
|
312
344
|
end
|
313
345
|
|
346
|
+
def schedule_heartbeat
|
347
|
+
return unless @heartbeat_interval
|
348
|
+
|
349
|
+
timer = nil
|
350
|
+
|
351
|
+
@lock.synchronize do
|
352
|
+
@scheduler.cancel_timer(@heartbeat) if @heartbeat && !@heartbeat.resolved?
|
353
|
+
|
354
|
+
@heartbeat = timer = @scheduler.schedule_timer(@heartbeat_interval)
|
355
|
+
end
|
356
|
+
|
357
|
+
timer.on_value do
|
358
|
+
send_request(HEARTBEAT, nil, false).on_value do
|
359
|
+
schedule_heartbeat
|
360
|
+
end
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
def reschedule_termination
|
365
|
+
return unless @idle_timeout
|
366
|
+
|
367
|
+
timer = nil
|
368
|
+
|
369
|
+
@lock.synchronize do
|
370
|
+
@scheduler.cancel_timer(@terminate) if @terminate
|
371
|
+
|
372
|
+
@terminate = timer = @scheduler.schedule_timer(@idle_timeout)
|
373
|
+
end
|
374
|
+
|
375
|
+
timer.on_value do
|
376
|
+
@terminate = nil
|
377
|
+
@connection.close(TERMINATED)
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
314
381
|
def next_stream_id
|
315
382
|
if (stream_id = @promises.index(nil))
|
316
383
|
stream_id
|
@@ -318,6 +385,9 @@ module Cassandra
|
|
318
385
|
nil
|
319
386
|
end
|
320
387
|
end
|
388
|
+
|
389
|
+
HEARTBEAT = OptionsRequest.new
|
390
|
+
TERMINATED = Errors::TimeoutError.new('Terminated due to inactivity')
|
321
391
|
end
|
322
392
|
end
|
323
393
|
end
|