cassandra-driver 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -23,24 +23,31 @@ module Cassandra
23
23
  attr_reader :credentials, :auth_provider, :compressor, :port,
24
24
  :connect_timeout, :ssl, :connections_per_local_node,
25
25
  :connections_per_remote_node, :heartbeat_interval,
26
- :idle_timeout
26
+ :idle_timeout, :schema_refresh_delay, :schema_refresh_timeout
27
27
  attr_accessor :protocol_version
28
28
 
29
- def initialize(protocol_version, credentials, auth_provider, compressor, port, connect_timeout, ssl, connections_per_local_node, connections_per_remote_node, heartbeat_interval, idle_timeout)
30
- @protocol_version = protocol_version
31
- @credentials = credentials
32
- @auth_provider = auth_provider
33
- @compressor = compressor
34
- @port = port
35
- @connect_timeout = connect_timeout
36
- @ssl = ssl
37
- @heartbeat_interval = heartbeat_interval
38
- @idle_timeout = idle_timeout
29
+ def initialize(protocol_version, credentials, auth_provider, compressor, port, connect_timeout, ssl, connections_per_local_node, connections_per_remote_node, heartbeat_interval, idle_timeout, synchronize_schema, schema_refresh_delay, schema_refresh_timeout)
30
+ @protocol_version = protocol_version
31
+ @credentials = credentials
32
+ @auth_provider = auth_provider
33
+ @compressor = compressor
34
+ @port = port
35
+ @connect_timeout = connect_timeout
36
+ @ssl = ssl
37
+ @heartbeat_interval = heartbeat_interval
38
+ @idle_timeout = idle_timeout
39
+ @synchronize_schema = synchronize_schema
40
+ @schema_refresh_delay = schema_refresh_delay
41
+ @schema_refresh_timeout = schema_refresh_timeout
39
42
 
40
43
  @connections_per_local_node = connections_per_local_node
41
44
  @connections_per_remote_node = connections_per_remote_node
42
45
  end
43
46
 
47
+ def synchronize_schema?
48
+ @synchronize_schema
49
+ end
50
+
44
51
  def compression
45
52
  @compressor && @compressor.algorithm
46
53
  end
@@ -130,6 +130,9 @@ module Cassandra
130
130
 
131
131
  return self unless keyspace
132
132
 
133
+ columns = columns.each_with_object(::Hash.new) do |row, index|
134
+ index[row['column_name']] = row
135
+ end
133
136
  table = create_table(table, columns, host.release_version)
134
137
  keyspace = keyspace.update_table(table)
135
138
 
@@ -139,7 +142,25 @@ module Cassandra
139
142
  @keyspaces = keyspaces
140
143
  end
141
144
 
142
- keyspace_updated(keyspace)
145
+ keyspace_changed(keyspace)
146
+
147
+ self
148
+ end
149
+
150
+ def delete_table(keyspace_name, table_name)
151
+ keyspace = @keyspaces[keyspace_name]
152
+
153
+ return self unless keyspace
154
+
155
+ keyspace = keyspace.delete_table(table_name)
156
+
157
+ synchronize do
158
+ keyspaces = @keyspaces.dup
159
+ keyspaces[keyspace_name] = keyspace
160
+ @keyspaces = keyspaces
161
+ end
162
+
163
+ keyspace_changed(keyspace)
143
164
 
144
165
  self
145
166
  end
@@ -20,7 +20,7 @@ module Cassandra
20
20
  # @private
21
21
  class Driver
22
22
  def self.let(name, &block)
23
- define_method(name) { @instances[name] ||= @defaults.fetch(name) { instance_eval(&block) } }
23
+ define_method(name) { @instances.has_key?(name) ? @instances[name] : @instances[name] = instance_eval(&block) }
24
24
  define_method(:"#{name}=") { |object| @instances[name] = object }
25
25
  end
26
26
 
@@ -42,7 +42,9 @@ module Cassandra
42
42
  no_replication_strategy
43
43
  )
44
44
  }
45
- let(:futures_factory) { Future }
45
+
46
+ let(:executor) { Executors::ThreadPool.new(thread_pool_size) }
47
+ let(:futures_factory) { Future::Factory.new(executor) }
46
48
 
47
49
  let(:schema_type_parser) { Cluster::Schema::TypeParser.new }
48
50
 
@@ -56,9 +58,9 @@ module Cassandra
56
58
 
