yam-db-charmer 1.7.01 → 1.7.4.0
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/.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
|