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 ADDED
@@ -0,0 +1,4 @@
1
+ == 0.0.1 2011-10-16
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
data/Manifest.txt ADDED
@@ -0,0 +1,9 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README
4
+ Rakefile
5
+ fresh_connection.gemspec
6
+ lib/fresh_connection.rb
7
+ lib/fresh_connection/slave_connection.rb
8
+ lib/fresh_connection/rack/connection_management.rb
9
+ rails/initializers/active_record_base.rb
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,8 @@
1
+ module FreshConnection
2
+ VERSION = "0.0.1"
3
+ end
4
+
5
+ require "fresh_connection/slave_connection"
6
+ require "fresh_connection/rack/connection_management"
7
+
8
+ Dir.glob("#{File.join(File.dirname(__FILE__), "../rails/initializers")}/*.rb").each{|path| require path}
@@ -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
+