fresh_connection 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +4 -0
- data/Manifest.txt +9 -0
- data/README.rdoc +57 -0
- data/Rakefile +27 -0
- data/fresh_connection.gemspec +29 -0
- data/lib/fresh_connection/rack/connection_management.rb +18 -0
- data/lib/fresh_connection/slave_connection.rb +66 -0
- data/lib/fresh_connection.rb +8 -0
- data/rails/initializers/active_record_base.rb +93 -0
- metadata +77 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
= FreshConnection
|
2
|
+
|
3
|
+
* https://github.com/tsukasaoishi/fresh_connection
|
4
|
+
|
5
|
+
== Description
|
6
|
+
|
7
|
+
FreshConnection supports of connect with Mysql slave servers via Load Balancers.
|
8
|
+
Currently, I have only tested with Rails2.3.14.
|
9
|
+
In the future, I plan to support Rails3.
|
10
|
+
|
11
|
+
All connections will be disconnected every time at the end of the action.
|
12
|
+
|
13
|
+
== How to use at Rails
|
14
|
+
|
15
|
+
=== Gemfile
|
16
|
+
gem "fresh_connection", "=0.0.1"
|
17
|
+
|
18
|
+
=== config/database.yml
|
19
|
+
production:
|
20
|
+
adapter: mysql
|
21
|
+
encoding: utf8
|
22
|
+
reconnect: true
|
23
|
+
database: kaeru
|
24
|
+
pool: 5
|
25
|
+
username: master
|
26
|
+
password: master
|
27
|
+
host: localhost
|
28
|
+
socket: /var/run/mysqld/mysqld.sock
|
29
|
+
|
30
|
+
slave:
|
31
|
+
username: slave
|
32
|
+
password: slave
|
33
|
+
host: slave
|
34
|
+
max_connection: 5
|
35
|
+
|
36
|
+
slave is config to connect to slave servers.
|
37
|
+
max_connection is max connection number to connect to slave.
|
38
|
+
Others will use the master setting. If you want to change, write in the slave.
|
39
|
+
|
40
|
+
|
41
|
+
=== config/environment.rb
|
42
|
+
require 'fresh_connection'
|
43
|
+
ActionController::Dispatcher.middleware.swap ActiveRecord::ConnectionAdapters::ConnectionManagement, FreshConnection::Rack::ConnectionManagement
|
44
|
+
|
45
|
+
== Synopis
|
46
|
+
Read query will be access to slave server.
|
47
|
+
Article.where(:id => 1)
|
48
|
+
|
49
|
+
If you want to access to master saver, use readonly(false).
|
50
|
+
Article.where(:id => 1).readonly(false)
|
51
|
+
|
52
|
+
In transaction, Always will be access to master server.
|
53
|
+
Article.transaction do
|
54
|
+
Article.where(:id => 1)
|
55
|
+
end
|
56
|
+
|
57
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
%w[rubygems rake rake/clean fileutils newgem rubigen].each { |f| require f }
|
2
|
+
require File.dirname(__FILE__) + '/lib/fresh_connection'
|
3
|
+
|
4
|
+
# Generate all the Rake tasks
|
5
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
6
|
+
$hoe = Hoe.new('fresh_connection', FreshConnection::VERSION) do |p|
|
7
|
+
p.developer('Tsukasa OISHI', 'tsukasa.oishi@gmail.com')
|
8
|
+
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
9
|
+
p.rubyforge_name = p.name # TODO this is default value
|
10
|
+
# p.extra_deps = [
|
11
|
+
# ['activesupport','>= 2.0.2'],
|
12
|
+
# ]
|
13
|
+
p.extra_dev_deps = [
|
14
|
+
['newgem', ">= #{::Newgem::VERSION}"]
|
15
|
+
]
|
16
|
+
|
17
|
+
p.clean_globs |= %w[**/.DS_Store tmp *.log]
|
18
|
+
path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
|
19
|
+
p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
|
20
|
+
p.rsync_args = '-av --delete --ignore-errors'
|
21
|
+
end
|
22
|
+
|
23
|
+
require 'newgem/tasks' # load /tasks/*.rake
|
24
|
+
Dir['tasks/**/*.rake'].each { |t| load t }
|
25
|
+
|
26
|
+
# TODO - want other tests/tasks run by default? Add them to the list
|
27
|
+
# task :default => [:spec, :features]
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{fresh_connection}
|
5
|
+
s.version = "0.0.1"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Tsukasa OISHI"]
|
9
|
+
s.date = %q{2010-10-16}
|
10
|
+
s.description = %q{MiyazakiResistance is a library like ActiveRecord to use Tokyo Tyrant.}
|
11
|
+
s.email = ["tsukasa.oishi@gmail.com"]
|
12
|
+
s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.rdoc"]
|
13
|
+
s.files = %w|
|
14
|
+
Rakefile
|
15
|
+
fresh_connection.gemspec
|
16
|
+
lib/fresh_connection.rb
|
17
|
+
lib/fresh_connection/slave_connection.rb
|
18
|
+
lib/fresh_connection/rack/connection_management.rb
|
19
|
+
rails/initializers/active_record_base.rb
|
20
|
+
|
|
21
|
+
s.has_rdoc = true
|
22
|
+
s.homepage = %q{http://www.kaeruspoon.net/keywords/MiyazakiResistance}
|
23
|
+
s.post_install_message = %q{PostInstall.txt}
|
24
|
+
s.rdoc_options = ["--main", "README.rdoc"]
|
25
|
+
s.require_paths = ["lib"]
|
26
|
+
s.rubyforge_project = %q{miyazakiresistance}
|
27
|
+
s.rubygems_version = %q{1.3.1}
|
28
|
+
s.summary = %q{MiyazakiResistance is a library like ActiveRecord to use Tokyo Tyrant.}
|
29
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module FreshConnection
|
2
|
+
module Rack
|
3
|
+
class ConnectionManagement
|
4
|
+
def initialize(app)
|
5
|
+
@app = app
|
6
|
+
end
|
7
|
+
|
8
|
+
def call(env)
|
9
|
+
@app.call(env)
|
10
|
+
ensure
|
11
|
+
unless env.key?("rack.test")
|
12
|
+
ActiveRecord::Base.clear_all_connections!
|
13
|
+
FreshConnection::SlaveConnection.clear_all_connections!
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module FreshConnection
|
2
|
+
class SlaveConnection
|
3
|
+
class << self
|
4
|
+
def connection
|
5
|
+
slave_connections[slave_pointer] ||= new_connection
|
6
|
+
end
|
7
|
+
|
8
|
+
def clear_all_connections!
|
9
|
+
if @slave_connections.present?
|
10
|
+
@slave_connections.values.each{|conns| conns.each{|c| c.disconnect!}}
|
11
|
+
end
|
12
|
+
@slave_connections = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def slave_access_in
|
16
|
+
Thread.current[:slave_access] = true
|
17
|
+
end
|
18
|
+
|
19
|
+
def slave_access_out
|
20
|
+
Thread.current[:slave_access] = false
|
21
|
+
end
|
22
|
+
|
23
|
+
def slave_access?
|
24
|
+
Thread.current[:slave_access] ||= false
|
25
|
+
end
|
26
|
+
|
27
|
+
def shift_slave_pointer
|
28
|
+
Thread.current[:slave_pointer] = slave_pointer + 1
|
29
|
+
Thread.current[:slave_pointer] = 0 if slave_pointer > max_slave_pointer
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def new_connection
|
35
|
+
ActiveRecord::Base.send("#{spec["adapter"]}_connection", spec)
|
36
|
+
end
|
37
|
+
|
38
|
+
def slave_connections
|
39
|
+
@slave_connections ||= {}
|
40
|
+
@slave_connections[current_thread_id] ||= []
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
def slave_pointer
|
45
|
+
Thread.current[:slave_pointer] ||= 0
|
46
|
+
end
|
47
|
+
|
48
|
+
def max_slave_pointer
|
49
|
+
@max_slave_pointer ||= (spec["max_connection"] || 1) - 1
|
50
|
+
end
|
51
|
+
|
52
|
+
def spec
|
53
|
+
@spec ||= get_spec
|
54
|
+
end
|
55
|
+
|
56
|
+
def get_spec
|
57
|
+
ret = ActiveRecord::Base.configurations[Rails.env]
|
58
|
+
ret.merge(ret["slave"] || {})
|
59
|
+
end
|
60
|
+
|
61
|
+
def current_thread_id
|
62
|
+
Thread.current.object_id
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
class AbstractAdapter
|
4
|
+
def select_all_with_slave_cluster(*args)
|
5
|
+
if FreshConnection::SlaveConnection.slave_access?
|
6
|
+
change_connection {select_all_without_slave_cluster(*args)}
|
7
|
+
else
|
8
|
+
select_all_without_slave_cluster(*args)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
alias_method_chain :select_all, :slave_cluster
|
12
|
+
|
13
|
+
def columns_with_slave_cluster(*args)
|
14
|
+
if FreshConnection::SlaveConnection.slave_access?
|
15
|
+
change_connection {columns_without_slave_cluster(*args)}
|
16
|
+
else
|
17
|
+
columns_without_slave_cluster(*args)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
alias_method_chain :columns, :slave_cluster
|
21
|
+
|
22
|
+
def log_info_with_slave_cluster(sql, name, ms)
|
23
|
+
name = "[MASTER] " + (name || "SQL") if !FreshConnection::SlaveConnection.slave_access? && name != "CACHE"
|
24
|
+
log_info_without_slave_cluster(sql, name, ms)
|
25
|
+
end
|
26
|
+
alias_method_chain :log_info, :slave_cluster
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def change_connection
|
31
|
+
master_con, @connection = @connection, FreshConnection::SlaveConnection.connection.raw_connection
|
32
|
+
yield
|
33
|
+
ensure
|
34
|
+
@connection = master_con
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class Base
|
40
|
+
class << self
|
41
|
+
def find_every_with_slave_cluster(options)
|
42
|
+
run_on_db(options) { find_every_without_slave_cluster(options) }
|
43
|
+
end
|
44
|
+
alias_method_chain :find_every, :slave_cluster
|
45
|
+
|
46
|
+
def find_by_sql_with_slave_cluster(sql, options = nil)
|
47
|
+
run_on_db(options) { find_by_sql_without_slave_cluster(sql) }
|
48
|
+
end
|
49
|
+
alias_method_chain :find_by_sql, :slave_cluster
|
50
|
+
|
51
|
+
def count_by_sql_with_slave_cluster(sql, options = nil)
|
52
|
+
run_on_db(options) { count_by_sql_without_slave_cluster(sql) }
|
53
|
+
end
|
54
|
+
alias_method_chain :count_by_sql, :slave_cluster
|
55
|
+
|
56
|
+
def calculate_with_slave_cluster(operation, column_name, options = {})
|
57
|
+
run_on_db(options) do
|
58
|
+
options.delete(:readonly)
|
59
|
+
calculate_without_slave_cluster(operation, column_name, options)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
alias_method_chain :calculate, :slave_cluster
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def run_on_db(options)
|
67
|
+
in_run_on_db[:go_slave] = go_slave?(options) if in_run_on_db[:count] == 0
|
68
|
+
in_run_on_db[:count] += 1
|
69
|
+
|
70
|
+
in_run_on_db[:go_slave] ? run_on_readonly_db{yield} : yield
|
71
|
+
ensure
|
72
|
+
in_run_on_db[:count] -= 1
|
73
|
+
end
|
74
|
+
|
75
|
+
def in_run_on_db
|
76
|
+
Thread.current[:in_run_on_db] ||= {:count => 0, :go_slave => false}
|
77
|
+
end
|
78
|
+
|
79
|
+
def go_slave?(options)
|
80
|
+
connection.open_transactions == 0 && (!options.is_a?(Hash) || !options.key?(:readonly) || options[:readonly].nil? || options[:readonly])
|
81
|
+
end
|
82
|
+
|
83
|
+
def run_on_readonly_db
|
84
|
+
FreshConnection::SlaveConnection.slave_access_in
|
85
|
+
yield
|
86
|
+
ensure
|
87
|
+
FreshConnection::SlaveConnection.shift_slave_pointer
|
88
|
+
FreshConnection::SlaveConnection.slave_access_out
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
metadata
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fresh_connection
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Tsukasa OISHI
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-10-16 00:00:00 Z
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: MiyazakiResistance is a library like ActiveRecord to use Tokyo Tyrant.
|
22
|
+
email:
|
23
|
+
- tsukasa.oishi@gmail.com
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files:
|
29
|
+
- History.txt
|
30
|
+
- Manifest.txt
|
31
|
+
- README.rdoc
|
32
|
+
files:
|
33
|
+
- Rakefile
|
34
|
+
- fresh_connection.gemspec
|
35
|
+
- lib/fresh_connection.rb
|
36
|
+
- lib/fresh_connection/slave_connection.rb
|
37
|
+
- lib/fresh_connection/rack/connection_management.rb
|
38
|
+
- rails/initializers/active_record_base.rb
|
39
|
+
- History.txt
|
40
|
+
- Manifest.txt
|
41
|
+
- README.rdoc
|
42
|
+
homepage: http://www.kaeruspoon.net/keywords/MiyazakiResistance
|
43
|
+
licenses: []
|
44
|
+
|
45
|
+
post_install_message: PostInstall.txt
|
46
|
+
rdoc_options:
|
47
|
+
- --main
|
48
|
+
- README.rdoc
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
hash: 3
|
57
|
+
segments:
|
58
|
+
- 0
|
59
|
+
version: "0"
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
hash: 3
|
66
|
+
segments:
|
67
|
+
- 0
|
68
|
+
version: "0"
|
69
|
+
requirements: []
|
70
|
+
|
71
|
+
rubyforge_project: miyazakiresistance
|
72
|
+
rubygems_version: 1.7.2
|
73
|
+
signing_key:
|
74
|
+
specification_version: 3
|
75
|
+
summary: MiyazakiResistance is a library like ActiveRecord to use Tokyo Tyrant.
|
76
|
+
test_files: []
|
77
|
+
|