57
59
  let(:connector) { Cluster::Connector.new(logger, io_reactor, cluster_registry, connection_options, execution_options) }
58
60
 
59
- let(:control_connection) { Cluster::ControlConnection.new(logger, io_reactor, cluster_registry, cluster_schema, cluster_metadata, load_balancing_policy, reconnection_policy, address_resolution_policy, connector) }
61
+ let(:control_connection) { Cluster::ControlConnection.new(logger, io_reactor, cluster_registry, cluster_schema, cluster_metadata, load_balancing_policy, reconnection_policy, address_resolution_policy, connector, connection_options) }
60
62
 
61
- let(:cluster) { Cluster.new(logger, io_reactor, control_connection, cluster_registry, cluster_schema, cluster_metadata, execution_options, connection_options, load_balancing_policy, reconnection_policy, retry_policy, address_resolution_policy, connector, futures_factory) }
63
+ let(:cluster) { Cluster.new(logger, io_reactor, executor, control_connection, cluster_registry, cluster_schema, cluster_metadata, execution_options, connection_options, load_balancing_policy, reconnection_policy, retry_policy, address_resolution_policy, connector, futures_factory) }
62
64
 
63
65
  let(:execution_options) do
64
66
  Execution::Options.new({
@@ -69,7 +71,24 @@ module Cassandra
69
71
  })
70
72
  end
71
73
 
72
- let(:connection_options) { Cluster::Options.new(protocol_version, credentials, auth_provider, compressor, port, connect_timeout, ssl, connections_per_local_node, connections_per_remote_node, heartbeat_interval, idle_timeout) }
74
+ let(:connection_options) do
75
+ Cluster::Options.new(
76
+ protocol_version,
77
+ credentials,
78
+ auth_provider,
79
+ compressor,
80
+ port,
81
+ connect_timeout,
82
+ ssl,
83
+ connections_per_local_node,
84
+ connections_per_remote_node,
85
+ heartbeat_interval,
86
+ idle_timeout,
87
+ synchronize_schema,
88
+ schema_refresh_delay,
89
+ schema_refresh_timeout
90
+ )
91
+ end
73
92
 
74
93
  let(:port) { 9042 }
75
94
  let(:protocol_version) { 2 }
@@ -80,7 +99,7 @@ module Cassandra
80
99
  let(:credentials) { nil }
81
100
  let(:auth_provider) { nil }
82
101
  let(:datacenter) { nil }
83
- let(:load_balancing_policy) { LoadBalancing::Policies::TokenAware.new(LoadBalancing::Policies::DCAwareRoundRobin.new(datacenter, 0)) }
102
+ let(:load_balancing_policy) { LoadBalancing::Policies::TokenAware.new(LoadBalancing::Policies::DCAwareRoundRobin.new(datacenter, 0), shuffle_replicas) }
84
103
  let(:reconnection_policy) { Reconnection::Policies::Exponential.new(0.5, 30, 2) }
85
104
  let(:retry_policy) { Retry::Policies::Default.new }
86
105
  let(:address_resolution_policy) { AddressResolution::Policies::None.new }
@@ -90,6 +109,11 @@ module Cassandra
90
109
  let(:heartbeat_interval) { 30 }
91
110
  let(:idle_timeout) { 60 }
92
111
  let(:timeout) { 10 }
112
+ let(:synchronize_schema) { true }
113
+ let(:schema_refresh_delay) { 1 }
114
+ let(:schema_refresh_timeout) { 10 }
115
+ let(:thread_pool_size) { 4 }
116
+ let(:shuffle_replicas) { true }
93
117
 
94
118
  let(:connections_per_local_node) { 2 }
95
119
  let(:connections_per_remote_node) { 1 }
@@ -97,8 +121,7 @@ module Cassandra
97
121
  let(:listeners) { [] }
98
122
 
99
123
  def initialize(defaults = {})
100
- @defaults = defaults
101
- @instances = {}
124
+ @instances = defaults
102
125
  end
103
126
 
104
127
  def connect(addresses)
@@ -113,7 +136,18 @@ module Cassandra
113
136
  addresses.each {|address| cluster_registry.host_found(address)}
114
137
 
115
138
  logger.info('Establishing control connection')
