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 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
+