fresh_connection 0.1.8 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/Appraisals +17 -0
  4. data/README.md +72 -8
  5. data/Rakefile +5 -0
  6. data/fresh_connection.gemspec +6 -1
  7. data/gemfiles/rails3.gemfile +9 -0
  8. data/gemfiles/rails40.gemfile +9 -0
  9. data/gemfiles/rails41.gemfile +9 -0
  10. data/lib/fresh_connection.rb +39 -9
  11. data/lib/fresh_connection/abstract_connection_manager.rb +4 -1
  12. data/lib/fresh_connection/access_control.rb +56 -0
  13. data/lib/fresh_connection/connection_manager.rb +2 -2
  14. data/lib/fresh_connection/extend/ar_base.rb +47 -0
  15. data/lib/fresh_connection/extend/ar_relation.rb +44 -0
  16. data/lib/fresh_connection/extend/connection_handler.rb +16 -0
  17. data/lib/fresh_connection/extend/mysql2_adapter.rb +46 -0
  18. data/lib/fresh_connection/initializer.rb +35 -0
  19. data/lib/fresh_connection/rack/connection_management.rb +1 -1
  20. data/lib/fresh_connection/railtie.rb +7 -4
  21. data/lib/fresh_connection/slave_connection.rb +29 -65
  22. data/lib/fresh_connection/slave_connection_handler.rb +43 -0
  23. data/lib/fresh_connection/version.rb +1 -1
  24. data/log/.gitkeep +0 -0
  25. data/spec/database.yml +14 -0
  26. data/spec/db_schema.sql +291 -0
  27. data/spec/prepare.rb +33 -0
  28. data/spec/spec_helper.rb +8 -0
  29. data/spec/support/active_record_logger.rb +3 -0
  30. data/spec/unit/access_control_spec.rb +63 -0
  31. data/spec/unit/ar_base_spec.rb +42 -0
  32. data/spec/unit/fresh_connection_spec.rb +106 -0
  33. data/spec/unit/recovery_spec.rb +39 -0
  34. metadata +154 -81
  35. data/lib/fresh_connection/active_record/abstract_adapter.rb +0 -35
  36. data/lib/fresh_connection/active_record/mysql2_adapter.rb +0 -14
  37. data/lib/fresh_connection/active_record/relation.rb +0 -33
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 145cd233562347272db93ca9a321d741c2d20228
4
+ data.tar.gz: 47b006e8bb8ac186726d6b71decbb337bd9ebd93
5
+ SHA512:
6
+ metadata.gz: faf4ef73727580b787ee41e7b2d7ceea4181687fb6ac9a4343272e32341ab7c36a6d431a471b87172748a48595af33bd00b6e33eed30ebeb4620e43bca0c63ba
7
+ data.tar.gz: 09e031f971560f1a235bb83adf3b679621671f9c308de92d8bfc98ea100222f3ea9477b51b8ff4726abb76be0b313bc1e1c0a51da474922c8d832caf12521c0e
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ *.gem
2
+ .bundle
3
+ .ruby-version
4
+ Gemfile.lock
5
+ gemfiles/*.lock
6
+ bundle
7
+ log/*
8
+ .*.sw[a-z]
data/Appraisals ADDED
@@ -0,0 +1,17 @@
1
+ appraise "rails3" do
2
+ gem "activerecord", "~> 3.2.0"
3
+ gem "activesupport", "~> 3.2.0"
4
+ gem "railties", "~> 3.2.0"
5
+ end
6
+
7
+ appraise "rails40" do
8
+ gem "activerecord", "~> 4.0.0"
9
+ gem "activesupport", "~> 4.0.0"
10
+ gem "railties", "~> 4.0.0"
11
+ end
12
+
13
+ appraise "rails41" do
14
+ gem "activerecord", "~> 4.1.0"
15
+ gem "activesupport", "~> 4.1.0"
16
+ gem "railties", "~> 4.1.0"
17
+ end
data/README.md CHANGED
@@ -18,9 +18,6 @@ Or install it yourself as:
18
18
 
19
19
  $ gem install fresh_connection
20
20
 
21
- ### For Rails2.3
22
-
23
- $ gem install fresh_connection -v 0.0.7
24
21
 
25
22
  ## Config
26
23
  ### config/database.yml
@@ -41,14 +38,62 @@ Or install it yourself as:
41
38
  password: slave
42
39
  host: slave
43
40
 
44
- slave is config to connect to slave servers.
41
+ ```slave``` is config to connect to slave servers.
45
42
  Others will use the master setting. If you want to change, write in the slave.
46
43
 
47
- ### config/initializers/fresh_connection.rb
44
+ ### use multiple slave servers group
45
+ If you may want to user multiple slave group, write multiple slave group to config/database.yml.
46
+
47
+ production:
48
+ adapter: mysql2
49
+ encoding: utf8
50
+ reconnect: true
51
+ database: kaeru
52
+ pool: 5
53
+ username: master
54
+ password: master
55
+ host: localhost
56
+ socket: /var/run/mysqld/mysqld.sock
57
+
58
+ slave:
59
+ username: slave
60
+ password: slave
61
+ host: slave
62
+
63
+ admin_slave:
64
+ username: slave
65
+ password: slave
66
+ host: admin_slaves
67
+
68
+ And call establish_fresh_connection method in model that access to ```admin_slave``` slave group.
69
+
70
+ class AdminUser < ActiveRecord::Base
71
+ establish_fresh_connection :admin_slave
72
+ end
73
+
74
+ The children is access to same slave group of parent.
75
+
76
+ class Parent < ActiveRecord::Base
77
+ establish_fresh_connection :admin_slave
78
+ end
79
+
80
+ class AdminUser < Parent
81
+ end
82
+
83
+ class Benefit < Parent
84
+ end
85
+
86
+ AdminUser and Benefit access to ```admin_slave``` slave group.
87
+
48
88
 
49
- FreshConnection::SlaveConnection.ignore_models = %w|Model1 Model2|
89
+ ### Declare model that doesn't use slave db
50
90
 
51
- If models that ignore access to slave servers is exist, You can write model name at FreshConnection::SlaveConnection.ignore models.
91
+ class SomethingModel < ActiveRecord::Base
92
+ master_db_only!
93
+ end
94
+
95
+ If model that always access to master servers is exist, You may want to write ```master_db_only!``` in model.
96
+ The model that master_db_only model's child is always access to master db.
52
97
 
53
98
  ### Slave Connection Manager
54
99
  Default slave connection manager is FreshConnection::ConnectionManager.
@@ -56,7 +101,7 @@ If you would like to change slave connection manager, assign yourself slave conn
56
101
 
57
102
  #### config/initializers/fresh_connection.rb
58
103
 
59
- FreshConnection::SlaveConnection.connection_manager = MySlaveConnection
104
+ FreshConnection.connection_manager = MySlaveConnection
60
105
 
61
106
 
62
107
  Yourself Slave Connection Manager should be inherited FreshConnection::AbstractConnectionManager
@@ -99,3 +144,22 @@ In transaction, Always will be access to master server.
99
144
  3. Commit your changes (`git commit -am 'Add some feature'`)
100
145
  4. Push to the branch (`git push origin my-new-feature`)
101
146
  5. Create new Pull Request
147
+
148
+ ## Test
149
+
150
+ I'm glad that you would do test!
151
+ To run the test suite, you need mysql installed.
152
+ How to setup your test environment.
153
+
154
+ ```bash
155
+ bundle install --path bundle
156
+ GEM_HOME=bundle/ruby/(your ruby version) gem install bundler --pre
157
+ bundle exec appraisal install
158
+ ```
159
+
160
+ This command run the spec suite for all rails versions supported.
161
+
162
+ ```base
163
+ bundle exec appraisal rake spec
164
+ ```
165
+
data/Rakefile CHANGED
@@ -1 +1,6 @@
1
1
  require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -19,7 +19,12 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.add_dependency 'activerecord', '>= 3.2.0'
22
+ spec.add_dependency 'activesupport', '>= 3.2.0'
23
+ spec.add_dependency 'railties', '>= 3.2.0'
22
24
 
23
25
  spec.add_development_dependency "bundler", "~> 1.3"
24
- spec.add_development_dependency "rake"
26
+ spec.add_development_dependency "rake", '>= 10.0.0'
27
+ spec.add_development_dependency "rspec", '>= 2.14.1'
28
+ spec.add_development_dependency 'mysql2', '>= 0.3.15'
29
+ spec.add_development_dependency 'appraisal', '>= 1.0.0'
25
30
  end
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 3.2.0"
6
+ gem "activesupport", "~> 3.2.0"
7
+ gem "railties", "~> 3.2.0"
8
+
9
+ gemspec :path => "../"
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 4.0.0"
6
+ gem "activesupport", "~> 4.0.0"
7
+ gem "railties", "~> 4.0.0"
8
+
9
+ gemspec :path => "../"
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 4.1.0"
6
+ gem "activesupport", "~> 4.1.0"
7
+ gem "railties", "~> 4.1.0"
8
+
9
+ gemspec :path => "../"
@@ -1,9 +1,39 @@
1
- require "fresh_connection/version"
2
- require "fresh_connection/connection_manager"
3
- require "fresh_connection/slave_connection"
4
- require "fresh_connection/rack/connection_management"
5
- require "fresh_connection/active_record/relation"
6
- require "fresh_connection/active_record/abstract_adapter"
7
- require "fresh_connection/active_record/mysql2_adapter"
8
-
9
- require "fresh_connection/railtie.rb" if defined?(Rails)
1
+ require 'active_record'
2
+ require 'active_support/core_ext'
3
+ require 'active_support/dependencies/autoload'
4
+
5
+ module FreshConnection
6
+ extend ActiveSupport::Autoload
7
+
8
+ autoload :AccessControl
9
+ autoload :ConnectionManager
10
+ autoload :SlaveConnectionHandler
11
+ autoload :Initializer
12
+ autoload :SlaveConnection
13
+
14
+ class << self
15
+ attr_writer :connection_manager, :ignore_configure_connection, :retry_limit
16
+
17
+ def connection_manager
18
+ @connection_manager || ConnectionManager
19
+ end
20
+
21
+ def ignore_configure_connection?
22
+ !!@ignore_configure_connection
23
+ end
24
+
25
+ def retry_limit
26
+ @retry_limit || 3
27
+ end
28
+
29
+ def rails_3?
30
+ ActiveRecord::VERSION::MAJOR == 3
31
+ end
32
+
33
+ def rails_4?
34
+ ActiveRecord::VERSION::MAJOR == 4
35
+ end
36
+ end
37
+ end
38
+
39
+ require "fresh_connection/railtie.rb"
@@ -6,8 +6,11 @@ module FreshConnection
6
6
  "Can't connect to local MySQL server"
7
7
  ].map{|msg| Regexp.escape(msg)}.join("|")
8
8
 
9
- def initialize
9
+ attr_reader :slave_group
10
+
11
+ def initialize(slave_group = "slave")
10
12
  @mutex = Mutex.new
13
+ @slave_group = (slave_group.presence || "slave").to_s
11
14
  end
12
15
 
13
16
  def slave_connection
@@ -0,0 +1,56 @@
1
+ module FreshConnection
2
+ class AccessControl
3
+ class << self
4
+ def force_master_access
5
+ db = access_db
6
+ access_to(:master)
7
+ yield
8
+ ensure
9
+ access_to(db)
10
+ end
11
+
12
+ def access(enable_slave_access)
13
+ access_in(enable_slave_access ? :slave : :master)
14
+ yield
15
+ ensure
16
+ access_out
17
+ end
18
+
19
+ def slave_access?
20
+ access_db == :slave
21
+ end
22
+
23
+ private
24
+
25
+ def access_in(db)
26
+ increment_access_count
27
+ access_to(db) unless access_db
28
+ end
29
+
30
+ def access_out
31
+ decrement_access_count
32
+ access_to(nil) if access_count == 0
33
+ end
34
+
35
+ def access_db
36
+ Thread.current[:fresh_connection_access_target]
37
+ end
38
+
39
+ def access_to(db)
40
+ Thread.current[:fresh_connection_access_target] = db
41
+ end
42
+
43
+ def access_count
44
+ Thread.current['fresh_connection_access_count'] || 0
45
+ end
46
+
47
+ def increment_access_count
48
+ Thread.current['fresh_connection_access_count'] = access_count + 1
49
+ end
50
+
51
+ def decrement_access_count
52
+ Thread.current['fresh_connection_access_count'] = access_count - 1
53
+ end
54
+ end
55
+ end
56
+ end
@@ -36,7 +36,7 @@ module FreshConnection
36
36
  end
37
37
 
38
38
  def new_connection
39
- ActiveRecord::Base.send("#{spec["adapter"]}_connection", spec)
39
+ ActiveRecord::Base.send("mysql2_connection", spec)
40
40
  end
41
41
 
42
42
  def spec
@@ -45,7 +45,7 @@ module FreshConnection
45
45
 
46
46
  def get_spec
47
47
  ret = ActiveRecord::Base.configurations[Rails.env]
48
- ret.merge(ret["slave"] || {})
48
+ ret.merge(ret[@slave_group] || {})
49
49
  end
50
50
  end
51
51
  end
@@ -0,0 +1,47 @@
1
+ module FreshConnection
2
+ module Extend
3
+ module ArBase
4
+ def self.extended(base)
5
+ base.class_attribute :slave_connection_handler, :instance_writer => false
6
+ base.slave_connection_handler = FreshConnection::SlaveConnectionHandler.new
7
+ end
8
+
9
+ def manage_access(slave_access, &block)
10
+ if master_db_only?
11
+ FreshConnection::AccessControl.force_master_access(&block)
12
+ else
13
+ FreshConnection::AccessControl.access(slave_access, &block)
14
+ end
15
+ end
16
+
17
+ def establish_fresh_connection(slave_group = nil)
18
+ slave_connection_handler.establish_connection(name, slave_group)
19
+ end
20
+
21
+ def slave_connection
22
+ slave_connection_handler.connection(self)
23
+ end
24
+
25
+ def master_db_only!
26
+ @_fresh_connection_master_only = true
27
+ end
28
+
29
+ def master_db_only?
30
+ @_fresh_connection_master_only ||
31
+ (self != ActiveRecord::Base && superclass.master_db_only?)
32
+ end
33
+
34
+ def put_aside!
35
+ slave_connection_handler.put_aside!
36
+ end
37
+
38
+ def recovery(failure_connection, exception)
39
+ slave_connection_handler.recovery(self, failure_connection, exception)
40
+ end
41
+
42
+ def slave_group
43
+ slave_connection_handler.slave_group(self)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,44 @@
1
+ module FreshConnection
2
+ module Extend
3
+ module ArRelation
4
+ def self.included(base)
5
+ base.alias_method_chain :exec_queries, :fresh_connection
6
+
7
+ if FreshConnection.rails_4?
8
+ base.__send__(:include, ForRails4)
9
+ elsif FreshConnection.rails_3?
10
+ base.__send__(:include, ForRails3)
11
+ end
12
+ end
13
+
14
+ def calculate(operation, column_name, options = {})
15
+ slave_access = enable_slave_access && options[:readonly] != false
16
+ @klass.manage_access(slave_access) { super }
17
+ end
18
+
19
+ private
20
+
21
+ def exec_queries_with_fresh_connection
22
+ return @records if loaded?
23
+
24
+ @klass.manage_access(enable_slave_access) do
25
+ exec_queries_without_fresh_connection
26
+ end
27
+ end
28
+
29
+ module ForRails4
30
+ private
31
+ def enable_slave_access
32
+ connection.open_transactions == 0 && (readonly_value.nil? || readonly_value)
33
+ end
34
+ end
35
+
36
+ module ForRails3
37
+ private
38
+ def enable_slave_access
39
+ connection.open_transactions == 0 && (@readonly_value.nil? || @readonly_value)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,16 @@
1
+ module FreshConnection
2
+ module Extend
3
+ module ConnectionHandler
4
+ def self.included(base)
5
+ base.alias_method_chain :retrieve_connection, :fresh_connection
6
+ end
7
+
8
+ def retrieve_connection_with_fresh_connection(klass)
9
+ c = retrieve_connection_without_fresh_connection(klass)
10
+ c.model_class = klass
11
+ c
12
+ end
13
+ end
14
+ end
15
+ end
16
+