cassandra-driver 1.0.0.rc.1-java → 1.1.0-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +58 -18
  3. data/lib/cassandra.rb +132 -93
  4. data/lib/cassandra/auth.rb +3 -3
  5. data/lib/cassandra/cluster.rb +65 -39
  6. data/lib/cassandra/cluster/client.rb +67 -28
  7. data/lib/cassandra/{client/connection_manager.rb → cluster/connection_pool.rb} +9 -3
  8. data/lib/cassandra/cluster/connector.rb +101 -30
  9. data/lib/cassandra/cluster/control_connection.rb +160 -96
  10. data/lib/cassandra/{client/null_logger.rb → cluster/failed_connection.rb} +12 -14
  11. data/lib/cassandra/cluster/options.rb +26 -11
  12. data/lib/cassandra/cluster/schema.rb +22 -1
  13. data/lib/cassandra/column.rb +5 -0
  14. data/lib/cassandra/driver.rb +46 -12
  15. data/lib/cassandra/errors.rb +5 -5
  16. data/lib/cassandra/execution/options.rb +42 -8
  17. data/lib/cassandra/execution/trace.rb +4 -4
  18. data/lib/cassandra/executors.rb +111 -0
  19. data/lib/cassandra/future.rb +88 -64
  20. data/lib/cassandra/keyspace.rb +12 -0
  21. data/lib/cassandra/load_balancing.rb +10 -0
  22. data/lib/cassandra/load_balancing/policies/dc_aware_round_robin.rb +10 -5
  23. data/lib/cassandra/load_balancing/policies/round_robin.rb +7 -5
  24. data/lib/cassandra/load_balancing/policies/token_aware.rb +31 -10
  25. data/lib/cassandra/load_balancing/policies/white_list.rb +4 -7
  26. data/lib/cassandra/null_logger.rb +35 -0
  27. data/lib/cassandra/protocol/cql_protocol_handler.rb +8 -1
  28. data/lib/cassandra/protocol/requests/query_request.rb +1 -11
  29. data/lib/cassandra/result.rb +34 -9
  30. data/lib/cassandra/session.rb +6 -0
  31. data/lib/cassandra/statements/prepared.rb +5 -1
  32. data/lib/cassandra/table.rb +5 -0
  33. data/lib/cassandra/util.rb +130 -0
  34. data/lib/cassandra/version.rb +1 -1
  35. metadata +40 -50
  36. data/lib/cassandra/client.rb +0 -144
  37. data/lib/cassandra/client/batch.rb +0 -212
  38. data/lib/cassandra/client/client.rb +0 -591
  39. data/lib/cassandra/client/column_metadata.rb +0 -54
  40. data/lib/cassandra/client/connector.rb +0 -273
  41. data/lib/cassandra/client/execute_options_decoder.rb +0 -59
  42. data/lib/cassandra/client/peer_discovery.rb +0 -50
  43. data/lib/cassandra/client/prepared_statement.rb +0 -314
  44. data/lib/cassandra/client/query_result.rb +0 -230
  45. data/lib/cassandra/client/request_runner.rb +0 -70
  46. data/lib/cassandra/client/result_metadata.rb +0 -48
  47. data/lib/cassandra/client/void_result.rb +0 -78
@@ -23,23 +23,38 @@ 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
46
+
47
+ def synchronize_schema?
48
+ @synchronize_schema
49
+ end
50
+
51
+ def compression
52
+ @compressor && @compressor.algorithm
53
+ end
54
+
55
+ def create_authenticator(authentication_class)
56
+ @auth_provider && @auth_provider.create_authenticator(authentication_class)
57
+ end
43
58
  end
44
59
  end
45
60
  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
@@ -78,6 +78,11 @@ module Cassandra
78
78
  cql
79
79
  end
80
80
 
81
+ # @return [String] a CLI-friendly column representation
82
+ def inspect
83
+ "#<#{self.class.name}:0x#{self.object_id.to_s(16)} @name=#{@name}>"
84
+ end
85
+
81
86
  # @return [Boolean] whether this column is equal to the other
82
87
  def eql?(other)
83
88
  other.is_a?(Column) &&
@@ -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
 
@@ -54,11 +56,11 @@ module Cassandra
54
56
  let(:ordered_partitioner) { Cluster::Schema::Partitioners::Ordered.new }
