cassandra-driver 3.0.3 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +8 -8
  2. data/README.md +46 -31
  3. data/lib/cassandra.rb +35 -44
  4. data/lib/cassandra/cluster.rb +40 -11
  5. data/lib/cassandra/cluster/client.rb +193 -159
  6. data/lib/cassandra/cluster/connector.rb +12 -10
  7. data/lib/cassandra/cluster/control_connection.rb +38 -10
  8. data/lib/cassandra/cluster/options.rb +8 -4
  9. data/lib/cassandra/cluster/registry.rb +1 -2
  10. data/lib/cassandra/cluster/schema/fetchers.rb +122 -26
  11. data/lib/cassandra/column_container.rb +9 -4
  12. data/lib/cassandra/custom_data.rb +24 -22
  13. data/lib/cassandra/driver.rb +30 -13
  14. data/lib/cassandra/errors.rb +12 -2
  15. data/lib/cassandra/execution/options.rb +52 -16
  16. data/lib/cassandra/execution/profile.rb +150 -0
  17. data/lib/cassandra/execution/profile_manager.rb +71 -0
  18. data/lib/cassandra/execution/trace.rb +5 -4
  19. data/lib/cassandra/executors.rb +1 -1
  20. data/lib/cassandra/index.rb +1 -1
  21. data/lib/cassandra/keyspace.rb +36 -1
  22. data/lib/cassandra/protocol.rb +5 -0
  23. data/lib/cassandra/protocol/coder.rb +2 -1
  24. data/lib/cassandra/protocol/cql_byte_buffer.rb +21 -0
  25. data/lib/cassandra/protocol/responses/read_failure_error_response.rb +10 -4
  26. data/lib/cassandra/protocol/responses/write_failure_error_response.rb +14 -8
  27. data/lib/cassandra/protocol/v3.rb +2 -1
  28. data/lib/cassandra/protocol/v4.rb +58 -20
  29. data/lib/cassandra/result.rb +1 -1
  30. data/lib/cassandra/session.rb +43 -16
  31. data/lib/cassandra/statements/bound.rb +5 -1
  32. data/lib/cassandra/statements/prepared.rb +8 -3
  33. data/lib/cassandra/table.rb +72 -0
  34. data/lib/cassandra/trigger.rb +67 -0
  35. data/lib/cassandra/types.rb +12 -24
  36. data/lib/cassandra/udt.rb +3 -6
  37. data/lib/cassandra/uuid/generator.rb +6 -3
  38. data/lib/cassandra/version.rb +1 -1
  39. metadata +5 -2
@@ -0,0 +1,71 @@
1
+ # encoding: utf-8
2
+
3
+ #--
4
+ # Copyright 2013-2016 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
+ module Execution
21
+ # @private
22
+ class ProfileManager
23
+ attr_reader :profiles
24
+ attr_reader :load_balancing_policies
25
+
26
+ def initialize(default_profile, profiles)
27
+ # default_profile is the default profile that we constructed internally. However, the user can override it
28
+ # with their own :default profile. If that happens, ignore the internally generated default profile.
29
+
30
+ profiles[:default] = default_profile unless profiles.key?(:default)
31
+
32
+ # Save off all of the load-balancing policies for easy access.
33
+ @load_balancing_policies = Set.new
34
+ profiles.each do |name, profile|
35
+ @load_balancing_policies << profile.load_balancing_policy
36
+ end
37
+ @profiles = profiles
38
+ end
39
+
40
+ def default_profile
41
+ @profiles[:default]
42
+ end
43
+
44
+ def distance(host)
45
+ # Return the min distance to the host, as per each lbp.
46
+ distances = Set.new
47
+ @load_balancing_policies.each do |lbp|
48
+ distances.add(lbp.distance(host))
49
+ end
50
+ return :local if distances.include?(:local)
51
+ return :remote if distances.include?(:remote)
52
+
53
+ # Fall back to ignore the host.
54
+ :ignore
55
+ end
56
+
57
+ # NOTE: It's only safe to call add_profile when setting up the cluster object. In particular,
58
+ # this is only ok before calling Driver#connect.
59
+ def add_profile(name, profile)
60
+ @profiles[name] = profile
61
+ @load_balancing_policies << profile.load_balancing_policy
62
+ end
63
+
64
+ # @private
65
+ def inspect
66
+ "#<#{self.class.name}:0x#{object_id.to_s(16)} " \
67
+ "profiles=#{@profiles.inspect}>"
68
+ end
69
+ end
70
+ end
71
+ end
@@ -53,7 +53,7 @@ module Cassandra
53
53
  attr_reader :id
54
54
 
55
55
  # @private
56
- def initialize(id, client)
56
+ def initialize(id, client, load_balancing_policy)
57
57
  @id = id
58
58
  @client = client