116
- control_connection.connect_async.map(cluster)
139
+
140
+ promise = futures_factory.promise
141
+
142
+ control_connection.connect_async.on_complete do |f|
143
+ if f.resolved?
144
+ promise.fulfill(cluster)
145
+ else
146
+ f.on_failure {|e| promise.break(e)}
147
+ end
148
+ end
149
+
150
+ promise.future
117
151
  end
118
152
  end
119
153
  end
@@ -31,6 +31,20 @@ module Cassandra
31
31
  # @return [Numeric] request timeout interval
32
32
  attr_reader :timeout
33
33
 
34
+ # @return [String] paging state
35
+ #
36
+ # @note Although this feature exists to allow web applications to store
37
+ # paging state in an [HTTP cookie](http://en.wikipedia.org/wiki/HTTP_cookie), **it is not safe to
38
+ # expose without encrypting or otherwise securing it**. Paging state
39
+ # contains information internal to the Apache Cassandra cluster, such as
40
+ # partition key and data. Additionally, if a paging state is sent with CQL
41
+ # statement, different from the original, the behavior of Cassandra is
42
+ # undefined and will likely cause a server process of the coordinator of
43
+ # such request to abort.
44
+ #
45
+ # @see Cassandra::Result#paging_state
46
+ attr_reader :paging_state
47
+
34
48
  # @private
35
49
  def initialize(options)
36
50
  consistency = options[:consistency]
@@ -38,6 +52,7 @@ module Cassandra
38
52
  trace = options[:trace]
39
53
  timeout = options[:timeout]
40
54
  serial_consistency = options[:serial_consistency]
55
+ paging_state = options[:paging_state]
41
56
 
42
57
  Util.assert_one_of(CONSISTENCIES, consistency) { ":consistency must be one of #{CONSISTENCIES.inspect}, #{consistency.inspect} given" }
43
58
 
@@ -46,7 +61,7 @@ module Cassandra
46
61
  end
47
62
 
48
63
  unless page_size.nil?
49
- page_size = options[:page_size] = Integer(page_size)
64
+ page_size = Integer(page_size)
50
65
  Util.assert(page_size > 0) { ":page_size must be a positive integer, #{page_size.inspect} given" }
51
66
  end
52
67
 
@@ -55,11 +70,17 @@ module Cassandra
55
70
  Util.assert(timeout > 0) { ":timeout must be greater than 0, #{timeout} given" }
56
71
  end
57
72
 
73
+ unless paging_state.nil?
74
+ paging_state = String(paging_state)
75
+ Util.assert_not_empty(paging_state) { ":paging_state must not be empty" }
76
+ end
77
+
58
78
  @consistency = consistency
59
79
  @page_size = page_size
60
80
  @trace = !!trace
61
81
  @timeout = timeout
62
82
  @serial_consistency = serial_consistency
83
+ @paging_state = paging_state
63
84
  end
64
85
 
65
86
  # @return [Boolean] whether request tracing was enabled
@@ -68,8 +89,14 @@ module Cassandra
68
89
  end
69
90
 
70
91
  # @private
71
- def override(options)
72
- Options.new(to_h.merge!(options))
92
+ def override(*options)
93
+ merged = options.unshift(to_h).inject do |base, opts|
94
+ next base unless opts
95
+ Util.assert_instance_of(::Hash, opts) { "options must be a Hash, #{options.inspect} given" }
96
+ base.merge!(opts)
97
+ end
98
+
99
+ Options.new(merged)
73
100
  end
74
101
 
75
102
  # @private
