cassandra-driver 1.0.0.beta.1 → 1.0.0.beta.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +20 -12
  3. data/ext/cassandra_murmur3/cassandra_murmur3.c +178 -0
  4. data/ext/cassandra_murmur3/extconf.rb +2 -0
  5. data/lib/cassandra.rb +132 -24
  6. data/lib/cassandra/auth.rb +5 -5
  7. data/lib/cassandra/client/connector.rb +11 -6
  8. data/lib/cassandra/cluster.rb +47 -23
  9. data/lib/cassandra/cluster/client.rb +5 -4
  10. data/lib/cassandra/cluster/connector.rb +17 -4
  11. data/lib/cassandra/cluster/control_connection.rb +21 -16
  12. data/lib/cassandra/cluster/metadata.rb +124 -0
  13. data/lib/cassandra/cluster/options.rb +5 -3
  14. data/lib/cassandra/cluster/registry.rb +26 -9
  15. data/lib/cassandra/cluster/schema.rb +23 -4
  16. data/lib/cassandra/cluster/schema/partitioners.rb +21 -0
  17. data/lib/cassandra/cluster/schema/partitioners/murmur3.rb +47 -0
  18. data/lib/cassandra/cluster/schema/partitioners/ordered.rb +37 -0
  19. data/lib/cassandra/cluster/schema/partitioners/random.rb +37 -0
  20. data/lib/cassandra/cluster/schema/replication_strategies.rb +21 -0
  21. data/lib/cassandra/cluster/schema/replication_strategies/network_topology.rb +92 -0
  22. data/lib/cassandra/cluster/schema/replication_strategies/none.rb +39 -0
  23. data/lib/cassandra/cluster/schema/replication_strategies/simple.rb +44 -0
  24. data/lib/cassandra/cluster/schema/type_parser.rb +1 -1
  25. data/lib/cassandra/compression.rb +1 -1
  26. data/lib/cassandra/driver.rb +29 -4
  27. data/lib/cassandra/execution/options.rb +3 -0
  28. data/lib/cassandra/host.rb +9 -5
  29. data/lib/cassandra/keyspace.rb +17 -4
  30. data/lib/cassandra/listener.rb +3 -3
  31. data/lib/cassandra/load_balancing.rb +12 -11
  32. data/lib/cassandra/load_balancing/policies.rb +2 -1
  33. data/lib/cassandra/load_balancing/policies/dc_aware_round_robin.rb +1 -1
  34. data/lib/cassandra/load_balancing/policies/round_robin.rb +37 -0
  35. data/lib/cassandra/load_balancing/policies/token_aware.rb +119 -0
  36. data/lib/cassandra/protocol/cql_protocol_handler.rb +2 -2
  37. data/lib/cassandra/reconnection.rb +5 -5
  38. data/lib/cassandra/retry.rb +3 -3
  39. data/lib/cassandra/session.rb +7 -0
  40. data/lib/cassandra/statements/bound.rb +4 -2
  41. data/lib/cassandra/statements/prepared.rb +28 -6
  42. data/lib/cassandra/table.rb +49 -4
  43. data/lib/cassandra/time_uuid.rb +1 -0
  44. data/lib/cassandra/util.rb +0 -2
  45. data/lib/cassandra/version.rb +1 -1
  46. metadata +50 -45
@@ -0,0 +1,39 @@
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
+ class Cluster
21
+ class Schema
22
+ # @private
23
+ module ReplicationStrategies
24
+ # @private
25
+ class None
26
+ def replication_map(token_hosts, token_ring, replication_options)
27
+ replication_map = ::Hash.new
28
+
29
+ token_hosts.each do |token, host|
30
+ replication_map[token] = [host].freeze
31
+ end
32
+
33
+ replication_map
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,44 @@
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
+ class Cluster
21
+ class Schema
22
+ # @private
23
+ module ReplicationStrategies
24
+ # @private
25
+ class Simple
26
+ def replication_map(token_hosts, token_ring, replication_options)
27
+ factor = Integer(replication_options['replication_factor'])
28
+ size = token_ring.size
29
+ factor = size if size < factor
30
+ replication_map = ::Hash.new
31
+
32
+ token_ring.each_with_index do |token, i|
33
+ replication_map[token] = factor.times.map do |j|
34
+ token_hosts[token_ring[(i + j) % size]]
35
+ end.freeze
36
+ end
37
+
38
+ replication_map
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -45,7 +45,7 @@ module Cassandra
45
45
  "org.apache.cassandra.db.marshal.MapType" => :map,
46
46
  "org.apache.cassandra.db.marshal.SetType" => :set,