59
59
  @coordinator = nil
@@ -65,6 +65,7 @@ module Cassandra
65
65
  @client_ip = nil
66
66
  @loaded = false
67
67
  @loaded_events = false
68
+ @load_balancing_policy = load_balancing_policy
68
69
 
69
70
  mon_initialize
70
71
  end
@@ -141,12 +142,12 @@ module Cassandra
141
142
 
142
143
  attempt = 1
143
144
  data = @client.query(Statements::Simple.new(SELECT_SESSION % @id),
144
- VOID_OPTIONS).get.first
145
+ VOID_OPTIONS.override(load_balancing_policy: @load_balancing_policy)).get.first
145
146
 
146
147
  while data.nil? && attempt <= 5
147
148
  sleep(attempt * 0.4)
148
149
  data = @client.query(Statements::Simple.new(SELECT_SESSION % @id),
149
- VOID_OPTIONS).get.first
150
+ VOID_OPTIONS.override(load_balancing_policy: @load_balancing_policy)).get.first
150
151
  break if data
151
152
  attempt += 1
152
153
  end
@@ -173,7 +174,7 @@ module Cassandra
173
174
  @events = []
174
175
 
175
176
  @client.query(Statements::Simple.new(SELECT_EVENTS % @id),
176
- VOID_OPTIONS).get.each do |row|
177
+ VOID_OPTIONS.override(load_balancing_policy: @load_balancing_policy)).get.each do |row|
177
178
  @events << Event.new(row['event_id'],
178
179
  row['activity'],
179
180
  row['source'],
@@ -43,8 +43,8 @@ module Cassandra
43
43
  @cond = new_cond
44
44
  @tasks = ::Array.new
45
45
  @waiting = 0
46
- @pool = ::Array.new(size, &method(:spawn_thread))
47
46
  @term = false
47
+ @pool = ::Array.new(size, &method(:spawn_thread))
48
48
  end
49
49
 
50
50
  def execute(*args, &block)
@@ -62,7 +62,7 @@ module Cassandra
62
62
  @options['class_name']
63
63
  end
64
64
 
65
- # @return [String] a cql representation of this table
65
+ # @return [String] a cql representation of this index
66
66
  def to_cql
67
67
  keyspace_name = Util.escape_name(@table.keyspace.name)
68
68
  table_name = Util.escape_name(@table.name)
@@ -69,11 +69,18 @@ module Cassandra
69
69
  @views = views
70
70
 
71
71
  # Set the keyspace attribute on the tables and views.
72
+ # Also set up the index collection on the keyspace and the view-collection on each table.
73
+ @indexes_hash = {}
72
74
  @tables.each_value do |t|
73
75
  t.set_keyspace(self)
76
+ t.each_index do |index|
77
+ @indexes_hash[index.name] = index
78
+ end
74
79
  end
75
80
  @views.each_value do |v|
76
81
  v.set_keyspace(self)
82
+ table = v.base_table
83
+ table.add_view(v) if table
77
84
  end
78
85
  end
79
86
 
@@ -110,6 +117,34 @@ module Cassandra
110
117
  end
111
118
  alias tables each_table
112
119
 
120
+ # @return [Boolean] whether this keyspace has an index with the given name
121
+ # @param name [String] index name
122
+ def has_index?(name)
123
+ @indexes_hash.key?(name)
124
+ end
125
+
126
+ # @return [Cassandra::Index, nil] an index or nil
127
+ # @param name [String] index name
128
+ def index(name)
129
+ @indexes_hash[name]
130
+ end
131
+
132
+ # Yield or enumerate each index defined in this keyspace
133
+ # @overload each_index
134
+ # @yieldparam index [Cassandra::Index] current index
135
+ # @return [Cassandra::Keyspace] self
136
+ # @overload each_index
137
+ # @return [Array<Cassandra::Index>] a list of indexes
138
+ def each_index(&block)
139
+ if block_given?
140
+ @indexes_hash.each_value(&block)
141
+ self
142
+ else
143
+ @indexes_hash.values
144
+ end
145
+ end
146
+ alias indexes each_index
147
+
113
148
  # @return [Boolean] whether this keyspace has a materialized view with the given name
114
149
  # @param name [String] materialized view name
115
150
  def has_materialized_view?(name)
@@ -133,7 +168,7 @@ module Cassandra
133
168
  def each_materialized_view(&block)
134
169
  if block_given?
135
170
  @views.each_value do |v|
136
- block.call(v) if v.base_table
171
+ yield(v) if v.base_table
137
172
  end
138
173
  self
139
174
  else
@@ -49,6 +49,11 @@ module Cassandra
49
49
  SCHEMA_CHANGE_TARGET_FUNCTION = 'FUNCTION'.freeze
50
50
  SCHEMA_CHANGE_TARGET_AGGREGATE = 'AGGREGATE'.freeze
51
51
  end
52
+
53
+ module Versions
54
+ BETA_VERSION = 5
55
+ MAX_SUPPORTED_VERSION = 4
56
+ end
52
57
  end
53
58
  end
54
59
 
@@ -268,7 +268,8 @@ module Cassandra
268
268
  value = ::Hash.new
269
269
 
270
270
  buffer.read_signed_int.times do
271
- value[read_value_v4(buffer, key_type, custom_type_handlers)] = read_value_v4(buffer, value_type, custom_type_handlers)
271
+ value[read_value_v4(buffer, key_type, custom_type_handlers)] =
272
+ read_value_v4(buffer, value_type, custom_type_handlers)
272
273
  end
273
274
 
274
275
  value
@@ -204,6 +204,15 @@ module Cassandra
204
204
  e.backtrace
205
205
  end
206
206
 
207
+ def read_inet_addr
208
+ size = read_byte
209
+ IPAddr.new_ntoh(read(size))
210
+ rescue RangeError => e
211
+ raise Errors::DecodingError,
212
+ "Not enough bytes available to decode an INET addr: #{e.message}",
213
+ e.backtrace
214
+ end
215
+
207
216
  def read_consistency
208
217
  index = read_unsigned_short
209
218
  if index >= CONSISTENCIES.size || CONSISTENCIES[index].nil?
@@ -232,6 +241,18 @@ module Cassandra
232
241
  map
233
242
  end
234
243
 
244
+ def read_reason_map
245
+ # reason_map is new in v5. Starts with an int indicating the number of key-value pairs, followed by
246
+ # the key-value pairs. Keys are inet's, values are short int's.
247
+ map = {}
248
+ map_size = read_int
249
+ map_size.times do
250
+ key = read_inet_addr
251
+ map[key] = read_short
252
+ end
253
+ map
254
+ end
255
+
235
256
  def read_string_multimap
236
257
  map = {}
237
258
  map_size = read_unsigned_short
@@ -19,7 +19,7 @@
19
19
  module Cassandra
20
20
  module Protocol
21
21
  class ReadFailureErrorResponse < ErrorResponse
22
- attr_reader :consistency, :received, :blockfor, :numfailures, :data_present
22
+ attr_reader :consistency, :received, :blockfor, :numfailures, :data_present, :failures_by_node
23
23
 
24
24
  def initialize(custom_payload,
25
25
  warnings,
@@ -29,14 +29,19 @@ module Cassandra
29
29
  received,
30
30
  blockfor,
31
31
  numfailures,
32
- data_present)
32
+ data_present,
33
+ failures_by_node)
33
34
  super(custom_payload, warnings, code, message)