@@ -0,0 +1,111 @@
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
+ module Executors
22
+ class ThreadPool
23
+ # @private
24
+ class Task
25
+ def initialize(*args, &block)
26
+ @args = args
27
+ @block = block
28
+ end
29
+
30
+ def run
31
+ @block.call(*@args)
32
+ rescue ::Exception
33
+ ensure
34
+ @args = @block = nil
35
+ end
36
+ end
37
+
38
+ include MonitorMixin
39
+
40
+ def initialize(size)
41
+ mon_initialize
42
+
43
+ @cond = new_cond
44
+ @tasks = ::Array.new
45
+ @waiting = 0
46
+ @pool = ::Array.new(size, &method(:spawn_thread))
47
+ @term = false
48
+ end
49
+
50
+ def execute(*args, &block)
51
+ synchronize do
52
+ @tasks << Task.new(*args, &block)
53
+ @cond.signal if @waiting > 0
54
+ end
55
+
56
+ nil
57
+ end
58
+
59
+ def shutdown
60
+ execute do
61
+ synchronize do
62
+ @term = true
63
+ @cond.broadcast if @waiting > 0
64
+ end
65
+ end
66
+
67
+ nil
68
+ end
69
+
70
+ private
71
+
72
+ def spawn_thread(i)
73
+ Thread.new(&method(:run))
74
+ end
75
+
76
+ def run
77
+ Thread.current.abort_on_exception = true
78
+
79
+ loop do
80
+ tasks = nil
81
+
82
+ synchronize do
83
+ @waiting += 1
84
+ @cond.wait while !@term && @tasks.empty?
85
+ @waiting -= 1
86
+
87
+ return if @tasks.empty?
88
+
89
+ tasks = @tasks
90
+ @tasks = ::Array.new
91
+ end
92
+
93
+ tasks.each(&:run).clear
94
+ end
95
+ end
96
+ end
97
+
98
+ class SameThread
99
+ def execute(*args, &block)
100
+ yield(*args)
101
+ nil
102
+ rescue ::Exception
103
+ nil
104
+ end
105
+
106
+ def shutdown
107
+ nil
108
+ end
109
+ end
110
+ end
111
+ end
@@ -48,8 +48,6 @@ 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
-
53
51
  @error = error
54
52
  end
55
53
 
@@ -163,18 +161,64 @@ module Cassandra
163
161
  end
164
162
  end
165
163
 
164
+ # @private
165
+ class Factory
166
+ def initialize(executor)
167
+ @executor = executor
168
+ end
169
+
170
+ def value(value)
171
+ Value.new(value)
172
+ end
173
+
174
+ def error(error)
175
+ Error.new(error)
176
+ end
177
+
178
+ def promise
179
+ Promise.new(@executor)
180
+ end
181
+
182
+ def all(*futures)
183
+ futures = Array(futures.first) if futures.one?
184
+ monitor = Monitor.new
185
+ promise = Promise.new(@executor)
186
+ remaining = futures.length
187
+ values = Array.new(remaining)
188
+
189
+ futures.each_with_index do |future, i|
190
+ future.on_complete do |v, e|
191
+ if e
192
+ promise.break(e)
193
+ else
194
+ done = false
195
+ monitor.synchronize do
196
+ remaining -= 1
197
+ done = (remaining == 0)
198
+ values[i] = v
199
+ end
200
+ promise.fulfill(values) if done
201
+ end
202
+ end
203
+ end
204
+ end
205
+ end
206
+
207
+ # @private
208
+ @@factory = Factory.new(Executors::SameThread.new)
209
+
166
210
  # Returns a future resolved to a given value
167
211
  # @param value [Object] value for the future
168
212
  # @return [Cassandra::Future<Object>] a future value
169
213
  def self.value(value)
170
- Value.new(value)
214
+ @@factory.value(value)
171
215
  end
172
216
 
173
217
  # Returns a future resolved to a given error
174
218
  # @param error [Exception] error for the future
175
219
  # @return [Cassandra::Future<Exception>] a future error
176
220
  def self.error(error)
177
- Error.new(error)
221
+ @@factory.error(error)
178
222
  end
179
223
 
180
224
  # Returns a future that resolves with values of all futures
@@ -186,35 +230,7 @@ module Cassandra
186
230
  # combine
187
231
  # @return [Cassandra::Future<Array<Object>>] a combined future
188
232
  def self.all(*futures)
189
- futures = Array(futures.first) if futures.one?
190
- monitor = Monitor.new
191
- promise = Promise.new
192
- remaining = futures.length
193
- values = Array.new(remaining)
194
-
195
- futures.each_with_index do |future, i|
196
- future.on_complete do |v, e|
197
- if e
198
- promise.break(e)
199
- else
200
- done = false
201
- monitor.synchronize do
202
- remaining -= 1
203
- done = (remaining == 0)
204
- values[i] = v
205
- end
206
- promise.fulfill(values) if done
207
- end
208
- end
209
- end
210
-
211
- promise.future
212
- end
213
-
214
- # @private
215
- # Returns a new promise instance
216
- def self.promise
217
- Promise.new
233
+ @@factory.all(*futures)
218
234
  end
219
235
 
220
236
  # @private
@@ -222,24 +238,6 @@ module Cassandra
222
238
  @signal = signal
223
239
  end
224
240
 
