master_slave 3.0.0
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/master_slave/base.rb +67 -0
- data/lib/master_slave/config.rb +38 -0
- data/lib/master_slave/connection_handler.rb +47 -0
- data/lib/master_slave/railtie.rb +20 -0
- data/lib/master_slave/runtime_registry.rb +32 -0
- data/lib/master_slave/version.rb +3 -0
- data/lib/master_slave.rb +6 -0
- data/lib/rails/generators/config_generator.rb +34 -0
- data/lib/rails/generators/templates/shards.yml +54 -0
- metadata +75 -0
@@ -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
|
data/lib/master_slave.rb
ADDED
@@ -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: []
|