yam-db-charmer 1.7.4.0 → 1.7.4.8
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/db_charmer.rb +2 -1
- data/lib/db_charmer/active_record/class_attributes.rb +9 -0
- data/lib/db_charmer/active_record/connection_switching.rb +4 -0
- data/lib/db_charmer/active_record/db_magic.rb +19 -0
- data/lib/db_charmer/active_record/migration/multi_db_migrations.rb +20 -0
- data/lib/db_charmer/active_record/sharding.rb +10 -0
- data/lib/db_charmer/connection_factory.rb +12 -7
- data/lib/db_charmer/rails3/active_record/relation/arel_engine.rb +22 -0
- data/lib/db_charmer/rails3/active_record/relation_method.rb +1 -7
- data/lib/db_charmer/sharding.rb +1 -1
- data/lib/db_charmer/sharding/method/db_block_group_map.rb +2 -0
- data/lib/db_charmer/sharding/method/db_block_group_map_base.rb +4 -6
- data/lib/db_charmer/sharding/method/db_block_schema_map.rb +34 -4
- data/lib/db_charmer/version.rb +1 -1
- data/lib/tasks/databases.rake +12 -1
- data/lib/tasks/test.rake +1 -0
- metadata +5 -4
data/lib/db_charmer.rb
CHANGED
@@ -130,8 +130,9 @@ ActiveRecord::Base.send(:include, DbCharmer::ActiveRecord::MultiDbProxy::Instanc
|
|
130
130
|
# Enable connection proxy for relations
|
131
131
|
if DbCharmer.rails3?
|
132
132
|
require 'db_charmer/rails3/active_record/relation_method'
|
133
|
+
require 'db_charmer/rails3/active_record/relation/arel_engine'
|
133
134
|
require 'db_charmer/rails3/active_record/relation/connection_routing'
|
134
|
-
ActiveRecord::Base.extend(DbCharmer::ActiveRecord::
|
135
|
+
ActiveRecord::Base.extend(DbCharmer::ActiveRecord::Relation::ArelEngine)
|
135
136
|
ActiveRecord::Relation.send(:include, DbCharmer::ActiveRecord::Relation::ConnectionRouting)
|
136
137
|
end
|
137
138
|
|
@@ -96,6 +96,15 @@ module DbCharmer
|
|
96
96
|
raise "Mappings must be nil or respond to []" if mappings && (! mappings.respond_to?(:[]))
|
97
97
|
@@db_charmer_database_remappings = mappings || { }
|
98
98
|
end
|
99
|
+
|
100
|
+
# For sharded models, return the table name without the schema prefix
|
101
|
+
def table_name_without_schema
|
102
|
+
if self.respond_to?(:orig_table_name)
|
103
|
+
self.orig_table_name
|
104
|
+
else
|
105
|
+
self.table_name
|
106
|
+
end
|
107
|
+
end
|
99
108
|
end
|
100
109
|
end
|
101
110
|
end
|
@@ -28,6 +28,10 @@ module DbCharmer
|
|
28
28
|
|
29
29
|
#-----------------------------------------------------------------------------------------------------------------
|
30
30
|
def hijack_connection!
|
31
|
+
|
32
|
+
# do not hijack anything if the model is not handled by db_charmer
|
33
|
+
return unless self.respond_to?(:db_charmer_extended)
|
34
|
+
|
31
35
|
return if self.respond_to?(:connection_with_magic)
|
32
36
|
class << self
|
33
37
|
# Make sure we check our accessors before going to the default connection retrieval method
|
@@ -3,9 +3,18 @@ module DbCharmer
|
|
3
3
|
module DbMagic
|
4
4
|
|
5
5
|
def db_magic(opt = {})
|
6
|
+
|
7
|
+
# define a method that can be used to check if
|
8
|
+
# a model is extended by db_charmer
|
9
|
+
create_true_method(:db_charmer_extended)
|
10
|
+
|
6
11
|
# Make sure we could use our connections management here
|
7
12
|
hijack_connection!
|
8
13
|
|
14
|
+
if DbCharmer.rails3?
|
15
|
+
self.extend(DbCharmer::ActiveRecord::RelationMethod) unless self.respond_to?(:relation_with_db_charmer)
|
16
|
+
end
|
17
|
+
|
9
18
|
# Should requested connections exist in the config?
|
10
19
|
should_exist = opt.has_key?(:should_exist) ? opt[:should_exist] : DbCharmer.connections_should_exist?
|
11
20
|
|
@@ -33,6 +42,16 @@ module DbCharmer
|
|
33
42
|
end
|
34
43
|
end
|
35
44
|
|
45
|
+
def create_true_method(method_name)
|
46
|
+
unless self.respond_to?(method_name.to_sym)
|
47
|
+
(class << self; self; end).instance_eval do
|
48
|
+
define_method method_name.to_sym do
|
49
|
+
true
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
36
55
|
private
|
37
56
|
|
38
57
|
def setup_children_magic(opt)
|
@@ -31,6 +31,10 @@ module DbCharmer
|
|
31
31
|
|
32
32
|
unless DbCharmer.rails31?
|
33
33
|
def migrate_with_db_wrapper(direction)
|
34
|
+
# define a method that can be used to check if
|
35
|
+
# a model is extended by db_charmer
|
36
|
+
::ActiveRecord::Base.create_true_method(:db_charmer_extended)
|
37
|
+
|
34
38
|
if names = multi_db_names
|
35
39
|
names.each do |multi_db_name|
|
36
40
|
on_db(multi_db_name) do
|
@@ -42,6 +46,22 @@ module DbCharmer
|
|
42
46
|
end
|
43
47
|
end
|
44
48
|
|
49
|
+
# Run some migration method on all shards
|
50
|
+
def on_each_shard(sharded_connection)
|
51
|
+
db_magic :sharded_connection => sharded_connection
|
52
|
+
if names = multi_db_names
|
53
|
+
names.each do |multi_db_name|
|
54
|
+
on_db(multi_db_name) do
|
55
|
+
yield
|
56
|
+
end
|
57
|
+
end
|
58
|
+
else
|
59
|
+
raise ArgumentError, 'no shard names defined'
|
60
|
+
end
|
61
|
+
ensure
|
62
|
+
self.multi_db_names = nil
|
63
|
+
end
|
64
|
+
|
45
65
|
def on_db(db_name)
|
46
66
|
s = "Switching connection to "
|
47
67
|
if db_name.is_a?(Hash)
|
@@ -12,6 +12,16 @@ module DbCharmer
|
|
12
12
|
on_db(conn, proxy_target, &block)
|
13
13
|
end
|
14
14
|
|
15
|
+
# The block passed to this function should return an object responding to key_field
|
16
|
+
def create_with_allocated_shard(key_field = :id, &block)
|
17
|
+
group = sharded_connection.sharder.least_loaded_group
|
18
|
+
shard_info = shard_info_by_group_id(group_id)
|
19
|
+
conn = shard_connection_config(shard_info, group.id)
|
20
|
+
key_obj = on_db(conn, proxy_target, &block)
|
21
|
+
allocate_new_block_for_key_on_group(key_obj.send(key_field), group)
|
22
|
+
key_obj
|
23
|
+
end
|
24
|
+
|
15
25
|
# Run on default shard (if supported by the sharding method)
|
16
26
|
def on_default_shard(proxy_target = nil, &block)
|
17
27
|
raise ArgumentError, "No sharded connection configured!" unless sharded_connection
|
@@ -12,10 +12,10 @@ module DbCharmer
|
|
12
12
|
@@connection_classes = {}
|
13
13
|
end
|
14
14
|
|
15
|
-
#
|
16
|
-
def self.connect(
|
17
|
-
|
18
|
-
@@connection_classes[
|
15
|
+
# Uses the passed in connection class or establishes a new one connection if one exists it will return an existing one from cache
|
16
|
+
def self.connect(connection_or_name, should_exist = true)
|
17
|
+
connection_or_name = connection_or_name.to_s unless connection_or_name.respond_to?(:connection)
|
18
|
+
@@connection_classes[connection_or_name] ||= establish_connection(connection_or_name, should_exist)
|
19
19
|
end
|
20
20
|
|
21
21
|
# Establishes connection or return an existing one from cache (not using AR database configs)
|
@@ -33,9 +33,14 @@ module DbCharmer
|
|
33
33
|
end
|
34
34
|
|
35
35
|
# Establish connection with a specified name
|
36
|
-
def self.establish_connection(
|
37
|
-
abstract_class =
|
38
|
-
|
36
|
+
def self.establish_connection(connection_or_name, should_exist = true)
|
37
|
+
abstract_class =
|
38
|
+
if connection_or_name.respond_to?(:connection)
|
39
|
+
connection_or_name
|
40
|
+
else
|
41
|
+
generate_abstract_class(connection_or_name, should_exist)
|
42
|
+
end
|
43
|
+
DbCharmer::ConnectionProxy.new(abstract_class, connection_or_name.to_s)
|
39
44
|
end
|
40
45
|
|
41
46
|
# Establish connection with a specified name (not using AR database configs)
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module DbCharmer
|
2
|
+
module ActiveRecord
|
3
|
+
module Relation
|
4
|
+
module ArelEngine
|
5
|
+
|
6
|
+
def self.extended(base)
|
7
|
+
class << base
|
8
|
+
alias_method_chain :arel_engine, :db_charmer
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# Use the model itself an engine for Arel, do not fall back to AR::Base
|
13
|
+
# That guarantees the correct connection will be used in Arel::Table
|
14
|
+
# for determining list of columns and stuff.
|
15
|
+
def arel_engine_with_db_charmer(*)
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -5,14 +5,13 @@ module DbCharmer
|
|
5
5
|
def self.extended(base)
|
6
6
|
class << base
|
7
7
|
alias_method_chain :relation, :db_charmer
|
8
|
-
alias_method_chain :arel_engine, :db_charmer
|
9
8
|
end
|
10
9
|
end
|
11
10
|
|
12
11
|
# Create a relation object and initialize its default connection
|
13
12
|
def relation_with_db_charmer(*args, &block)
|
14
13
|
relation_without_db_charmer(*args, &block).tap do |rel|
|
15
|
-
rel.db_charmer_connection = self.
|
14
|
+
rel.db_charmer_connection = self.connection
|
16
15
|
rel.db_charmer_enable_slaves = self.db_charmer_slaves.any?
|
17
16
|
rel.db_charmer_connection_is_forced = !db_charmer_top_level_connection?
|
18
17
|
|
@@ -23,11 +22,6 @@ module DbCharmer
|
|
23
22
|
end
|
24
23
|
end
|
25
24
|
|
26
|
-
# Use the model itself an engine for Arel, do not fall back to AR::Base
|
27
|
-
def arel_engine_with_db_charmer(*)
|
28
|
-
self
|
29
|
-
end
|
30
|
-
|
31
25
|
end
|
32
26
|
end
|
33
27
|
end
|
data/lib/db_charmer/sharding.rb
CHANGED
@@ -11,7 +11,7 @@ module DbCharmer
|
|
11
11
|
@@sharded_connections[name] = DbCharmer::Sharding::Connection.new(config)
|
12
12
|
|
13
13
|
# Enable multi-db migrations
|
14
|
-
if
|
14
|
+
if @@sharded_connections[name].sharder.is_a?(DbCharmer::Sharding::Method::DbBlockSchemaMap)
|
15
15
|
::ActiveRecord::Base.extend(DbCharmer::Sharding::Method::SchemaTableNamePrefix)
|
16
16
|
end
|
17
17
|
end
|
@@ -14,6 +14,7 @@ module DbCharmer
|
|
14
14
|
|
15
15
|
# Shard connection info model
|
16
16
|
class Shard < ::ActiveRecord::Base
|
17
|
+
db_magic
|
17
18
|
validates_presence_of :db_host
|
18
19
|
validates_presence_of :db_port
|
19
20
|
validates_presence_of :db_user
|
@@ -25,6 +26,7 @@ module DbCharmer
|
|
25
26
|
|
26
27
|
# Table group info model
|
27
28
|
class Group < ::ActiveRecord::Base
|
29
|
+
db_magic
|
28
30
|
validates_presence_of :shard_id
|
29
31
|
belongs_to :shard, :class_name => 'DbCharmer::Sharding::Method::DbBlockGroupMap::Shard'
|
30
32
|
end
|
@@ -32,13 +32,11 @@ module DbCharmer
|
|
32
32
|
def initialize(config)
|
33
33
|
@name = config[:name] or raise(ArgumentError, "Missing required :name parameter!")
|
34
34
|
|
35
|
-
if config[:method] == :db_block_schema_map
|
36
|
-
if ::ActiveRecord::Base.configurations[DbCharmer.env]['adapter'] != 'postgresql'
|
37
|
-
raise(ArgumentError, 'DbBlockSchemaMap method can only be used with the postgresql adapter')
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
35
|
@connection = DbCharmer::ConnectionFactory.connect(config[:connection], true)
|
36
|
+
if self.is_a?(DbCharmer::Sharding::Method::DbBlockSchemaMap) && ::ActiveRecord::Base.configurations[DbCharmer.env]['adapter'] != 'postgresql'
|
37
|
+
raise(ArgumentError, 'DbBlockSchemaMap method can only be used with the postgresql adapter')
|
38
|
+
end
|
39
|
+
|
42
40
|
@block_size = (config[:block_size] || 10000).to_i
|
43
41
|
|
44
42
|
@map_table = config[:map_table] or raise(ArgumentError, "Missing required :map_table parameter!")
|
@@ -15,6 +15,7 @@ module DbCharmer
|
|
15
15
|
|
16
16
|
# Shard connection info model
|
17
17
|
class Shard < ::ActiveRecord::Base
|
18
|
+
db_magic
|
18
19
|
validates_presence_of :db_host
|
19
20
|
validates_presence_of :db_port
|
20
21
|
validates_presence_of :db_user
|
@@ -26,6 +27,7 @@ module DbCharmer
|
|
26
27
|
|
27
28
|
# Table group info model
|
28
29
|
class Group < ::ActiveRecord::Base
|
30
|
+
db_magic
|
29
31
|
validates_presence_of :shard_id
|
30
32
|
belongs_to :shard, :class_name => 'DbCharmer::Sharding::Method::DbBlockSchemaMap::Shard'
|
31
33
|
end
|
@@ -48,12 +50,13 @@ module DbCharmer
|
|
48
50
|
# Here we get the mapping connection's configuration
|
49
51
|
# They do not expose configs so we hack in and get the instance var
|
50
52
|
# FIXME: Find a better way, maybe move config method to our ar extenstions
|
53
|
+
slave_host = shard.respond_to?(:db_slave_host) ? shard.db_slave_host : ''
|
51
54
|
connection.config.clone.merge(
|
52
55
|
# Name for the connection factory
|
53
56
|
:connection_name => connection_name,
|
54
57
|
# Connection params
|
55
58
|
:host => shard.db_host,
|
56
|
-
:slave_host =>
|
59
|
+
:slave_host => slave_host,
|
57
60
|
:port => shard.db_port,
|
58
61
|
:username => shard.db_user,
|
59
62
|
:password => shard.db_pass,
|
@@ -64,6 +67,26 @@ module DbCharmer
|
|
64
67
|
)
|
65
68
|
end
|
66
69
|
|
70
|
+
#---------------------------------------------------------------------------------------------------------------
|
71
|
+
def allocate_new_block_for_key_on_group(key, group)
|
72
|
+
start_id = block_start_for_key(key)
|
73
|
+
end_id = block_end_for_key(key)
|
74
|
+
# Try to insert a new mapping (ignore duplicate key errors)
|
75
|
+
sql = <<-SQL
|
76
|
+
INSERT INTO #{map_table}
|
77
|
+
(start_id, end_id, group_id, block_size, created_at, updated_at) VALUES
|
78
|
+
(#{start_id}, #{end_id}, #{group.id}, #{block_size}, NOW(), NOW())
|
79
|
+
SQL
|
80
|
+
connection.execute(sql, "Allocate new block")
|
81
|
+
|
82
|
+
# Increment the blocks counter on the shard
|
83
|
+
group_class.update_counters(group.id, :blocks_count => +1)
|
84
|
+
|
85
|
+
# Retry block search after creation
|
86
|
+
block_for_key(key)
|
87
|
+
end
|
88
|
+
|
89
|
+
|
67
90
|
#---------------------------------------------------------------------------------------------------------------
|
68
91
|
def create_shard(params)
|
69
92
|
params = params.symbolize_keys
|
@@ -141,6 +164,9 @@ module DbCharmer
|
|
141
164
|
#Rails.logger.debug "set_schema_table_name_prefix: self=#{self} @dbcharmer_table_name_prefix=#{@dbcharmer_table_name_prefix}"
|
142
165
|
if con.is_a?(Hash) && con[:schema_name]
|
143
166
|
new_prefix = con[:schema_name] + '.'
|
167
|
+
if self.to_s!='ActiveRecord::Base' && @dbcharmer_table_name_prefix.blank?
|
168
|
+
@orig_table_name = table_name if defined?(:original_table_name)
|
169
|
+
end
|
144
170
|
else
|
145
171
|
new_prefix = '' if self.to_s=='ActiveRecord::Base' # this is for migrations
|
146
172
|
end
|
@@ -160,15 +186,19 @@ module DbCharmer
|
|
160
186
|
# is AR::Base and there is no actual table name.
|
161
187
|
unless self.to_s=='ActiveRecord::Base'
|
162
188
|
#Rails.logger.debug "set_schema_table_name_prefix: resetting"
|
163
|
-
reset_cached_table_name
|
189
|
+
reset_cached_table_name(@orig_table_name)
|
164
190
|
end
|
165
191
|
end
|
166
192
|
end
|
167
193
|
|
194
|
+
def orig_table_name
|
195
|
+
@orig_table_name ? @orig_table_name : table_name
|
196
|
+
end
|
197
|
+
|
168
198
|
# Rails memoizes table_name and table_name_prefix at the time the models are loaded.
|
169
199
|
# This method forces refreshing those names
|
170
|
-
def reset_cached_table_name
|
171
|
-
self.
|
200
|
+
def reset_cached_table_name(orig_table_name)
|
201
|
+
self.table_name = "#{full_table_name_prefix}#{orig_table_name}#{table_name_suffix}"
|
172
202
|
@arel_table = nil
|
173
203
|
@relation = nil
|
174
204
|
end
|
data/lib/db_charmer/version.rb
CHANGED
data/lib/tasks/databases.rake
CHANGED
@@ -89,6 +89,10 @@ end
|
|
89
89
|
# find all schema sharded databases and drop them
|
90
90
|
def drop_schema_shard_databases(config)
|
91
91
|
exit unless Rails.env=='test'
|
92
|
+
|
93
|
+
DbCharmer.env = 'test'
|
94
|
+
DbCharmer::ConnectionFactory.reset!
|
95
|
+
|
92
96
|
config.each do |name, sub_config|
|
93
97
|
next unless sub_config.is_a?(Hash)
|
94
98
|
next unless sub_config['database']
|
@@ -101,7 +105,14 @@ def drop_schema_shard_databases(config)
|
|
101
105
|
# itereate through entries in the shards_info table to find the
|
102
106
|
# databases that will be dropped
|
103
107
|
dbgm = DbCharmer::Sharding::Method::DbBlockSchemaMap.new(connection.config)
|
104
|
-
|
108
|
+
|
109
|
+
# Rescue this as there may not be any tables in the admin db yet
|
110
|
+
begin
|
111
|
+
dbgm.drop_all_shard_databases
|
112
|
+
rescue ActiveRecord::StatementInvalid => e
|
113
|
+
end
|
114
|
+
|
115
|
+
dbgm.connection.disconnect! # or subsequent rake tasks may fail
|
105
116
|
end
|
106
117
|
end
|
107
118
|
|
data/lib/tasks/test.rake
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: yam-db-charmer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 103
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 7
|
9
9
|
- 4
|
10
|
-
-
|
11
|
-
version: 1.7.4.
|
10
|
+
- 8
|
11
|
+
version: 1.7.4.8
|
12
12
|
platform: ruby
|
13
13
|
authors:
|
14
14
|
- Oleksiy Kovyrin
|
@@ -17,7 +17,7 @@ autorequire:
|
|
17
17
|
bindir: bin
|
18
18
|
cert_chain: []
|
19
19
|
|
20
|
-
date: 2012-
|
20
|
+
date: 2012-10-17 00:00:00 -07:00
|
21
21
|
default_executable:
|
22
22
|
dependencies:
|
23
23
|
- !ruby/object:Gem::Dependency
|
@@ -129,6 +129,7 @@ files:
|
|
129
129
|
- lib/db_charmer/rails3/abstract_adapter/connection_name.rb
|
130
130
|
- lib/db_charmer/rails3/active_record/log_subscriber.rb
|
131
131
|
- lib/db_charmer/rails3/active_record/master_slave_routing.rb
|
132
|
+
- lib/db_charmer/rails3/active_record/relation/arel_engine.rb
|
132
133
|
- lib/db_charmer/rails3/active_record/relation/connection_routing.rb
|
133
134
|
- lib/db_charmer/rails3/active_record/relation_method.rb
|
134
135
|
- lib/db_charmer/rails31/active_record/migration/command_recorder.rb
|