module DbCharmer

This class is used to automatically generate small abstract ActiveRecord classes that would then be used as a source of database connections for DbCharmer magic. This way we do not need to re-implement all the connection establishing code that ActiveRecord already has and we make our code less dependant on Rails versions.

Simple proxy that sends all method calls to a real database connection

This is a more sophisticated sharding method based on a two layer database-backed blocks map that holds block-shard associations. Record blocks are mapped to tablegroups and groups are mapped to shards.

It automatically creates new blocks for new keys and assigns them to existing groups. Warning: make sure to create at least one shard and one group before inserting any records.

This is a more sophisticated sharding method based on a database-backed blocks map that holds block-shard associations. It automatically creates new blocks for new keys and assigns them to shards.

This is a simple proxy class used as a default connection on sharded models

The idea is to proxy all utility method calls to a real connection (set by the set_real_connection method when we switch shards) and fail on real database querying calls forcing users to switch shard connections.

Public Class Methods

connections_should_exist?() click to toggle source
# File lib/db_charmer.rb, line 56
def self.connections_should_exist?
  !! connections_should_exist
end
current_controller() click to toggle source
# File lib/db_charmer/force_slave_reads.rb, line 2
def self.current_controller
  Thread.current[:db_charmer_current_controller]
end
current_controller=(val) click to toggle source
# File lib/db_charmer/force_slave_reads.rb, line 6
def self.current_controller=(val)
  Thread.current[:db_charmer_current_controller] = val
end
detect_environment() click to toggle source

Returns current environment name based on Rails or Rack environment variables

# File lib/db_charmer.rb, line 42
def self.detect_environment
  return Rails.env if running_with_rails?
  ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'default'
end
enable_controller_magic!() click to toggle source
# File lib/db_charmer.rb, line 68
def self.enable_controller_magic!
  ::ActionController::Base.extend(DbCharmer::ActionController::ForceSlaveReads::ClassMethods)
  ::ActionController::Base.send(:include, DbCharmer::ActionController::ForceSlaveReads::InstanceMethods)
end
force_slave_reads() { || ... } click to toggle source
# File lib/db_charmer/force_slave_reads.rb, line 45
def self.force_slave_reads
  raise ArgumentError, "No block given" unless block_given?
  old_forced_slave_reads = self.forced_slave_reads_setting
  begin
    self.forced_slave_reads_setting = true
    yield
  ensure
    self.forced_slave_reads_setting = old_forced_slave_reads
  end
end
force_slave_reads?() click to toggle source
# File lib/db_charmer/force_slave_reads.rb, line 20
def self.force_slave_reads?
  # If global force slave reads is requested, do it
  return true if Thread.current[:db_charmer_forced_slave_reads]

  # If not, try to use current controller to decide on this
  return false unless current_controller.respond_to?(:force_slave_reads?)

  slave_reads = current_controller.force_slave_reads?
  logger.debug("Using controller to figure out if slave reads should be forced: #{slave_reads}")
  return slave_reads
end
forced_slave_reads_setting() click to toggle source
# File lib/db_charmer/force_slave_reads.rb, line 11
def self.forced_slave_reads_setting
  Thread.current[:db_charmer_forced_slave_reads]
end
forced_slave_reads_setting=(val) click to toggle source
# File lib/db_charmer/force_slave_reads.rb, line 15
def self.forced_slave_reads_setting=(val)
  Thread.current[:db_charmer_forced_slave_reads] = val
end
hijack_new_classes?() click to toggle source
# File lib/db_charmer/with_remapped_databases.rb, line 16
def self.hijack_new_classes?
  !! Thread.current[:db_charmer_hijack_new_classes]
end
logger() click to toggle source
# File lib/db_charmer.rb, line 61
def self.logger
  return Rails.logger if running_with_rails?
  @@logger ||= Logger.new(STDERR)
end
rails2?() click to toggle source

Used in all Rails2-specific places

# File lib/db_charmer.rb, line 26
def self.rails2?
  ::ActiveRecord::VERSION::MAJOR == 2
end
rails31?() click to toggle source

Used in all Rails3.1-specific places

# File lib/db_charmer.rb, line 21
def self.rails31?
  rails3? && ::ActiveRecord::VERSION::MINOR >= 1
end
rails324?() click to toggle source

Detect broken Rails version

# File lib/db_charmer.rb, line 31
def self.rails324?
  ActiveRecord::VERSION::STRING == '3.2.4'
end
rails3?() click to toggle source
# File lib/db_charmer.rb, line 16
def self.rails3?
  ::ActiveRecord::VERSION::MAJOR > 2
end
running_with_rails?() click to toggle source
# File lib/db_charmer.rb, line 37
def self.running_with_rails?
  defined?(Rails) && Rails.respond_to?(:env)
end
with_controller(controller) { || ... } click to toggle source
# File lib/db_charmer/force_slave_reads.rb, line 33
def self.with_controller(controller)
  raise ArgumentError, "No block given" unless block_given?
  logger.debug("Setting current controller for db_charmer: #{controller.class.name}")
  self.current_controller = controller
  yield
ensure
  logger.debug('Clearing current controller for db_charmer')
  self.current_controller = nil
end
with_remapped_databases(mappings, &proc) click to toggle source
# File lib/db_charmer/with_remapped_databases.rb, line 2
def self.with_remapped_databases(mappings, &proc)
  old_mappings = ::ActiveRecord::Base.db_charmer_database_remappings
  begin
    ::ActiveRecord::Base.db_charmer_database_remappings = mappings
    if mappings[:master] || mappings['master']
      with_all_hijacked(&proc)
    else
      proc.call
    end
  ensure
    ::ActiveRecord::Base.db_charmer_database_remappings = old_mappings
  end
end

Private Class Methods

with_all_hijacked() { || ... } click to toggle source
# File lib/db_charmer/with_remapped_databases.rb, line 22
def self.with_all_hijacked
  old_hijack_new_classes = Thread.current[:db_charmer_hijack_new_classes]
  begin
    Thread.current[:db_charmer_hijack_new_classes] = true
    subclasses_method = DbCharmer.rails3? ? :descendants : :subclasses
    ::ActiveRecord::Base.send(subclasses_method).each do |subclass|
      subclass.hijack_connection!
    end
    yield
  ensure
    Thread.current[:db_charmer_hijack_new_classes] = old_hijack_new_classes
  end
end