47
47
  "org.apache.cassandra.db.marshal.ListType" => :list
48
- }
48
+ }.freeze
49
49
 
50
50
  def parse(string)
51
51
  create_result(parse_node(string))
@@ -18,7 +18,7 @@
18
18
 
19
19
  module Cassandra
20
20
  module Compression
21
- # @note Compressors given to {Cassandra.connect} as the `:compressor`
21
+ # @abstract Compressors given to {Cassandra.connect} as the `:compressor`
22
22
  # option don't need to be subclasses of this class, but need to implement
23
23
  # the same methods. This class exists only for documentation purposes.
24
24
  class Compressor
@@ -27,15 +27,38 @@ module Cassandra
27
27
  let(:io_reactor) { Io::IoReactor.new }
28
28
  let(:cluster_registry) { Cluster::Registry.new(logger) }
29
29
  let(:cluster_schema) { Cluster::Schema.new(schema_type_parser) }
30
+ let(:cluster_metadata) { Cluster::Metadata.new(
31
+ cluster_registry,
32
+ cluster_schema,
33
+ {
34
+ 'org.apache.cassandra.dht.Murmur3Partitioner' => murmur3_partitioner,
35
+ 'org.apache.cassandra.dht.ByteOrderedPartitioner' => ordered_partitioner,
36
+ 'org.apache.cassandra.dht.RandomPartitioner' => random_partitioner
37
+ }.freeze,
38
+ {
39
+ 'org.apache.cassandra.locator.SimpleStrategy' => simple_replication_strategy,
40
+ 'org.apache.cassandra.locator.NetworkTopologyStrategy' => network_topology_replication_strategy
41
+ }.freeze,
42
+ no_replication_strategy
43
+ )
44
+ }
30
45
  let(:futures_factory) { Future }
31
46
 
32
47
  let(:schema_type_parser) { Cluster::Schema::TypeParser.new }
33
48
 
49
+ let(:simple_replication_strategy) { Cluster::Schema::ReplicationStrategies::Simple.new }
50
+ let(:network_topology_replication_strategy) { Cluster::Schema::ReplicationStrategies::NetworkTopology.new }
51
+ let(:no_replication_strategy) { Cluster::Schema::ReplicationStrategies::None.new }
52
+
53
+ let(:murmur3_partitioner) { Cluster::Schema::Partitioners::Murmur3.new }
54
+ let(:ordered_partitioner) { Cluster::Schema::Partitioners::Ordered.new }
55
+ let(:random_partitioner) { Cluster::Schema::Partitioners::Random.new }
56
+
34
57
  let(:connector) { Cluster::Connector.new(logger, io_reactor, cluster_registry, connection_options) }
35
58
 
36
- let(:control_connection) { Cluster::ControlConnection.new(logger, io_reactor, cluster_registry, cluster_schema, load_balancing_policy, reconnection_policy, connector) }
59
+ let(:control_connection) { Cluster::ControlConnection.new(logger, io_reactor, cluster_registry, cluster_schema, cluster_metadata, load_balancing_policy, reconnection_policy, connector) }
37
60
 
38
- let(:cluster) { Cluster.new(logger, io_reactor, control_connection, cluster_registry, cluster_schema, execution_options, connection_options, load_balancing_policy, reconnection_policy, retry_policy, connector, futures_factory) }
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, connector, futures_factory) }
39
62
 
40
63
  let(:execution_options) do
41
64
  Execution::Options.new({
@@ -45,11 +68,12 @@ module Cassandra
45
68
  })
46
69
  end
47
70
 
48
- let(:connection_options) { Cluster::Options.new(protocol_version, credentials, auth_provider, compressor, port, connection_timeout, connections_per_local_node, connections_per_remote_node) }
71
+ let(:connection_options) { Cluster::Options.new(protocol_version, credentials, auth_provider, compressor, port, connect_timeout, ssl, connections_per_local_node, connections_per_remote_node) }
49
72
 
50
73
  let(:port) { 9042 }
51
74
  let(:protocol_version) { 2 }
52
- let(:connection_timeout) { 10 }
75
+ let(:connect_timeout) { 10 }
76
+ let(:ssl) { false }
53
77
  let(:logger) { Client::NullLogger.new }
54
78
  let(:compressor) { nil }
55
79
  let(:credentials) { nil }
@@ -72,6 +96,7 @@ module Cassandra
72
96
  end
73
97
 
74
98
  def connect(addresses)
99
+ load_balancing_policy.setup(cluster)
75
100
  cluster_registry.add_listener(load_balancing_policy)
76
101
  cluster_registry.add_listener(control_connection)
77
102
  listeners.each do |listener|