55
57
  let(:random_partitioner) { Cluster::Schema::Partitioners::Random.new }
56
58
 
57
- let(:connector) { Cluster::Connector.new(logger, io_reactor, cluster_registry, connection_options) }
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,27 +71,49 @@ 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 }
76
95
  let(:connect_timeout) { 10 }
77
96
  let(:ssl) { false }
78
- let(:logger) { Client::NullLogger.new }
97
+ let(:logger) { NullLogger.new }
79
98
  let(:compressor) { nil }
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 }
87
106
  let(:consistency) { :one }
88
107
  let(:trace) { false }
89
- let(:page_size) { nil }
108
+ let(:page_size) { 10000 }
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
@@ -49,28 +49,28 @@ module Cassandra
49
49
  end
50
50
 
51
51
  # Mixed into all internal driver errors.
52
- module InternalError
52
+ class InternalError < ::RuntimeError
53
53
  include Error, HostError
54
54
  end
55
55
 
56
56
  # Raised when data decoding fails.
57
57
  class DecodingError < ::RuntimeError
58
- include InternalError
58
+ include Error, HostError
59
59
  end
60
60
 
61
61
  # Raised when data encoding fails.
62
62
  class EncodingError < ::RuntimeError
63
- include InternalError
63
+ include Error, HostError
64
64
  end
65
65
 
66
66
  # Raised when a connection level error occured.
67
67
  class IOError < ::IOError
68
- include InternalError
68
+ include Error, HostError
69
69
  end
70
70
 
71
71
  # Raised when a timeout has occured.
72
72
  class TimeoutError < ::Timeout::Error
73
- include InternalError
73
+ include Error, HostError
74
74
  end
75
75
 
76
76
  # Mixed into all request execution errors.
@@ -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,21 +52,35 @@ 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]
56
+
57
+ Util.assert_one_of(CONSISTENCIES, consistency) { ":consistency must be one of #{CONSISTENCIES.inspect}, #{consistency.inspect} given" }
41
58
 
42
- raise ::ArgumentError, ":consistency must be one of #{CONSISTENCIES.inspect}, #{consistency.inspect} given" unless CONSISTENCIES.include?(consistency)
43
- raise ::ArgumentError, ":serial_consistency must be one of #{SERIAL_CONSISTENCIES.inspect}, #{serial_consistency.inspect} given" if serial_consistency && !SERIAL_CONSISTENCIES.include?(serial_consistency)
59
+ unless serial_consistency.nil?
60
+ Util.assert_one_of(SERIAL_CONSISTENCIES, serial_consistency) { ":serial_consistency must be one of #{SERIAL_CONSISTENCIES.inspect}, #{serial_consistency.inspect} given" }
61
+ end
44
62
 
45
- page_size = page_size && Integer(page_size)
46
- timeout = timeout && Integer(timeout)
63
+ unless page_size.nil?
64
+ page_size = Integer(page_size)
65
+ Util.assert(page_size > 0) { ":page_size must be a positive integer, #{page_size.inspect} given" }
66
+ end
47
67
 
48
- raise ::ArgumentError, ":page_size must be greater than 0, #{page_size.inspect} given" if page_size && page_size <= 0
49
- raise ::ArgumentError, ":timeout must be greater than 0, #{timeout.inspect} given" if timeout && timeout <= 0
68
+ unless timeout.nil?
69
+ Util.assert_instance_of(::Numeric, timeout) { ":timeout must be a number of seconds, #{timeout} given" }
70
+ Util.assert(timeout > 0) { ":timeout must be greater than 0, #{timeout} given" }
71
+ end
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
50
77
 
51
78
  @consistency = consistency
52
79
  @page_size = page_size
53
80
  @trace = !!trace
54
81
  @timeout = timeout
55
82
  @serial_consistency = serial_consistency
83
+ @paging_state = paging_state
56
84
  end
57
85
 
58
86
  # @return [Boolean] whether request tracing was enabled
@@ -61,8 +89,14 @@ module Cassandra
61
89
  end
62
90
 
63
91
  # @private
64
- def override(options)
65
- 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)
66
100
  end
67
101
 
68
102
  # @private
@@ -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, @id), VOID_OPTIONS).get.first
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, @id), VOID_OPTIONS).get.each do |row|
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
 
@@ -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