34
35
 
35
36
  @consistency = consistency
36
37
  @received = received
37
38
  @blockfor = blockfor
38
- @numfailures = numfailures
39
39
  @data_present = data_present
40
+ @failures_by_node = failures_by_node
41
+
42
+ # If failures_by_node is set, numfailures isn't, and v.v. Set @numfailures to the size of the failure-map
43
+ # if numfailures is nil.
44
+ @numfailures = numfailures || @failures_by_node.size
40
45
  end
41
46
 
42
47
  def to_error(keyspace, statement, options, hosts, consistency, retries)
@@ -53,7 +58,8 @@ module Cassandra
53
58
  @consistency,
54
59
  @blockfor,
55
60
  @numfailures,
56
- @received)
61
+ @received,
62
+ @failures_by_node)
57
63
  end
58
64
 
59
65
  def to_s
@@ -19,7 +19,7 @@
19
19
  module Cassandra
20
20
  module Protocol
21
21
  class WriteFailureErrorResponse < ErrorResponse
22
- attr_reader :consistency, :received, :blockfor, :numfailures, :write_type
22
+ attr_reader :consistency, :received, :blockfor, :numfailures, :write_type, :failures_by_node
23
23
 
24
24
  def initialize(custom_payload,
25
25
  warnings,
@@ -29,16 +29,21 @@ module Cassandra
29
29
  received,
30
30
  blockfor,
31
31
  numfailures,
32
- write_type)
32
+ write_type,
33
+ failures_by_node)
33
34
  super(custom_payload, warnings, code, message)
34
35
 
35
36
  write_type.downcase!
36
37
 
37
- @consistency = consistency
38
- @received = received
39
- @blockfor = blockfor
40
- @numfailures = numfailures
41
- @write_type = write_type.to_sym
38
+ @consistency = consistency
39
+ @received = received
40
+ @blockfor = blockfor
41
+ @write_type = write_type.to_sym
42
+ @failures_by_node = failures_by_node
43
+
44
+ # If failures_by_node is set, numfailures isn't, and v.v. Set @numfailures to the size of the failure-map
45
+ # if numfailures is nil.
46
+ @numfailures = numfailures || @failures_by_node.size
42
47
  end