@@ -45,6 +45,9 @@ module Cassandra
45
45
  page_size = page_size && Integer(page_size)
46
46
  timeout = timeout && Integer(timeout)
47
47
 
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
50
+
48
51
  @consistency = consistency
49
52
  @page_size = page_size
50
53
  @trace = !!trace
@@ -20,28 +20,32 @@ module Cassandra
20
20
  class Host
21
21
  # @return [IPAddr] host ip
22
22
  attr_reader :ip
23
- # @note Host id can be `nil` before cluster connected.
23
+ # @note Host id can be `nil` before cluster has connected.
24
24
  # @return [Cassandra::Uuid, nil] host id.
25
25
  attr_reader :id
26
- # @note Host datacenter can be `nil` before cluster connected.
26
+ # @note Host datacenter can be `nil` before cluster has connected.
27
27
  # @return [String, nil] host datacenter
28
28
  attr_reader :datacenter
29
- # @note Host rack can be `nil` before cluster connected.
29
+ # @note Host rack can be `nil` before cluster has connected.
30
30
  # @return [String, nil] host rack
31
31
  attr_reader :rack
32
- # @note Host's cassandra version can be `nil` before cluster connected.
32
+ # @note Host's cassandra version can be `nil` before cluster has connected.
33
33
  # @return [String, nil] version of cassandra that a host is running
34
34
  attr_reader :release_version
35
+ # @note Host tokens will be empty before cluster has connected.
36
+ # @return [Array<String>] a list of tokens owned by this host
37
+ attr_reader :tokens
35
38
  # @return [Symbol] host status. Must be `:up` or `:down`
36
39
  attr_reader :status
37
40
 
38
41
  # @private
39
- def initialize(ip, id = nil, rack = nil, datacenter = nil, release_version = nil, status = :up)
42
+ def initialize(ip, id = nil, rack = nil, datacenter = nil, release_version = nil, tokens = EMPTY_LIST, status = :up)
40
43
  @ip = ip
41
44
  @id = id
42
45
  @rack = rack
43
46
  @datacenter = datacenter
44
47
  @release_version = release_version
48
+ @tokens = tokens
45
49
  @status = status
46
50
  end
47
51
 
@@ -78,11 +78,16 @@ module Cassandra
78
78
  # Yield or enumerate each table defined in this keyspace
79
79
  # @overload each_table
80
80
  # @yieldparam table [Cassandra::Table] current table
81
- # @return [Array<Cassandra::Table>] a list of tables
81
+ # @return [Cassandra::Keyspace] self
82
82
  # @overload each_table
83
- # @return [Enumerator<Cassandra::Table>] an enumerator
83
+ # @return [Array<Cassandra::Table>] a list of tables
84
84
  def each_table(&block)
85
- @tables.values.each(&block)
85
+ if block_given?
86
+ @tables.each_value(&block)
87
+ self
88
+ else
89
+ @tables.values
90
+ end
86
91
  end
87
92
  alias :tables :each_table
88
93
 
@@ -103,7 +108,15 @@ module Cassandra
103
108
 
104
109
  # @private
105
110
  def update_table(table)
106
- Keyspace.new(@name, @durable_writes, @replication, @tables.merge(table.name => table))
111
+ tables = @tables.dup
112
+ tables[table.name] = table
113
+ Keyspace.new(@name, @durable_writes, @replication, tables)
114
+ end
115
+
116
+ # @private
117
+ def create_partition_key(table, values)
118
+ table = @tables[table]
119
+ table && table.create_partition_key(values)
107
120
  end
108
121
 
109
122
  # @private
@@ -19,9 +19,9 @@
19
19
  module Cassandra
20
20
  # Cassandra state listener.
21
21
  #
22
- # @note Actual state listener implementations don't need to inherit from this
23
- # class as long as they conform to its interface. This class exists solely
24
- # for documentation purposes
22
+ # @abstract Actual state listener implementations don't need to inherit from
23
+ # this class as long as they conform to its interface. This class exists
24
+ # solely for documentation purposes
25
25
  #
26
26
  # @see Cassandra::Cluster#register
27
27
  class Listener
@@ -22,34 +22,36 @@ module Cassandra
22
22
  # {Cassandra::LoadBalancing::Policy#distance} must return
23
23
  DISTANCES = [:ignore, :local, :remote].freeze
24
24
 
25
- # @note Actual load balancing policies don't need to extend this class,
25
+ # @abstract Actual load balancing policies don't need to extend this class,
26
26
  # only implement its methods. This class exists for documentation
27
- # purposes only
27
+ # purposes only.
28
28
  class Policy
