fresh_connection 0.1.8 → 0.2.0

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