datastax_rails 1.0.11 → 1.0.12
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.
- data/README.rdoc +7 -2
- data/lib/blankslate.rb +106 -0
- data/lib/datastax_rails/base.rb +8 -2
- data/lib/datastax_rails/connection.rb +78 -0
- data/lib/datastax_rails/railtie.rb +3 -3
- data/lib/datastax_rails/relation.rb +3 -2
- data/lib/datastax_rails/rsolr_client_wrapper.rb +27 -0
- data/lib/datastax_rails/tasks/column_family.rb +0 -6
- data/lib/datastax_rails/version.rb +1 -1
- data/lib/datastax_rails.rb +3 -0
- data/lib/schema_migration.rb +8 -0
- data/spec/datastax_rails/cql/update_spec.rb +1 -1
- data/spec/dummy/config/datastax.yml +8 -4
- data/spec/dummy/log/test.log +904 -0
- metadata +7 -4
data/README.rdoc
CHANGED
@@ -36,10 +36,12 @@ Configure the config/datastax.yml file:
|
|
36
36
|
connection_options:
|
37
37
|
timeout: 2
|
38
38
|
solr:
|
39
|
-
|
39
|
+
port: 8983
|
40
|
+
path: /solr
|
40
41
|
|
41
42
|
The above is configured to use NetworkTopologyStrategy. If you go with this, you'll need to configure Datastax to use the
|
42
43
|
NetworkTopologySnitch and set up the cassandra-topology.properties file. See the Datastax documentation for more information.
|
44
|
+
|
43
45
|
For a more simple, single datacenter setup, something like this should probably work:
|
44
46
|
|
45
47
|
development:
|
@@ -50,7 +52,10 @@ For a more simple, single datacenter setup, something like this should probably
|
|
50
52
|
connection_options:
|
51
53
|
timeout: 2
|
52
54
|
solr:
|
53
|
-
|
55
|
+
port: 8983
|
56
|
+
path: /solr
|
57
|
+
|
58
|
+
See DatastaxRails::Connection::ClassMethods for a description of what options are available.
|
54
59
|
|
55
60
|
Create your keyspace:
|
56
61
|
|
data/lib/blankslate.rb
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#--
|
3
|
+
# Copyright 2004, 2006 by Jim Weirich (jim@weirichhouse.org).
|
4
|
+
# All rights reserved.
|
5
|
+
|
6
|
+
# Permission is granted for use, copying, modification, distribution,
|
7
|
+
# and distribution of modified versions of this work as long as the
|
8
|
+
# above copyright notice is included.
|
9
|
+
#++
|
10
|
+
|
11
|
+
######################################################################
|
12
|
+
# BlankSlate provides an abstract base class with no predefined
|
13
|
+
# methods (except for <tt>\_\_send__</tt> and <tt>\_\_id__</tt>).
|
14
|
+
# BlankSlate is useful as a base class when writing classes that
|
15
|
+
# depend upon <tt>method_missing</tt> (e.g. dynamic proxies).
|
16
|
+
class BlankSlate
|
17
|
+
class << self
|
18
|
+
|
19
|
+
# Hide the method named +name+ in the BlankSlate class. Don't
|
20
|
+
# hide +instance_eval+ or any method beginning with "__".
|
21
|
+
def hide(name)
|
22
|
+
methods = instance_methods.map(&:to_sym)
|
23
|
+
if methods.include?(name.to_sym) and
|
24
|
+
name !~ /^(__|instance_eval)/
|
25
|
+
@hidden_methods ||= {}
|
26
|
+
@hidden_methods[name.to_sym] = instance_method(name)
|
27
|
+
undef_method name
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def find_hidden_method(name)
|
32
|
+
@hidden_methods ||= {}
|
33
|
+
@hidden_methods[name] || superclass.find_hidden_method(name)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Redefine a previously hidden method so that it may be called on a blank
|
37
|
+
# slate object.
|
38
|
+
def reveal(name)
|
39
|
+
hidden_method = find_hidden_method(name)
|
40
|
+
fail "Don't know how to reveal method '#{name}'" unless hidden_method
|
41
|
+
define_method(name, hidden_method)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
instance_methods.each { |m| hide(m) }
|
46
|
+
end
|
47
|
+
|
48
|
+
######################################################################
|
49
|
+
# Since Ruby is very dynamic, methods added to the ancestors of
|
50
|
+
# BlankSlate <em>after BlankSlate is defined</em> will show up in the
|
51
|
+
# list of available BlankSlate methods. We handle this by defining a
|
52
|
+
# hook in the Object and Kernel classes that will hide any method
|
53
|
+
# defined after BlankSlate has been loaded.
|
54
|
+
module Kernel
|
55
|
+
class << self
|
56
|
+
alias_method :blank_slate_method_added, :method_added
|
57
|
+
|
58
|
+
# Detect method additions to Kernel and remove them in the
|
59
|
+
# BlankSlate class.
|
60
|
+
def method_added(name)
|
61
|
+
result = blank_slate_method_added(name)
|
62
|
+
return result if self != Kernel
|
63
|
+
BlankSlate.hide(name)
|
64
|
+
result
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
######################################################################
|
70
|
+
# Same as above, except in Object.
|
71
|
+
class Object
|
72
|
+
class << self
|
73
|
+
alias_method :blank_slate_method_added, :method_added
|
74
|
+
|
75
|
+
# Detect method additions to Object and remove them in the
|
76
|
+
# BlankSlate class.
|
77
|
+
def method_added(name)
|
78
|
+
result = blank_slate_method_added(name)
|
79
|
+
return result if self != Object
|
80
|
+
BlankSlate.hide(name)
|
81
|
+
result
|
82
|
+
end
|
83
|
+
|
84
|
+
def find_hidden_method(name)
|
85
|
+
nil
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
######################################################################
|
91
|
+
# Also, modules included into Object need to be scanned and have their
|
92
|
+
# instance methods removed from blank slate. In theory, modules
|
93
|
+
# included into Kernel would have to be removed as well, but a
|
94
|
+
# "feature" of Ruby prevents late includes into modules from being
|
95
|
+
# exposed in the first place.
|
96
|
+
class Module
|
97
|
+
alias blankslate_original_append_features append_features
|
98
|
+
def append_features(mod)
|
99
|
+
result = blankslate_original_append_features(mod)
|
100
|
+
return result if mod != Object
|
101
|
+
instance_methods.each do |name|
|
102
|
+
BlankSlate.hide(name)
|
103
|
+
end
|
104
|
+
result
|
105
|
+
end
|
106
|
+
end
|
data/lib/datastax_rails/base.rb
CHANGED
@@ -445,20 +445,26 @@ module DatastaxRails #:nodoc:
|
|
445
445
|
class << self
|
446
446
|
delegate :find, :first, :all, :exists?, :any?, :many?, :to => :scoped
|
447
447
|
delegate :destroy, :destroy_all, :delete, :update, :update_all, :to => :scoped
|
448
|
-
# delegate :find_each, :find_in_batches, :to => :scoped
|
449
448
|
delegate :order, :limit, :where, :where_not, :page, :paginate, :select, :to => :scoped
|
450
449
|
delegate :per_page, :each, :group, :total_pages, :search, :fulltext, :to => :scoped
|
451
450
|
delegate :count, :first, :first!, :last, :last!, :to => :scoped
|
452
451
|
delegate :cql, :with_cassandra, :with_solr, :commit_solr, :to => :scoped
|
453
452
|
|
453
|
+
# Sets the column family name
|
454
|
+
#
|
455
|
+
# @param [String] column_family the name of the column family in cassandra
|
454
456
|
def column_family=(column_family)
|
455
457
|
@column_family = column_family
|
456
458
|
end
|
457
459
|
|
460
|
+
# Returns the column family name. If it has been set manually, the set name is returned.
|
461
|
+
# Otherwise returns the pluralized version of the class name.
|
462
|
+
#
|
463
|
+
# Returns [String] the name of the column family
|
458
464
|
def column_family
|
459
465
|
@column_family || name.pluralize
|
460
466
|
end
|
461
|
-
|
467
|
+
|
462
468
|
def base_class
|
463
469
|
klass = self
|
464
470
|
while klass.superclass != Base
|
@@ -1,9 +1,13 @@
|
|
1
|
+
# require 'datastax_rails/rsolr_client_wrapper'
|
1
2
|
module DatastaxRails
|
3
|
+
# The connection module holds all the code for establishing and maintaining a connection to
|
4
|
+
# Datastax Exterprise. This includes both the Cassandra and Solr connections.
|
2
5
|
module Connection
|
3
6
|
extend ActiveSupport::Concern
|
4
7
|
|
5
8
|
included do
|
6
9
|
class_attribute :connection
|
10
|
+
class_attribute :solr
|
7
11
|
end
|
8
12
|
|
9
13
|
module ClassMethods
|
@@ -11,12 +15,86 @@ module DatastaxRails
|
|
11
15
|
:servers => "127.0.0.1:9160",
|
12
16
|
:thrift => {}
|
13
17
|
}
|
18
|
+
|
19
|
+
# Returns the current server that we are talking to. This is useful when you are talking to a
|
20
|
+
# cluster, and we want to know which server specifically we are connected to.
|
21
|
+
#
|
22
|
+
# Used by Relation to calculate the SOLR URL so that it follows the Cassandra connection.
|
23
|
+
def current_server
|
24
|
+
thrift_client.instance_variable_get(:@current_server).to_s.split(/\:/).first
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns the thrift client object
|
28
|
+
def thrift_client
|
29
|
+
self.connection.instance_variable_get(:@connection)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Establish a Cassandra connection to DSE. datastax.yml will be read and the current environment's
|
33
|
+
# settings passed to this method.
|
34
|
+
#
|
35
|
+
# The following is an example production configuration document. Assume that your setup consists
|
36
|
+
# of three datacenters each with three servers and RF=3 (i.e., you're storing your data 9 times)
|
37
|
+
#
|
38
|
+
# servers: ["10.1.2.5:9160", "10.1.2.6:9160", "10.1.2.7:9160"]
|
39
|
+
# keyspace: "datastax_rails_production"
|
40
|
+
# strategy_class: "org.apache.cassandra.locator.NetworkTopologyStrategy"
|
41
|
+
# strategy_options: {"DS1": "3", "DS2": "3", "DS3": "3"}
|
42
|
+
# connection_options:
|
43
|
+
# timeout: 10
|
44
|
+
# retries: 2
|
45
|
+
# server_max_requests: 1000
|
46
|
+
# solr:
|
47
|
+
# port: 8983
|
48
|
+
# path: /solr
|
49
|
+
#
|
50
|
+
# The +servers+ entry should be a list of all of the servers in your local datacenter. These
|
51
|
+
# are the servers that DSR will attempt to connect to and will round-robin through.
|
52
|
+
#
|
53
|
+
# Since we're using the NetworkTopologyStrategy for our locator, it is important that you configure
|
54
|
+
# cassandra-topology.properties. See the DSE documentation at http://www.datastax.com for more
|
55
|
+
# information.
|
56
|
+
#
|
57
|
+
# strategy_options lets us specify what our topology looks like. In this case, we have RF=3 in all
|
58
|
+
# three of our datacenters (DS1, DS2, and DS3).
|
59
|
+
#
|
60
|
+
# connection_options are the options that are passed to the thrift layer for the connection to
|
61
|
+
# cassandra.
|
62
|
+
# * *retries* - Number of times a request will be retried. Should likely be the number of servers - 1. Defaults to 0.
|
63
|
+
# * *server_retry_period* - Amount of time to wait before retrying a down server. Defaults to 1.
|
64
|
+
# * *server_max_requests* - Number of requests to make to a server before moving to the next one (helps keep load balanced). Default to nil which means cycling does not take place.
|
65
|
+
# * *retry_overrides* - Overrides retries option for individual exceptions.
|
66
|
+
# * *connect_timeout* - The connection timeout on the Thrift socket. Defaults to 0.1.
|
67
|
+
# * *timeout* - The timeout for the transport layer. Defaults to 1.
|
68
|
+
# * *timeout_overrides* - Overrides the timeout value for specific methods (advanced).
|
69
|
+
# * *exception_classes* - List of exceptions for which Thrift will automatically retry a new server in the cluster (up to retry limit).
|
70
|
+
# Defaults to [IOError, Thrift::Exception, Thrift::ApplicationException, Thrift::TransportException].
|
71
|
+
# * *exception_class_overrides* - List of exceptions which will never cause a retry. Defaults to [CassandraCQL::Thrift::InvalidRequestException].
|
72
|
+
# * *wrapped_exception_options* - List of exceptions that will be automatically wrapped in an exception provided by client class with the same name (advanced).
|
73
|
+
# Defaults to [Thrift::ApplicationException, Thrift::TransportException].
|
74
|
+
# * *raise* - Whether to raise exceptions or default calls that cause an error (advanced). Defaults to true (raise exceptions).
|
75
|
+
# * *defaults* - When raise is false and an error is encountered, these methods are called to default the return value (advanced). Should be a hash of method names to values.
|
76
|
+
# * *protocol* - The thrift protocol to use (advanced). Defaults to Thrift::BinaryProtocol.
|
77
|
+
# * *protocol_extra_params* - Any extra parameters to send to the protocol (advanced).
|
78
|
+
# * *transport* - The thrift transport to use (advanced). Defaults to Thrift::Socket.
|
79
|
+
# * *transport_wrapper* - The thrift transport wrapper to use (advanced). Defaults to Thrift::FramedTransport.
|
80
|
+
#
|
81
|
+
# See +solr_connection+ for a description of the solr options in datastax.yml
|
14
82
|
def establish_connection(spec)
|
15
83
|
DatastaxRails::Base.config = spec.with_indifferent_access
|
16
84
|
spec.reverse_merge!(DEFAULT_OPTIONS)
|
17
85
|
connection_options = spec[:connection_options] || {}
|
18
86
|
self.connection = CassandraCQL::Database.new(spec[:servers], {:keyspace => spec[:keyspace]}, connection_options.symbolize_keys)
|
19
87
|
end
|
88
|
+
|
89
|
+
# Similar to +establish_connection+, this method creates a connection object for Solr. Since HTTP is stateless, this doesn't
|
90
|
+
# actually launch the connection, but it gets everything set up so that RSolr can do its work. It's important to note that
|
91
|
+
# unlike the cassandra connection which is global to all of DSR, each model will have its own solr_connection.
|
92
|
+
def solr_connection
|
93
|
+
DatastaxRails::Base.establish_connection unless self.connection
|
94
|
+
port = DatastaxRails::Base.config[:solr][:port]
|
95
|
+
path = DatastaxRails::Base.config[:solr][:path]
|
96
|
+
@rsolr ||= DatastaxRails::RSolrClientWrapper.new(RSolr.connect :url => "http://#{self.current_server}:#{port}#{path}/#{DatastaxRails::Base.connection.keyspace}.#{self.column_family}")
|
97
|
+
end
|
20
98
|
end
|
21
99
|
end
|
22
100
|
end
|
@@ -13,8 +13,8 @@ module DatastaxRails
|
|
13
13
|
load 'datastax_rails/tasks/ds.rake'
|
14
14
|
end
|
15
15
|
|
16
|
-
generators do
|
17
|
-
require 'datastax_rails/generators/migration_generator'
|
18
|
-
end
|
16
|
+
# generators do
|
17
|
+
# require 'datastax_rails/generators/migration_generator'
|
18
|
+
# end
|
19
19
|
end
|
20
20
|
end
|
@@ -406,8 +406,9 @@ module DatastaxRails
|
|
406
406
|
end
|
407
407
|
end
|
408
408
|
|
409
|
-
|
410
|
-
|
409
|
+
# Calculates the solr URL and sets up an RSolr connection
|
410
|
+
def rsolr
|
411
|
+
@klass.solr_connection
|
411
412
|
end
|
412
413
|
end
|
413
414
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module DatastaxRails
|
2
|
+
# Wraps the RSolr Client class so that exceptions such as Connection Refused can be caught and
|
3
|
+
# a new server tried (if one is available)
|
4
|
+
class RSolrClientWrapper < BlankSlate
|
5
|
+
def initialize(rsolr)
|
6
|
+
@rsolr = rsolr
|
7
|
+
end
|
8
|
+
|
9
|
+
def method_missing(sym, *args, &block)
|
10
|
+
if @rsolr.uri.host != DatastaxRails::Base.current_server
|
11
|
+
@rsolr.uri.host = DatastaxRails::Base.current_server
|
12
|
+
@rsolr = RSolr.connect(:url => @rsolr.uri.to_s)
|
13
|
+
end
|
14
|
+
@rsolr.__send__(sym, *args, &block)
|
15
|
+
rescue Errno::ECONNREFUSED
|
16
|
+
tries ||= DatastaxRails::Base.thrift_client.options[:retries] + 1
|
17
|
+
tries -= 1
|
18
|
+
if tries > 0
|
19
|
+
# Force cassandra connection to roll
|
20
|
+
DatastaxRails::Cql::Select.new(SchemaMigration, ['id']).limit(1).execute
|
21
|
+
retry
|
22
|
+
else
|
23
|
+
raise
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -2,12 +2,6 @@ require 'digest/sha1'
|
|
2
2
|
|
3
3
|
module DatastaxRails
|
4
4
|
module Tasks
|
5
|
-
class SchemaMigration
|
6
|
-
def self.column_family
|
7
|
-
'schema_migrations'
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
5
|
class ColumnFamily
|
12
6
|
COMPARATOR_TYPES = [:blob, :ascii, :text, :varint, :bigint, :uuid, :timestamp, :boolean, :float, :doublt, :decimal]
|
13
7
|
|
data/lib/datastax_rails.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
require 'active_support/all'
|
2
2
|
require 'cassandra-cql/1.0'
|
3
|
+
require 'blankslate'
|
4
|
+
require 'schema_migration'
|
3
5
|
|
4
6
|
# Welcome to DatastaxRails. DatastaxRails::Base is probably a good place to start.
|
5
7
|
module DatastaxRails
|
@@ -27,6 +29,7 @@ module DatastaxRails
|
|
27
29
|
autoload :SpawnMethods
|
28
30
|
end
|
29
31
|
|
32
|
+
autoload :RSolrClientWrapper, 'datastax_rails/rsolr_client_wrapper'
|
30
33
|
autoload :Schema
|
31
34
|
autoload :Scoping
|
32
35
|
autoload :Serialization
|
@@ -8,7 +8,7 @@ describe DatastaxRails::Cql::Update do
|
|
8
8
|
it "should generate valid CQL" do
|
9
9
|
cql = DatastaxRails::Cql::Update.new(@model_class, "12345")
|
10
10
|
cql.using(DatastaxRails::Cql::Consistency::QUORUM).columns(:name => 'John', :age => '23')
|
11
|
-
cql.to_cql.should == "update users using consistency QUORUM SET
|
11
|
+
cql.to_cql.should == "update users using consistency QUORUM SET name = 'John', age = '23' WHERE KEY IN ('12345')"
|
12
12
|
end
|
13
13
|
|
14
14
|
it_has_behavior "default_consistency"
|
@@ -3,16 +3,20 @@ development:
|
|
3
3
|
keyspace: "datastax_rails_development"
|
4
4
|
strategy_class: "org.apache.cassandra.locator.SimpleStrategy"
|
5
5
|
strategy_options: {"DC1": "1"}
|
6
|
-
|
6
|
+
connection_options:
|
7
|
+
timeout: 10
|
7
8
|
solr:
|
8
|
-
|
9
|
+
port: 8983
|
10
|
+
path: /solr
|
9
11
|
|
10
12
|
test:
|
11
13
|
servers: ["localhost:9160"]
|
12
14
|
keyspace: "datastax_rails_test"
|
13
15
|
strategy_class: "org.apache.cassandra.locator.SimpleStrategy"
|
14
16
|
strategy_options: {"DC1": "1"}
|
15
|
-
|
17
|
+
connection_options:
|
18
|
+
timeout: 10
|
16
19
|
solr:
|
17
|
-
|
20
|
+
port: 8983
|
21
|
+
path: /solr
|
18
22
|
|