29
- # @abstract implementation should be provided by an actual policy
29
+ # Allows policy to initialize with the cluster instance. This method is
30
+ # called once before connecting to the cluster.
31
+ # @param cluster [Cassandra::Cluster] current cluster instance
32
+ # @return [void]
33
+ def setup(cluster)
34
+ end
35
+
30
36
  # @see Cassandra::Listener#host_up
31
37
  def host_up(host)
32
38
  end
33
39
 
34
- # @abstract implementation should be provided by an actual policy
35
40
  # @see Cassandra::Listener#host_down
36
41
  def host_down(host)
37
42
  end
38
43
 
39
- # @abstract implementation should be provided by an actual policy
40
44
  # @see Cassandra::Listener#host_found
41
45
  def host_found(host)
42
46
  end
43
47
 
44
- # @abstract implementation should be provided by an actual policy
45
48
  # @see Cassandra::Listener#host_lost
46
49
  def host_lost(host)
47
50
  end
48
51
 
49
- # Returns a distance that lets the driver to determine host many
50
- # connections (if any) to open to the host
52
+ # Returns a distance that determines how many connections (if any) the
53
+ # driver will open to the host.
51
54
  #
52
- # @abstract implementation should be provided by an actual policy
53
55
  # @param host [Cassandra::Host] a host instance
54
56
  # @return [Symbol] distance to host. Must be one of
55
57
  # {Cassandra::LoadBalancing::DISTANCES}
@@ -62,7 +64,6 @@ module Cassandra
62
64
  #
63
65
  # @note Hosts that should be ignored, must not be included in the Plan
64
66
  #
65
- # @abstract implementation should be provided by an actual policy
66
67
  # @param keyspace [String] current keyspace of the {Cassandra::Session}
67
68
  # @param statement [Cassandra::Statement] actual statement to be executed
68
69
  # @param options [Cassandra::Execution::Options] execution options to be used
@@ -80,7 +81,7 @@ module Cassandra
80
81
 
81
82
  # A load balancing plan is used to determine the order of hosts for running
82
83
  # queries, preparing statements and establishing connections.
83
- # @note Plans returned by {Cassandra::LoadBalancing::Policy}
84
+ # @abstract Plans returned by {Cassandra::LoadBalancing::Policy#plan}
84
85
  # implementations don't need to extend this class, only implement its
85
86
  # methods. This class exists for documentation purposes only.
86
87
  class Plan
@@ -13,6 +13,7 @@
13
13
  # limitations under the License.
14
14
  #++
15
15
 
16
- require 'cassandra/load_balancing/policies/round_robin'
17
16
  require 'cassandra/load_balancing/policies/dc_aware_round_robin'
17
+ require 'cassandra/load_balancing/policies/round_robin'
18
+ require 'cassandra/load_balancing/policies/token_aware'
18
19
  require 'cassandra/load_balancing/policies/white_list'
@@ -124,7 +124,7 @@ module Cassandra
124
124
  if LOCAL_CONSISTENCIES.include?(options.consistency) && !@use_remote
125
125
  remote = EMPTY_ARRAY
126
126
  else
127
- remote = @remote
127
+ remote = @remote
128
128
  end
129
129
 
130
130
  position = @position
@@ -52,12 +52,22 @@ module Cassandra
52
52
  mon_initialize
53
53
  end
54
54
 
55
+ # Adds this host to rotation
56
+ #
57
+ # @param host [Cassandra::Host] a host instance
58
+ # @return [Cassandra::LoadBalancing::Policies::RoundRobin] self
59
+ # @see Cassandra::Listener#host_up
55
60
  def host_up(host)
56
61
  synchronize { @hosts = @hosts.dup.push(host) }
57
62
 
58
63
  self
59
64
  end
60
65
 
66
+ # Removes this host from rotation
67
+ #
68
+ # @param host [Cassandra::Host] a host instance
69
+ # @return [Cassandra::LoadBalancing::Policies::RoundRobin] self
70
+ # @see Cassandra::Listener#host_down
61
71
  def host_down(host)
62
72
  synchronize do
63
73
  @hosts = @hosts.dup
@@ -67,18 +77,45 @@ module Cassandra
67
77
  self
68
78
  end
69
79
 
80
+ # Noop
81
+ #
82
+ # @param host [Cassandra::Host] a host instance
83
+ # @return [Cassandra::LoadBalancing::Policies::RoundRobin] self
84
+ # @see Cassandra::Listener#host_found
70
85
  def host_found(host)
71
86
  self
72
87
  end
73
88
 