43
48
 
44
49
  def to_error(keyspace, statement, options, hosts, consistency, retries)
@@ -55,7 +60,8 @@ module Cassandra
55
60
  @consistency,
56
61
  @blockfor,
57
62
  @numfailures,
58
- @received)
63
+ @received,
64
+ @failures_by_node)
59
65
  end
60
66
 
61
67
  def to_s
@@ -166,7 +166,8 @@ module Cassandra
166
166
  if compression == 1
167
167
  if @compressor
168
168
  buffer = CqlByteBuffer.new(
169
- @compressor.decompress(buffer.read(frame_length)))
169
+ @compressor.decompress(buffer.read(frame_length))
170
+ )
170
171
  frame_length = buffer.size
171
172
  else
172
173
  raise Errors::DecodingError,
@@ -23,7 +23,7 @@ module Cassandra
23
23
  class Encoder
24
24
  HEADER_FORMAT = 'c2ncN'.freeze
25
25
 
26
- def initialize(compressor = nil, protocol_version = 4)
26
+ def initialize(compressor, protocol_version)
27
27
  @compressor = compressor
28
28
  @protocol_version = protocol_version
29
29
  end
@@ -31,6 +31,7 @@ module Cassandra
31
31
  def encode(buffer, request, stream_id)
32
32
  flags = 0
33
33
  flags |= 0x02 if request.trace?
34
+ flags |= 0x10 if @protocol_version == Cassandra::Protocol::Versions::BETA_VERSION
34
35
 
35
36
  body = CqlByteBuffer.new
36
37
 
@@ -178,7 +179,8 @@ module Cassandra
178
179
  if compression
179
180
  if @compressor
180
181
  buffer = CqlByteBuffer.new(
181
- @compressor.decompress(buffer.read(frame_length)))
182
+ @compressor.decompress(buffer.read(frame_length))
183
+ )
182
184
  frame_length = buffer.size
183
185
  else
184
186
  raise Errors::DecodingError,
@@ -259,15 +261,33 @@ module Cassandra
259
261
  buffer.read_int,
260
262
  (buffer.read_byte != 0))
261
263
  when 0x1300
262
- ReadFailureErrorResponse.new(custom_payload,
263
- warnings,
264
- code,
265
- message,
266
- buffer.read_consistency,
267
- buffer.read_int,
268
- buffer.read_int,
269
- buffer.read_int,
270
- (buffer.read_byte != 0))
264
+ cl = buffer.read_consistency
265
+ received = buffer.read_int
266
+ block_for = buffer.read_int
267
+ if protocol_version < 5
268
+ ReadFailureErrorResponse.new(custom_payload,
269
+ warnings,
270
+ code,
271
+ message,
272
+ cl,
273
+ received,
274
+ block_for,
275
+ buffer.read_int,
276
+ (buffer.read_byte != 0),
277
+ nil)
278
+ else
279
+ failures_by_node = buffer.read_reason_map
280
+ ReadFailureErrorResponse.new(custom_payload,
281
+ warnings,
282
+ code,
283
+ message,
284
+ cl,
285
+ received,
286
+ block_for,
287
+ nil,
288
+ (buffer.read_byte != 0),
289
+ failures_by_node)
290
+ end
271
291
  when 0x1400
272
292
  FunctionFailureErrorResponse.new(custom_payload,
273
293
  warnings,
@@ -277,15 +297,33 @@ module Cassandra
277
297
  buffer.read_string,
278
298
  buffer.read_string_list)
279
299
  when 0x1500
280
- WriteFailureErrorResponse.new(custom_payload,
281
- warnings,
282
- code,
283
- message,
284
- buffer.read_consistency,
285
- buffer.read_int,
286
- buffer.read_int,
287
- buffer.read_int,
288
- buffer.read_string)
300
+ cl = buffer.read_consistency
301
+ received = buffer.read_int
302
+ block_for = buffer.read_int
303
+ if protocol_version < 5
304
+ WriteFailureErrorResponse.new(custom_payload,
305
+ warnings,
306
+ code,
307
+ message,
308
+ cl,
309
+ received,
310
+ block_for,
311
+ buffer.read_int,
312
+ buffer.read_string,
313
+ nil)
314
+ else
315
+ failures_by_node = buffer.read_reason_map
316
+ WriteFailureErrorResponse.new(custom_payload,
317
+ warnings,
318
+ code,
319
+ message,
320
+ cl,
321
+ received,
322
+ block_for,
323
+ nil,
324
+ buffer.read_string,
325
+ failures_by_node)
326
+ end
289
327
  when 0x2400
290
328
  AlreadyExistsErrorResponse.new(custom_payload,
291
329
  warnings,