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 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