89
+ # Noop
90
+ #
91
+ # @param host [Cassandra::Host] a host instance
92
+ # @return [Cassandra::LoadBalancing::Policies::RoundRobin] self
93
+ # @see Cassandra::Listener#host_lost
74
94
  def host_lost(host)
75
95
  self
76
96
  end
77
97
 
98
+ # Returns distance to host. All hosts in rotation are considered
99
+ # `:local`, all other hosts - `:ignore`.
100
+ #
101
+ # @param host [Cassandra::Host] a host instance
102
+ # @return [Symbol] `:local` for all hosts in rotation and `:ignore` for
103
+ # all other hosts.
104
+ # @see Cassandra::LoadBalancing::Policy#distance
78
105
  def distance(host)
79
106
  @hosts.include?(host) ? :local : :ignore
80
107
  end
81
108
 
109
+ # Returns a load balancing plan that rotates hosts by 1 each time a
110
+ # plan is requested.
111
+ #
112
+ # @param keyspace [String] current keyspace of the {Cassandra::Session}
113
+ # @param statement [Cassandra::Statement] actual statement to be
114
+ # executed
115
+ # @param options [Cassandra::Execution::Options] execution options to
116
+ # be used
117
+ # @return [Cassandra::LoadBalancing::Plan] a rotated load balancing plan
118
+ # @see Cassandra::LoadBalancing::Policy#plan
82
119
  def plan(keyspace, statement, options)
83
120
  hosts = @hosts
84
121
  position = @position
@@ -0,0 +1,119 @@
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
+ module LoadBalancing
21
+ module Policies
22
+ class TokenAware < Policy
23
+ # @private
24
+ class Plan
25
+ def initialize(hosts, policy, keyspace, statement, options)
26
+ @hosts = hosts
27
+ @policy = policy
28
+ @keyspace = keyspace
29
+ @statement = statement
30
+ @options = options
31
+ @seen = ::Hash.new
32
+ end
33
+
34
+ def has_next?
35
+ until @hosts.empty?
36
+ host = @hosts.shift
37
+
38
+ if @policy.distance(host) == :local
39
+ @seen[host] = true
40
+ @next = host
41
+ break
42
+ end
43
+ end
44
+
45
+ return true if @next
46
+
47
+ @plan ||= @policy.plan(@keyspace, @statement, @options)
48
+
49
+ while @plan.has_next?
50
+ host = @plan.next
51
+
52
+ unless @seen[host]
53
+ @next = host
54
+ return true
55
+ end
56
+ end
57
+
58
+ false
59
+ end
60
+
61
+ def next
62
+ host = @next
63
+ @next = nil
64
+ host
65
+ end
66
+ end
67
+
68
+ extend Forwardable
69
+
70
+ # @!method distance(host)
71
+ # Delegates to wrapped policy
72
+ # @see Cassandra::LoadBalancing::Policy#distance
73
+ #
74
+ # @!method host_found(host)
75
+ # Delegates to wrapped policy
76
+ # @see Cassandra::LoadBalancing::Policy#host_found
77
+ #
78
+ # @!method host_up(host)
79
+ # Delegates to wrapped policy
80
+ # @see Cassandra::LoadBalancing::Policy#host_up
81
+ #
82
+ # @!method host_down(host)
83
+ # Delegates to wrapped policy
84
+ # @see Cassandra::LoadBalancing::Policy#host_down
85
+ #
86
+ # @!method host_lost(host)
87
+ # Delegates to wrapped policy
88
+ # @see Cassandra::LoadBalancing::Policy#host_lost
89
+ def_delegators :@policy, :distance, :host_found, :host_up, :host_down, :host_lost
90
+
91
+ # @param wrapped_policy [Cassandra::LoadBalancing::Policy] actual policy to filter
92
+ def initialize(wrapped_policy)
93
+ methods = [:host_up, :host_down, :host_found, :host_lost, :distance, :plan]
94
+
95
+ unless methods.all? {|method| wrapped_policy.respond_to?(method)}
96
+ raise ::ArgumentError, "supplied policy must be a Cassandra::LoadBalancing::Policy, #{wrapped_policy.inspect} given"
97
+ end
98
+
99
+ @policy = wrapped_policy
100
+ end
101
+
102
+ def setup(cluster)
103
+ @cluster = cluster
104
+ @policy.setup(cluster)
105
+ nil
106
+ end
107
+
108
+ def plan(keyspace, statement, options)
109
+ return @policy.plan(keyspace, statement, options) unless @cluster
110
+
111
+ replicas = @cluster.find_replicas(keyspace, statement)
112
+ return @policy.plan(keyspace, statement, options) if replicas.empty?
113
+
114
+ Plan.new(replicas.dup, @policy, keyspace, statement, options)
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end