225
- # Returns future value or raises future error
226
- # @note This method blocks until a future is resolved
227
- # @raise [Exception] error used to resolve this future if any
228
- # @return [Object] value used to resolve this future if any
229
- def get
230
- @signal.get
231
- end
232
-
233
- # Block until the future has been resolved
234
- # @note This method blocks until a future is resolved
235
- # @note This method won't raise any errors or return anything but the
236
- # future itself
237
- # @return [self]
238
- def join
239
- @signal.join
240
- self
241
- end
242
-
243
241
  # Run block when future resolves to a value
244
242
  # @note The block can be called synchronously from current thread if the
245
243
  # future has already been resolved, or, asynchronously, from background
@@ -350,6 +348,25 @@ module Cassandra
350
348
  raise ::ArgumentError, "no block given" unless block_given?
351
349
  @signal.fallback(&block)
352
350
  end
351
+
352
+ # Returns future value or raises future error
353
+ # @note This method blocks until a future is resolved
354
+ # @raise [Exception] error used to resolve this future if any
355
+ # @return [Object] value used to resolve this future if any
356
+ def get
357
+ @signal.get
358
+
359
+ end
360
+
361
+ # Block until the future has been resolved
362
+ # @note This method blocks until a future is resolved
363
+ # @note This method won't raise any errors or return anything but the
364
+ # future itself
365
+ # @return [self]
366
+ def join
367
+ @signal.join
368
+ self
369
+ end
353
370
  end
354
371
 
355
372
  # @private
@@ -457,10 +474,11 @@ module Cassandra
457
474
 
458
475
  include MonitorMixin
459
476
 
460
- def initialize
477
+ def initialize(executor)
461
478
  mon_initialize
462
479
 
463
480
  @cond = new_cond
481
+ @executor = executor
464
482
  @state = :pending
465
483
  @waiting = 0
466
484
  @error = nil
@@ -473,8 +491,6 @@ module Cassandra
473
491
  raise ::ArgumentError, "error must be an exception, #{error.inspect} given"
474
492
  end
475
493
 
476
- error.set_backtrace(caller) unless error.backtrace
477
-
478
494
  return unless @state == :pending
479
495
 
480
496
  listeners = nil
@@ -488,12 +504,14 @@ module Cassandra
488
504
  listeners, @listeners = @listeners, nil
489
505
  end
490
506
 
491
- listeners.each do |listener|
492
- listener.failure(error) rescue nil
493
- end
507
+ @executor.execute do
508
+ listeners.each do |listener|
509
+ listener.failure(error) rescue nil
510
+ end
494
511
 
495
- synchronize do
496
- @cond.broadcast if @waiting > 0
512
+ synchronize do
513
+ @cond.broadcast if @waiting > 0
514
+ end
497
515
  end
498
516
 
499
517
  self
@@ -513,12 +531,14 @@ module Cassandra
513
531
  listeners, @listeners = @listeners, nil
514
532
  end
515
533
 
516
- listeners.each do |listener|
517
- listener.success(value) rescue nil
518
- end
534
+ @executor.execute do
535
+ listeners.each do |listener|
536
+ listener.success(value) rescue nil
537
+ end
519
538
 
520
- synchronize do
521
- @cond.broadcast if @waiting > 0
539
+ synchronize do
540
+ @cond.broadcast if @waiting > 0
541
+ end
522
542
  end
523
543
 
524
544
  self
@@ -612,7 +632,7 @@ module Cassandra
612
632
  if @state == :pending
613
633
  synchronize do
614
634
  if @state == :pending
615
- promise = Promise.new
635
+ promise = Promise.new(@executor)
616
636
  listener = Listeners::Then.new(promise, &block)
617
637
  @listeners << listener
618
638
  return promise.future
@@ -635,7 +655,7 @@ module Cassandra
635
655
  if @state == :pending
636
656
  synchronize do
637
657
  if @state == :pending
638
- promise = Promise.new
658
+ promise = Promise.new(@executor)
639
659
  listener = Listeners::Fallback.new(promise, &block)
640
660
  @listeners << listener
641
661
  return promise.future
@@ -657,8 +677,8 @@ module Cassandra
657
677
 
658
678
  attr_reader :future
659
679
 
660
- def initialize
661
- @signal = Signal.new
680
+ def initialize(executor)
681
+ @signal = Signal.new(executor)
662
682
  @future = Future.new(@signal)
663
683
  end
664
684