master_slave 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,67 @@
1
+ module MasterSlave
2
+ module Base
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ cattr_accessor :slave_connection_names
7
+ self.slave_connection_names = []
8
+
9
+ class << self
10
+ alias_method :connection_without_master_slave, :connection
11
+ alias_method :connection, :connection_with_master_slave
12
+ end
13
+ end
14
+
15
+ module ClassMethods
16
+
17
+ def slave(&block)
18
+ slave_name = select_slave_connection_name
19
+ if slave_name.blank?
20
+ block.call
21
+ else
22
+ with_slave(slave_name) do
23
+ block.call
24
+ end
25
+ end
26
+ end
27
+
28
+ def using(slave_name, &block)
29
+ with_slave(slave_name) do
30
+ block.call
31
+ end
32
+ end
33
+
34
+ def connection_with_master_slave
35
+ slave_block = MasterSlave::RuntimeRegistry.slave_block
36
+ current_slave_name = MasterSlave::RuntimeRegistry.current_slave_name
37
+
38
+ if defined?(Rails) && Rails.env.test?
39
+ connection_without_master_slave
40
+ elsif slave_block && current_slave_name
41
+ pool_name = MasterSlave::ConnectionHandler.connection_pool_name(current_slave_name)
42
+ ar_proxy = MasterSlave::ConnectionHandler::ArProxy.new(pool_name)
43
+ ActiveRecord::Base.connection_handler.retrieve_connection(ar_proxy)
44
+ else
45
+ connection_without_master_slave
46
+ end
47
+ end
48
+
49
+ protected
50
+
51
+ def select_slave_connection_name
52
+ ActiveRecord::Base.slave_connection_names[rand(ActiveRecord::Base.slave_connection_names.size)]
53
+ end
54
+
55
+ def with_slave(slave_name)
56
+ slave_name = slave_name.to_s.strip
57
+ raise "#{slave_name} not exist" unless ActiveRecord::Base.slave_connection_names.include?(slave_name)
58
+ MasterSlave::RuntimeRegistry.current_slave_name = slave_name
59
+ MasterSlave::RuntimeRegistry.slave_block = true
60
+ yield
61
+ ensure
62
+ MasterSlave::RuntimeRegistry.current_slave_name = nil
63
+ MasterSlave::RuntimeRegistry.slave_block = false
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,38 @@
1
+ module MasterSlave
2
+
3
+ def self.config
4
+ @config ||= MasterSlave::Configuration.new
5
+ end
6
+
7
+ class Configuration
8
+ cattr_accessor :config_file
9
+
10
+ def initialize
11
+ @content = load_config
12
+ end
13
+
14
+ def slave_names
15
+ raise "#{Rails.env}'s slave config not exist" if @content.blank?
16
+ @shard_names ||= @content.keys.sort
17
+ end
18
+
19
+ def slave_config(slave_name)
20
+ @content[slave_name]
21
+ end
22
+
23
+ def self.config_file
24
+ @@config_file ||= File.join(Rails.root, 'config', 'shards.yml')
25
+ end
26
+
27
+ private
28
+
29
+ def load_config
30
+ if File.exist?(self.class.config_file)
31
+ hash_config = YAML.load(ERB.new(File.open(self.class.config_file).read).result)
32
+ HashWithIndifferentAccess.new(hash_config)[Rails.env]
33
+ else
34
+ {}
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,47 @@
1
+ # encoding: utf-8
2
+ module MasterSlave
3
+ class ConnectionHandler
4
+
5
+ class ArProxy
6
+ attr_reader :name
7
+
8
+ def initialize(name)
9
+ @name = name
10
+ end
11
+ end
12
+
13
+ class << self
14
+
15
+ def setup
16
+ setup_connection
17
+ end
18
+
19
+ def connection_pool_name(slave_name)
20
+ "master_slave_#{slave_name.to_s.strip}"
21
+ end
22
+
23
+ protected
24
+
25
+ def setup_connection
26
+ ActiveRecord::Base.slave_connection_names ||= []
27
+ MasterSlave.config.slave_names.each do |slave_name|
28
+ ActiveRecord::Base.slave_connection_names << slave_name.to_s.strip
29
+ configuration = MasterSlave.config.slave_config(slave_name).symbolize_keys
30
+ unless configuration.key?(:adapter) then raise AdapterNotSpecified, "database configuration does not specify adapter" end
31
+
32
+ adapter_method = "#{configuration[:adapter]}_connection"
33
+ unless ActiveRecord::Base.respond_to?(adapter_method)
34
+ raise AdapterNotFound, "database configuration specifies nonexistent #{spec.config[:adapter]} adapter"
35
+ end
36
+
37
+ # remove_connection 时会调用方法内部 ar_proxy.name
38
+ ar_proxy = ArProxy.new(connection_pool_name(slave_name))
39
+ ActiveRecord::Base.remove_connection(ar_proxy)
40
+
41
+ spec = ActiveRecord::Base::ConnectionSpecification.new(configuration, adapter_method)
42
+ ActiveRecord::Base.connection_handler.establish_connection ar_proxy.name, spec
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,20 @@
1
+ module MasterSlave
2
+ class Railtie < Rails::Railtie # :nodoc:
3
+
4
+ config.after_initialize do
5
+ if File.exist?(MasterSlave::Configuration.config_file)
6
+ puts "\033[32mmaster_slave is on!\033[0m"
7
+ ActiveRecord::Base.send :include, MasterSlave::Base
8
+ MasterSlave::ConnectionHandler.setup
9
+ else
10
+ puts "\033[31mNo such file #{MasterSlave::Configuration.config_file}\033[0m"
11
+ puts "\033[31mPlease execute `rails g master_slave:config`\033[0m"
12
+ puts "\033[31mmaster_slave is off!\033[0m"
13
+ end
14
+ end
15
+
16
+ generators do
17
+ require File.expand_path('../../rails/generators/config_generator', __FILE__)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,32 @@
1
+ begin
2
+ require 'active_support/per_thread_registry'
3
+ rescue LoadError
4
+ module ActiveSupport
5
+ module PerThreadRegistry
6
+ protected
7
+
8
+ def method_missing(name, *args, &block) # :nodoc:
9
+ # Caches the method definition as a singleton method of the receiver.
10
+ define_singleton_method(name) do |*a, &b|
11
+ per_thread_registry_instance.public_send(name, *a, &b)
12
+ end
13
+
14
+ send(name, *args, &block)
15
+ end
16
+
17
+ private
18
+
19
+ def per_thread_registry_instance
20
+ Thread.current[name] ||= new
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ module MasterSlave
27
+ class RuntimeRegistry
28
+ extend ActiveSupport::PerThreadRegistry
29
+
30
+ attr_accessor :slave_block, :current_slave_name
31
+ end
32
+ end
@@ -0,0 +1,3 @@
1
+ module MasterSlave
2
+ VERSION = '3.0.0'
3
+ end
@@ -0,0 +1,6 @@
1
+ require 'master_slave/base'
2
+ require 'master_slave/config'
3
+ require 'master_slave/connection_handler'
4
+ require 'master_slave/runtime_registry'
5
+ require 'master_slave/version'
6
+ require 'master_slave/railtie'
@@ -0,0 +1,34 @@
1
+ require "rails/generators/named_base"
2
+ module MasterSlave
3
+ module Generators
4
+ class ConfigGenerator < Rails::Generators::Base
5
+ desc "Creates a MasterSlave configuration file at config/shards.yml"
6
+
7
+ source_root File.expand_path('../templates', __FILE__)
8
+
9
+ argument :database_name, type: :string, optional: true
10
+
11
+ def app_name
12
+ Rails::Application.subclasses.first.parent.to_s.underscore
13
+ end
14
+
15
+ def create_config_file
16
+ template 'shards.yml', File.join(Rails.root, 'config', "shards.yml")
17
+ end
18
+
19
+ def mysql_socket
20
+ @mysql_socket ||= [
21
+ "/tmp/mysql.sock", # default
22
+ "/var/run/mysqld/mysqld.sock", # debian/gentoo
23
+ "/var/tmp/mysql.sock", # freebsd
24
+ "/var/lib/mysql/mysql.sock", # fedora
25
+ "/opt/local/lib/mysql/mysql.sock", # fedora
26
+ "/opt/local/var/run/mysqld/mysqld.sock", # mac + darwinports + mysql
27
+ "/opt/local/var/run/mysql4/mysqld.sock", # mac + darwinports + mysql4
28
+ "/opt/local/var/run/mysql5/mysqld.sock", # mac + darwinports + mysql5
29
+ "/opt/lampp/var/mysql/mysql.sock" # xampp for linux
30
+ ].find { |f| File.exist?(f) } unless RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,54 @@
1
+ # MySQL. Versions 4.1 and 5.0 are recommended.
2
+ #
3
+ # Install the MYSQL driver
4
+ # gem install mysql2
5
+ #
6
+ # Ensure the MySQL gem is defined in your Gemfile
7
+ # gem 'mysql2'
8
+ #
9
+ # And be sure to use new-style password hashing:
10
+ # http://dev.mysql.com/doc/refman/5.0/en/old-client.html
11
+ development:
12
+ slave:
13
+ adapter: mysql2
14
+ encoding: utf8
15
+ database: <%= database_name || app_name %>_development_slave
16
+ pool: 5
17
+ username: root
18
+ password:
19
+ <% if mysql_socket -%>
20
+ socket: <%= mysql_socket %>
21
+ <% else -%>
22
+ host: localhost
23
+ <% end -%>
24
+
25
+ # Warning: The database defined as "test" will be erased and
26
+ # re-generated from your development database when you run "rake".
27
+ # Do not set this db to the same as development or production.
28
+ test:
29
+ slave:
30
+ adapter: mysql2
31
+ encoding: utf8
32
+ database: <%= database_name || app_name %>_test_slave
33
+ pool: 5
34
+ username: root
35
+ password:
36
+ <% if mysql_socket -%>
37
+ socket: <%= mysql_socket %>
38
+ <% else -%>
39
+ host: localhost
40
+ <% end -%>
41
+
42
+ production:
43
+ slave:
44
+ adapter: mysql2
45
+ encoding: utf8
46
+ database: <%= database_name || app_name %>_production_slave
47
+ pool: 5
48
+ username: root
49
+ password:
50
+ <% if mysql_socket -%>
51
+ socket: <%= mysql_socket %>
52
+ <% else -%>
53
+ host: localhost
54
+ <% end -%>
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: master_slave
3
+ version: !ruby/object:Gem::Version
4
+ version: 3.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Tumayun, Zhangyuan
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-11-22 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rails
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 3.0.0
22
+ - - <
23
+ - !ruby/object:Gem::Version
24
+ version: 3.2.0
25
+ type: :runtime
26
+ prerelease: false
27
+ version_requirements: !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: 3.0.0
33
+ - - <
34
+ - !ruby/object:Gem::Version
35
+ version: 3.2.0
36
+ description: mysql separate read and write.
37
+ email: tumayun.2010@gmail.com
38
+ executables: []
39
+ extensions: []
40
+ extra_rdoc_files: []
41
+ files:
42
+ - lib/master_slave.rb
43
+ - lib/rails/generators/templates/shards.yml
44
+ - lib/rails/generators/config_generator.rb
45
+ - lib/master_slave/version.rb
46
+ - lib/master_slave/railtie.rb
47
+ - lib/master_slave/config.rb
48
+ - lib/master_slave/connection_handler.rb
49
+ - lib/master_slave/runtime_registry.rb
50
+ - lib/master_slave/base.rb
51
+ homepage: https://github.com/tumayun/master_slave
52
+ licenses: []
53
+ post_install_message:
54
+ rdoc_options: []
55
+ require_paths:
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ requirements: []
70
+ rubyforge_project:
71
+ rubygems_version: 1.8.23
72
+ signing_key:
73
+ specification_version: 3
74
+ summary: master_slave.
75
+ test_files: []