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 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::RelationMethod)
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
- # Establishes connection or return an existing one from cache
16
- def self.connect(connection_name, should_exist = true)
17
- connection_name = connection_name.to_s
18
- @@connection_classes[connection_name] ||= establish_connection(connection_name, should_exist)
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(connection_name, should_exist = true)
37
- abstract_class = generate_abstract_class(connection_name, should_exist)
38
- DbCharmer::ConnectionProxy.new(abstract_class, connection_name)
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.retrieve_connection
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
@@ -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 config[:method] == :db_block_schema_map
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 => shard.db_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.reset_table_name
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
@@ -3,7 +3,7 @@ module DbCharmer
3
3
  MAJOR = 1
4
4
  MINOR = 7
5
5
  PATCH = 4
6
- BUILD = 0
6
+ BUILD = 8
7
7
 
8
8
  STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join('.')
9
9
  end
@@ -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
- dbgm.drop_all_shard_databases
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
@@ -14,6 +14,7 @@ namespace :db_charmer do
14
14
  end
15
15
 
16
16
  task :purge do
17
+ Rake::Task['db_charmer:drop:schema_shards'].invoke
17
18
  Rake::Task['db_charmer:drop'].invoke
18
19
  Rake::Task['db_charmer:create'].invoke
19
20
  end
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: 119
4
+ hash: 103
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
8
  - 7
9
9
  - 4
10
- - 0
11
- version: 1.7.4.0
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-09-18 00:00:00 -07:00
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