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