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

Sign up to get free protection for your applications and to get access to all the features.
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