fresh_connection 0.0.1
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/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
|
+
|