yam-db-charmer 1.7.01 → 1.7.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/CHANGES +8 -0
- data/README.rdoc +10 -6
- data/db-charmer.gemspec +2 -2
- data/lib/db_charmer/action_controller/force_slave_reads.rb +2 -2
- data/lib/db_charmer/active_record/connection_switching.rb +17 -0
- data/lib/db_charmer/active_record/db_magic.rb +9 -4
- data/lib/db_charmer/active_record/migration/multi_db_migrations.rb +113 -35
- data/lib/db_charmer/active_record/multi_db_proxy.rb +9 -0
- data/lib/db_charmer/connection_factory.rb +8 -0
- data/lib/db_charmer/rails3/active_record/relation_method.rb +6 -1
- data/lib/db_charmer/rails31/active_record/migration/command_recorder.rb +11 -0
- data/lib/db_charmer/rails31/active_record/preloader/association.rb +21 -0
- data/lib/db_charmer/rails31/active_record/preloader/has_and_belongs_to_many.rb +23 -0
- data/lib/db_charmer/sharding/connection.rb +4 -0
- data/lib/db_charmer/sharding/method/db_block_group_map.rb +12 -177
- data/lib/db_charmer/sharding/method/db_block_group_map_base.rb +270 -0
- data/lib/db_charmer/sharding/method/db_block_map.rb +4 -9
- data/lib/db_charmer/sharding/method/db_block_schema_map.rb +179 -0
- data/lib/db_charmer/sharding/method.rb +2 -0
- data/lib/db_charmer/sharding.rb +28 -0
- data/lib/db_charmer/version.rb +2 -2
- data/lib/db_charmer.rb +50 -22
- data/lib/tasks/databases.rake +30 -4
- data/lib/tasks/test.rake +115 -0
- metadata +19 -12
data/CHANGES
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
1.7.1 (2012-04-22):
|
2
|
+
|
3
|
+
Beta feature: Rails 3.1 and 3.2 support
|
4
|
+
|
5
|
+
Thanks to the community (and Eugene Pimenov aka @libc in particular) we now have support
|
6
|
+
for Rails versions up to 3.2.1, including new migrations classes.
|
7
|
+
|
8
|
+
----------------------------------------------------------------------------------------
|
1
9
|
1.7.0 (2011-08-29):
|
2
10
|
|
3
11
|
Beta feature: Rails 3 support
|
data/README.rdoc
CHANGED
@@ -16,7 +16,7 @@ For more information on the project, you can check out our web site at http://ko
|
|
16
16
|
== Installation
|
17
17
|
|
18
18
|
There are two options when approaching +DbCharmer+ installation:
|
19
|
-
* using the gem (recommended and the only way of using it with Rails 3.
|
19
|
+
* using the gem (recommended and the only way of using it with Rails 3.2+)
|
20
20
|
* install as a Rails plugin (works in Rails 2.x only)
|
21
21
|
|
22
22
|
To install as a gem, add this to your Gemfile:
|
@@ -276,7 +276,7 @@ This option could be used to disable automated slave reads on your models so tha
|
|
276
276
|
call <tt>on_slave</tt> or use other methods to enable slave reads when you need it. Example:
|
277
277
|
|
278
278
|
class User < ActiveRecord::Base
|
279
|
-
db_magic :slave => slave01, :
|
279
|
+
db_magic :slave => slave01, :force_slave_reads => false
|
280
280
|
end
|
281
281
|
|
282
282
|
2) <tt>force_slave_reads</tt> +ActionController+ class method. This method could be used to
|
@@ -582,17 +582,17 @@ the DbCharmer Users Group mailing list:
|
|
582
582
|
|
583
583
|
== What Ruby and Rails implementations does it work for?
|
584
584
|
|
585
|
-
We have a continuous integration setup for this gem on with Rails 2.
|
585
|
+
We have a continuous integration setup for this gem on with Rails 2.3, 3.0, 3.1 and 3.2 using a few
|
586
586
|
different versions of Ruby. For more information about the build matrix please visit
|
587
587
|
http://github.com/kovyrin/db-charmer-sandbox web site.
|
588
588
|
|
589
|
-
In addition to CI testing,
|
589
|
+
In addition to CI testing, this gem is used in production on Scribd.com (one of the largest RoR
|
590
590
|
sites in the world) with Ruby Enterprise Edition and Rails 2.2, Rails 2.3, Sinatra and plain
|
591
591
|
Rack applications.
|
592
592
|
|
593
|
-
Starting with version 1.7.0 we support Rails 3.0, but until further notice we consider it a
|
593
|
+
Starting with version 1.7.0 we support Rails 3.0-3.2, but until further notice we consider it a
|
594
594
|
beta-quality feature since we do not run any production software on this version of Rails. If you
|
595
|
-
run your application on Rails 3.
|
595
|
+
run your application on Rails 3.x and having any issues with the gem, please contact the author.
|
596
596
|
|
597
597
|
|
598
598
|
== Who are the authors?
|
@@ -605,8 +605,12 @@ Other contributors who have helped with the development of this library are (alp
|
|
605
605
|
* Allen Madsen
|
606
606
|
* Andrew Geweke
|
607
607
|
* Ashley Martens
|
608
|
+
* Cauê Guerra
|
609
|
+
* David Dai
|
608
610
|
* Dmytro Shteflyuk
|
609
611
|
* Eric Lindvall
|
612
|
+
* Eugene Pimenov
|
613
|
+
* Jonathan Viney
|
610
614
|
* Gregory Man
|
611
615
|
* Michael Birk
|
612
616
|
* Tyler McMullen
|
data/db-charmer.gemspec
CHANGED
@@ -20,8 +20,8 @@ Gem::Specification.new do |s|
|
|
20
20
|
s.extra_rdoc_files = [ 'LICENSE', 'README.rdoc' ]
|
21
21
|
|
22
22
|
# Dependencies
|
23
|
-
s.add_dependency 'activesupport', '
|
24
|
-
s.add_dependency 'activerecord', '
|
23
|
+
s.add_dependency 'activesupport', '~> 3.0'
|
24
|
+
s.add_dependency 'activerecord', '~> 3.0'
|
25
25
|
|
26
26
|
s.add_development_dependency 'rspec'
|
27
27
|
s.add_development_dependency 'yard'
|
@@ -4,7 +4,7 @@ module DbCharmer
|
|
4
4
|
|
5
5
|
module ClassMethods
|
6
6
|
@@db_charmer_force_slave_reads_actions = {}
|
7
|
-
def force_slave_reads(params)
|
7
|
+
def force_slave_reads(params = {})
|
8
8
|
@@db_charmer_force_slave_reads_actions[self.name] = {
|
9
9
|
:except => params[:except] ? [*params[:except]].map(&:to_s) : [],
|
10
10
|
:only => params[:only] ? [*params[:only]].map(&:to_s) : []
|
@@ -55,7 +55,7 @@ module DbCharmer
|
|
55
55
|
|
56
56
|
protected
|
57
57
|
|
58
|
-
class_eval <<-EOF
|
58
|
+
class_eval <<-EOF, __FILE__, __LINE__+1
|
59
59
|
def #{DISPATCH_METHOD}_with_forced_slave_reads(*args, &block)
|
60
60
|
DbCharmer.with_controller(self) do
|
61
61
|
#{DISPATCH_METHOD}_without_forced_slave_reads(*args, &block)
|
@@ -1,6 +1,7 @@
|
|
1
1
|
module DbCharmer
|
2
2
|
module ActiveRecord
|
3
3
|
module ConnectionSwitching
|
4
|
+
|
4
5
|
def establish_real_connection_if_exists(name, should_exist = false)
|
5
6
|
name = name.to_s
|
6
7
|
|
@@ -34,11 +35,22 @@ module DbCharmer
|
|
34
35
|
db_charmer_remapped_connection || db_charmer_connection_proxy || connection_without_magic
|
35
36
|
end
|
36
37
|
alias_method_chain :connection, :magic
|
38
|
+
|
39
|
+
def connection_pool_with_magic
|
40
|
+
abstract_connection_class = connection.abstract_connection_class rescue nil # respond_to? doesn't work on connection_proxy...
|
41
|
+
if abstract_connection_class
|
42
|
+
connection_handler.retrieve_connection_pool(abstract_connection_class) || connection_pool_without_magic
|
43
|
+
else
|
44
|
+
connection_pool_without_magic
|
45
|
+
end
|
46
|
+
end
|
47
|
+
alias_method_chain :connection_pool, :magic
|
37
48
|
end
|
38
49
|
end
|
39
50
|
|
40
51
|
#-----------------------------------------------------------------------------------------------------------------
|
41
52
|
def coerce_to_connection_proxy(conn, should_exist = true)
|
53
|
+
#Rails.logger.debug "coerce_to_connection_proxy: conn class=#{conn.class}"
|
42
54
|
return nil if conn.nil?
|
43
55
|
|
44
56
|
if conn.kind_of?(Symbol) || conn.kind_of?(String)
|
@@ -70,11 +82,16 @@ module DbCharmer
|
|
70
82
|
db_charmer_connection_proxy.set_real_connection(new_conn)
|
71
83
|
end
|
72
84
|
|
85
|
+
# Set schema name as table_name_prefix
|
86
|
+
set_schema_table_name_prefix(conn) if self.respond_to? :set_schema_table_name_prefix
|
87
|
+
|
88
|
+
|
73
89
|
self.db_charmer_connection_proxy = new_conn
|
74
90
|
self.hijack_connection!
|
75
91
|
|
76
92
|
# self.reset_column_information
|
77
93
|
end
|
94
|
+
|
78
95
|
|
79
96
|
end
|
80
97
|
end
|
@@ -38,9 +38,15 @@ module DbCharmer
|
|
38
38
|
def setup_children_magic(opt)
|
39
39
|
self.db_charmer_opts = opt.clone
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
41
|
+
unless self.respond_to?(:inherited_with_db_magic)
|
42
|
+
class << self
|
43
|
+
def inherited_with_db_magic(child)
|
44
|
+
o = inherited_without_db_magic(child)
|
45
|
+
child.db_magic(self.db_charmer_opts)
|
46
|
+
o
|
47
|
+
end
|
48
|
+
alias_method_chain :inherited, :db_magic
|
49
|
+
end
|
44
50
|
end
|
45
51
|
end
|
46
52
|
|
@@ -82,4 +88,3 @@ module DbCharmer
|
|
82
88
|
end
|
83
89
|
end
|
84
90
|
end
|
85
|
-
|
@@ -3,23 +3,90 @@ module DbCharmer
|
|
3
3
|
module Migration
|
4
4
|
module MultiDbMigrations
|
5
5
|
|
6
|
-
def self.
|
7
|
-
|
8
|
-
|
6
|
+
def self.append_features(base)
|
7
|
+
return false if base < self
|
8
|
+
super
|
9
|
+
base.extend const_get("ClassMethods") if const_defined?("ClassMethods")
|
10
|
+
|
11
|
+
base.class_eval do
|
12
|
+
if DbCharmer.rails31?
|
13
|
+
alias_method_chain :migrate, :db_wrapper
|
14
|
+
else
|
15
|
+
class << self
|
16
|
+
alias_method_chain :migrate, :db_wrapper
|
17
|
+
end
|
18
|
+
end
|
9
19
|
end
|
10
20
|
end
|
11
21
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
22
|
+
module ClassMethods
|
23
|
+
@@multi_db_names = {}
|
24
|
+
def multi_db_names
|
25
|
+
@@multi_db_names[self.name] || @@multi_db_names['ActiveRecord::Migration']
|
26
|
+
end
|
27
|
+
|
28
|
+
def multi_db_names=(names)
|
29
|
+
@@multi_db_names[self.name] = names
|
30
|
+
end
|
31
|
+
|
32
|
+
unless DbCharmer.rails31?
|
33
|
+
def migrate_with_db_wrapper(direction)
|
34
|
+
if names = multi_db_names
|
35
|
+
names.each do |multi_db_name|
|
36
|
+
on_db(multi_db_name) do
|
37
|
+
migrate_without_db_wrapper(direction)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
else
|
41
|
+
migrate_without_db_wrapper(direction)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def on_db(db_name)
|
46
|
+
s = "Switching connection to "
|
47
|
+
if db_name.is_a?(Hash)
|
48
|
+
s << db_name[:connection_name]
|
49
|
+
s << ", schema #{db_name[:schema_name]}" if db_name[:schema_name]
|
50
|
+
else
|
51
|
+
s << db_name.inspect
|
52
|
+
end
|
53
|
+
announce s
|
54
|
+
# Switch connection
|
55
|
+
old_proxy = ::ActiveRecord::Base.db_charmer_connection_proxy
|
56
|
+
db_name = nil if db_name == :default
|
57
|
+
::ActiveRecord::Base.switch_connection_to(db_name, DbCharmer.connections_should_exist?)
|
58
|
+
# Yield the block
|
59
|
+
yield
|
60
|
+
ensure
|
61
|
+
# Switch it back
|
62
|
+
::ActiveRecord::Base.verify_active_connections!
|
63
|
+
announce "Switching connection back"
|
64
|
+
::ActiveRecord::Base.switch_connection_to(old_proxy)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def db_magic(opts = {})
|
69
|
+
# Collect connections from all possible options
|
70
|
+
conns = [ opts[:connection], opts[:connections] ]
|
71
|
+
conns << shard_connections(opts[:sharded_connection]) if opts[:sharded_connection]
|
72
|
+
|
73
|
+
# Get a unique set of connections
|
74
|
+
conns = conns.flatten.compact.uniq
|
75
|
+
raise ArgumentError, "No connection name - no magic!" unless conns.any?
|
76
|
+
|
77
|
+
# Save connections
|
78
|
+
self.multi_db_names = conns
|
79
|
+
end
|
16
80
|
|
17
|
-
|
18
|
-
|
81
|
+
# Return a list of connections to shards in a sharded connection
|
82
|
+
def shard_connections(conn_name)
|
83
|
+
conn = DbCharmer::Sharding.sharded_connection(conn_name)
|
84
|
+
conn.shard_connections
|
85
|
+
end
|
19
86
|
end
|
20
87
|
|
21
88
|
def migrate_with_db_wrapper(direction)
|
22
|
-
if names = multi_db_names
|
89
|
+
if names = self.class.multi_db_names
|
23
90
|
names.each do |multi_db_name|
|
24
91
|
on_db(multi_db_name) do
|
25
92
|
migrate_without_db_wrapper(direction)
|
@@ -30,41 +97,52 @@ module DbCharmer
|
|
30
97
|
end
|
31
98
|
end
|
32
99
|
|
33
|
-
def
|
34
|
-
|
35
|
-
|
100
|
+
def record_on_db(db_name, block)
|
101
|
+
recorder = ::ActiveRecord::Migration::CommandRecorder.new(DbCharmer::ConnectionFactory.connect(db_name))
|
102
|
+
old_recorder, @connection = @connection, recorder
|
103
|
+
block.call
|
104
|
+
old_recorder.record :on_db, [db_name, @connection]
|
105
|
+
@connection = old_recorder
|
106
|
+
end
|
107
|
+
|
108
|
+
def replay_commands_on_db(name, commands)
|
109
|
+
on_db(name) do
|
110
|
+
commands.each do |cmd, args|
|
111
|
+
send(cmd, *args)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def on_db(db_name, &block)
|
117
|
+
if @connection.is_a?(::ActiveRecord::Migration::CommandRecorder)
|
118
|
+
record_on_db(db_name, block)
|
119
|
+
return
|
120
|
+
end
|
121
|
+
|
122
|
+
s = "Switching connection to "
|
123
|
+
if db_name.is_a?(Hash)
|
124
|
+
s << db_name[:connection_name]
|
125
|
+
s << ", schema #{db_name[:schema_name]}" if db_name[:schema_name]
|
126
|
+
else
|
127
|
+
s << db_name.inspect
|
128
|
+
end
|
129
|
+
announce s
|
36
130
|
# Switch connection
|
37
|
-
old_proxy = ::ActiveRecord::Base.db_charmer_connection_proxy
|
131
|
+
old_connection, old_proxy = @connection, ::ActiveRecord::Base.db_charmer_connection_proxy
|
38
132
|
db_name = nil if db_name == :default
|
39
133
|
::ActiveRecord::Base.switch_connection_to(db_name, DbCharmer.connections_should_exist?)
|
40
134
|
# Yield the block
|
41
|
-
|
135
|
+
::ActiveRecord::Base.connection_pool.with_connection do |conn|
|
136
|
+
@connection = conn
|
137
|
+
yield
|
138
|
+
end
|
42
139
|
ensure
|
140
|
+
@connection = old_connection
|
43
141
|
# Switch it back
|
44
142
|
::ActiveRecord::Base.verify_active_connections!
|
45
143
|
announce "Switching connection back"
|
46
144
|
::ActiveRecord::Base.switch_connection_to(old_proxy)
|
47
145
|
end
|
48
|
-
|
49
|
-
def db_magic(opts = {})
|
50
|
-
# Collect connections from all possible options
|
51
|
-
conns = [ opts[:connection], opts[:connections] ]
|
52
|
-
conns << shard_connections(opts[:sharded_connection]) if opts[:sharded_connection]
|
53
|
-
|
54
|
-
# Get a unique set of connections
|
55
|
-
conns = conns.flatten.compact.uniq
|
56
|
-
raise ArgumentError, "No connection name - no magic!" unless conns.any?
|
57
|
-
|
58
|
-
# Save connections
|
59
|
-
self.multi_db_names = conns
|
60
|
-
end
|
61
|
-
|
62
|
-
# Return a list of connections to shards in a sharded connection
|
63
|
-
def shard_connections(conn_name)
|
64
|
-
conn = DbCharmer::Sharding.sharded_connection(conn_name)
|
65
|
-
conn.shard_connections
|
66
|
-
end
|
67
|
-
|
68
146
|
end
|
69
147
|
end
|
70
148
|
end
|
@@ -29,7 +29,16 @@ module DbCharmer
|
|
29
29
|
def on_db(con, proxy_target = nil)
|
30
30
|
proxy_target ||= self
|
31
31
|
|
32
|
+
# This is for the block_schema_map method.
|
33
|
+
# Setting the table_name_prefix is done in switch_connection_to
|
34
|
+
# but we don't go through switch_connection_to when the connection is set
|
35
|
+
# outside of shard_for.
|
36
|
+
if self.respond_to? :set_schema_table_name_prefix
|
37
|
+
self.set_schema_table_name_prefix(con)
|
38
|
+
end
|
39
|
+
|
32
40
|
# Chain call
|
41
|
+
#Rails.logger.debug "MultiDbProxy#on_db: con=#{con} proxy_target=#{proxy_target}"
|
33
42
|
return OnDbProxy.new(proxy_target, con) unless block_given?
|
34
43
|
|
35
44
|
# Block call
|
@@ -24,6 +24,14 @@ module DbCharmer
|
|
24
24
|
@@connection_classes[connection_name] ||= establish_connection_to_db(connection_name, config)
|
25
25
|
end
|
26
26
|
|
27
|
+
def self.reset_connection(connection_name)
|
28
|
+
connection_name = connection_name.to_s
|
29
|
+
if @@connection_classes[connection_name]
|
30
|
+
@@connection_classes[connection_name].disconnect!
|
31
|
+
@@connection_classes.delete(connection_name)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
27
35
|
# Establish connection with a specified name
|
28
36
|
def self.establish_connection(connection_name, should_exist = true)
|
29
37
|
abstract_class = generate_abstract_class(connection_name, should_exist)
|
@@ -12,9 +12,14 @@ module DbCharmer
|
|
12
12
|
# Create a relation object and initialize its default connection
|
13
13
|
def relation_with_db_charmer(*args, &block)
|
14
14
|
relation_without_db_charmer(*args, &block).tap do |rel|
|
15
|
-
rel.db_charmer_connection = self.
|
15
|
+
rel.db_charmer_connection = self.retrieve_connection
|
16
16
|
rel.db_charmer_enable_slaves = self.db_charmer_slaves.any?
|
17
17
|
rel.db_charmer_connection_is_forced = !db_charmer_top_level_connection?
|
18
|
+
|
19
|
+
# The relation may have been memoized at a time when the schema
|
20
|
+
# was different. Reset the schema here to use the value saved in the model
|
21
|
+
# (which might have been updated previously when fetching the connection).
|
22
|
+
rel.table.name = self.table_name
|
18
23
|
end
|
19
24
|
end
|
20
25
|
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module DbCharmer
|
2
|
+
module ActiveRecord
|
3
|
+
module Preloader
|
4
|
+
module Association
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
included do
|
7
|
+
alias_method_chain :build_scope, :db_magic
|
8
|
+
end
|
9
|
+
|
10
|
+
def build_scope_with_db_magic
|
11
|
+
if model.db_charmer_top_level_connection? || reflection.options[:polymorphic] ||
|
12
|
+
model.db_charmer_default_connection != klass.db_charmer_default_connection
|
13
|
+
build_scope_without_db_magic
|
14
|
+
else
|
15
|
+
build_scope_without_db_magic.on_db(model)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module DbCharmer
|
2
|
+
module ActiveRecord
|
3
|
+
module Preloader
|
4
|
+
module HasAndBelongsToMany
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
included do
|
7
|
+
alias_method_chain :records_for, :db_magic
|
8
|
+
end
|
9
|
+
|
10
|
+
def records_for_with_db_magic(ids)
|
11
|
+
if model.db_charmer_top_level_connection? || reflection.options[:polymorphic] ||
|
12
|
+
model.db_charmer_default_connection != klass.db_charmer_default_connection
|
13
|
+
records_for_without_db_magic(ids)
|
14
|
+
else
|
15
|
+
klass.on_db(model) do
|
16
|
+
records_for_without_db_magic(ids)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|