active_record_slave 1.0.0 → 1.1.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 85a394f332c93d140dda4319bd811a50332bbb3a
4
+ data.tar.gz: 9eb811c94d7d43de483569245d587530a7a4725f
5
+ SHA512:
6
+ metadata.gz: 00eb5c81081b4c205843f7a65aa7fef94f4e6614f35dfb662b0832bb2ec79813dd25fc50edb7aaa8cd612d545bf0eeb9053383d6653ee021d22fe702bce13402
7
+ data.tar.gz: 3e9ffeb4eb9cbe4bfea45ad2450af2c420a49b4095680a45baaa9e607d1fd49fed600d57f9b24be673da6698518eb75c36df0eed497d98251071605e3762f47f
data/README.md CHANGED
@@ -3,7 +3,7 @@ active_record_slave
3
3
 
4
4
  ActiveRecord drop-in solution to efficiently redirect reads to slave databases
5
5
 
6
- * http://github.com/ClarityServices/active_record_slave
6
+ * http://github.com/reidmorrison/active_record_slave
7
7
 
8
8
  ## Introduction
9
9
 
@@ -20,18 +20,24 @@ database to ensure data consistency.
20
20
  * Transaction aware. Detects when a query is inside of a transaction and sends
21
21
  those reads to the master
22
22
  * Lightweight footprint
23
- * No overhead when a slave is not configured
24
- * Minimal overhead when redirecting reads to the slave
23
+ * No overhead whatsoever when a slave is not configured
24
+ * Negligible overhead when redirecting reads to the slave
25
25
  * Connection Pools to both databases are retained and maintained independently by ActiveRecord
26
26
  * The master and slave databases do not have to be of the same type.
27
27
  For example one can be MySQL and the other Oracle if required.
28
- * Debug logs include 'Slave: ' prefix to indicate which SQL statements are going
28
+ * Debug logs include a prefix of 'Slave: ' to indicate which SQL statements are going
29
29
  to the slave database
30
30
 
31
31
  ### Example showing Slave redirected read
32
- r = Role.where(:name => "manager").first
33
- r.description = 'Manager'
34
- r.save!
32
+
33
+ ```ruby
34
+ # Read from the slave database
35
+ r = Role.where(:name => "manager").first
36
+ r.description = 'Manager'
37
+
38
+ # Save changes back to the master database
39
+ r.save!
40
+ ```
35
41
 
36
42
  Log file output:
37
43
 
@@ -39,11 +45,14 @@ Log file output:
39
45
  03-13-12 05:56:22 pm,[2608],b[0],[0], AREL (12.0ms) UPDATE `roles` SET `description` = 'Manager' WHERE `roles`.`id` = 5
40
46
 
41
47
  ### Example showing how reads within a transaction go to the master
42
- Role.transaction do
43
- r = Role.where(:name => "manager").first
44
- r.description = 'Manager'
45
- r.save!
46
- end
48
+
49
+ ```ruby
50
+ Role.transaction do
51
+ r = Role.where(:name => "manager").first
52
+ r.description = 'Manager'
53
+ r.save!
54
+ end
55
+ ```
47
56
 
48
57
  Log file output:
49
58
 
@@ -82,18 +91,16 @@ D, [2012-11-06T19:43:26.891667 #89002] DEBUG -- : SQL (0.4ms) DELETE FROM "us
82
91
  D, [2012-11-06T19:43:26.892697 #89002] DEBUG -- : (0.9ms) commit transaction
83
92
  ```
84
93
 
94
+ ## Dependencies
85
95
 
86
- ## Requirements
96
+ * Tested on Rails 3 and Rails 4
87
97
 
88
- * ActiveRecord 3 or greater (Rails 3 or greater)
89
-
90
- May also work with Rails 2. Anyone want to give it a try and let me know?
91
- Happy to make it work with Rails 2 if anyone needs it
98
+ See [.travis.yml](https://github.com/reidmorrison/active_record_slave/.travis.yml) for the list of tested Ruby platforms
92
99
 
93
100
  ## Note
94
101
 
95
- ActiveRecord::Base.execute is commonly used to perform custom SQL calls against
96
- the database that bypasses ActiveRecord. It is necessary to replace these calls
102
+ ActiveRecord::Base.execute is sometimes used to perform custom SQL calls against
103
+ the database to bypass ActiveRecord. It is necessary to replace these calls
97
104
  with the standard ActiveRecord::Base.select call for them to be picked up by
98
105
  active_record_slave and redirected to the slave.
99
106
 
@@ -111,62 +118,59 @@ along with all the usual ActiveRecord database configuration options.
111
118
 
112
119
  For Example:
113
120
 
114
- development:
115
- database: clarity_development
116
- username: root
117
- password:
118
- encoding: utf8
119
- adapter: mysql
120
- host: 127.0.0.1
121
- pool: 20
122
- slave:
123
- database: clarity_development_replica
124
- username: root
125
- password:
126
- encoding: utf8
127
- adapter: mysql
128
- host: 127.0.0.1
129
- pool: 20
121
+ ```yaml
122
+ development:
123
+ database: myapp_development
124
+ username: root
125
+ password:
126
+ encoding: utf8
127
+ adapter: mysql
128
+ host: 127.0.0.1
129
+ pool: 20
130
+ slave:
131
+ database: myapp_development_replica
132
+ username: root
133
+ password:
134
+ encoding: utf8
135
+ adapter: mysql
136
+ host: 127.0.0.1
137
+ pool: 20
138
+ ```
130
139
 
131
140
  Sometimes it is useful to turn on slave reads per host, for example to activate
132
141
  slave reads only on the linux host 'batch':
133
142
 
134
- development:
135
- database: clarity_development
136
- username: root
137
- password:
138
- encoding: utf8
139
- adapter: mysql
140
- host: 127.0.0.1
141
- pool: 20
142
- <% if `hostname`.strip == 'batch' %>
143
- slave:
144
- database: clarity_development_replica
145
- username: root
146
- password:
147
- encoding: utf8
148
- adapter: mysql
149
- host: 127.0.0.1
150
- pool: 20
151
- <% end %>
152
-
153
- ## Tests
154
-
155
- * active_record_slave is running in a large production system and was tested
156
- directly as part of that solution
157
- * I welcome anyone that can submit tests to verify this gem in a standalone outside
158
- environment
143
+ ```yaml
144
+ development:
145
+ database: myapp_development
146
+ username: root
147
+ password:
148
+ encoding: utf8
149
+ adapter: mysql
150
+ host: 127.0.0.1
151
+ pool: 20
152
+ <% if `hostname`.strip == 'batch' %>
153
+ slave:
154
+ database: myapp_development_replica
155
+ username: root
156
+ password:
157
+ encoding: utf8
158
+ adapter: mysql
159
+ host: 127.0.0.1
160
+ pool: 20
161
+ <% end %>
162
+ ```
159
163
 
160
164
  ## Possible Future Enhancements
161
165
 
162
- * Support for multiple slaves (ask for it by submitting a ticket)
166
+ * Support for multiple slaves (ask for it by submitting an issue)
163
167
 
164
168
  Meta
165
169
  ----
166
170
 
167
- * Code: `git clone git://github.com/ClarityServices/active_record_slave.git`
168
- * Home: <https://github.com/ClarityServices/active_record_slave>
169
- * Bugs: <https://github.com/ClarityServices/active_record_slave/issues>
171
+ * Code: `git clone git://github.com/reidmorrison/active_record_slave.git`
172
+ * Home: <https://github.com/reidmorrison/active_record_slave>
173
+ * Bugs: <https://github.com/reidmorrison/active_record_slave/issues>
170
174
  * Gems: <http://rubygems.org/gems/active_record_slave>
171
175
 
172
176
  This project uses [Semantic Versioning](http://semver.org/).
@@ -179,7 +183,7 @@ Reid Morrison :: reidmo@gmail.com :: @reidmorrison
179
183
  License
180
184
  -------
181
185
 
182
- Copyright 2012 Clarity Services, Inc.
186
+ Copyright 2012, 2013, 2014 Reid Morrison
183
187
 
184
188
  Licensed under the Apache License, Version 2.0 (the "License");
185
189
  you may not use this file except in compliance with the License.
data/Rakefile CHANGED
@@ -1,28 +1,18 @@
1
- lib = File.expand_path('../lib/', __FILE__)
2
- $:.unshift lib unless $:.include?(lib)
3
-
4
1
  require 'rake/clean'
5
2
  require 'rake/testtask'
6
- require 'date'
3
+
4
+ $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
7
5
  require 'active_record_slave/version'
8
6
 
9
- desc "Build gem"
10
- task :gem do |t|
11
- gemspec = Gem::Specification.new do |s|
12
- s.name = 'active_record_slave'
13
- s.version = ActiveRecordSlave::VERSION
14
- s.platform = Gem::Platform::RUBY
15
- s.authors = ['Reid Morrison']
16
- s.email = ['reidmo@gmail.com']
17
- s.homepage = 'https://github.com/ClarityServices/active_record_slave'
18
- s.date = Date.today.to_s
19
- s.summary = "ActiveRecord read from slave"
20
- s.description = "ActiveRecordSlave is a library to seamlessly enable reading from database slaves in a Rails 3 project, written in Ruby."
21
- s.files = FileList["./**/*"].exclude(/.gem$/, /.log$/,/nbproject/,/sqlite3$/).map{|f| f.sub(/^\.\//, '')}
22
- s.has_rdoc = true
23
- #s.add_dependency 'activerecord', '>= 3.0.0'
24
- end
25
- Gem::Builder.new(gemspec).build
7
+ task :gem do
8
+ system "gem build active_record_slave.gemspec"
9
+ end
10
+
11
+ task :publish => :gem do
12
+ system "git tag -a v#{ActiveRecordSlave::VERSION} -m 'Tagging #{ActiveRecordSlave::VERSION}'"
13
+ system "git push --tags"
14
+ system "gem push active_record_slave-#{ActiveRecordSlave::VERSION}.gem"
15
+ system "rm active_record_slave-#{ActiveRecordSlave::VERSION}.gem"
26
16
  end
27
17
 
28
18
  desc "Run Test Suite"
@@ -34,3 +24,5 @@ task :test do
34
24
 
35
25
  Rake::Task['functional'].invoke
36
26
  end
27
+
28
+ task :default => :test
@@ -34,14 +34,49 @@ module ActiveRecordSlave
34
34
  # Force reads for the supplied block to read from the master database
35
35
  # Only applies to calls made within the current thread
36
36
  def self.read_from_master
37
- # Set :master indicator in thread local storage so that it is visible
38
- # during the select call
39
- current = Thread.current[:active_record_slave]
40
- Thread.current[:active_record_slave] = :master
41
- yield
42
- ensure
43
- Thread.current[:active_record_slave] = current
37
+ return yield if read_from_master?
38
+ begin
39
+ # Set :master indicator in thread local storage so that it is visible
40
+ # during the select call
41
+ read_from_master!
42
+ yield
43
+ ensure
44
+ read_from_slave!
45
+ end
44
46
  end
45
47
 
46
- end
48
+ if RUBY_VERSION.to_i >= 2
49
+ # Fibers have their own thread local variables so use thread_variable_get
50
+
51
+ # Whether this thread is currently forcing all reads to go against the master database
52
+ def self.read_from_master?
53
+ Thread.current.thread_variable_get(:active_record_slave) == :master
54
+ end
47
55
 
56
+ # Force all subsequent reads on this thread and any fibers called by this thread to go the master
57
+ def self.read_from_master!
58
+ Thread.current.thread_variable_set(:active_record_slave, :master)
59
+ end
60
+
61
+ # Subsequent reads on this thread and any fibers called by this thread can go to a slave
62
+ def self.read_from_slave!
63
+ Thread.current.thread_variable_set(:active_record_slave, nil)
64
+ end
65
+ else
66
+ # Whether this thread is currently forcing all reads to go against the master database
67
+ def self.read_from_master?
68
+ Thread.current[:active_record_slave] == :master
69
+ end
70
+
71
+ # Force all subsequent reads on this thread and any fibers called by this thread to go the master
72
+ def self.read_from_master!
73
+ Thread.current[:active_record_slave] = :master
74
+ end
75
+
76
+ # Subsequent reads on this thread and any fibers called by this thread can go to a slave
77
+ def self.read_from_slave!
78
+ Thread.current[:active_record_slave] = nil
79
+ end
80
+ end
81
+
82
+ end
@@ -5,7 +5,7 @@ module ActiveRecordSlave
5
5
  # Replace #select with one that calls the slave connection instead
6
6
  def select_with_slave_reader(sql, name = nil, *args)
7
7
  # Only read from slave when not in a transaction and when this is not already the slave connection
8
- if (open_transactions == 0) && (Thread.current[:active_record_slave] != :master)
8
+ if (open_transactions == 0) && !ActiveRecordSlave.read_from_master?
9
9
  ActiveRecordSlave.read_from_master do
10
10
  Slave.connection.select(sql, "Slave: #{name || 'SQL'}", *args)
11
11
  end
@@ -1,3 +1,3 @@
1
1
  module ActiveRecordSlave #:nodoc
2
- VERSION = "1.0.0"
2
+ VERSION = "1.1.0"
3
3
  end
metadata CHANGED
@@ -1,62 +1,85 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_record_slave
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
5
- prerelease:
4
+ version: 1.1.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Reid Morrison
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-11-06 00:00:00.000000000 Z
13
- dependencies: []
14
- description: ActiveRecordSlave is a library to seamlessly enable reading from database
15
- slaves in a Rails 3 project, written in Ruby.
11
+ date: 2014-04-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sync_attr
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: thread_safe
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.1.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 0.1.0
41
+ description:
16
42
  email:
17
43
  - reidmo@gmail.com
18
44
  executables: []
19
45
  extensions: []
20
46
  extra_rdoc_files: []
21
47
  files:
22
- - Gemfile
23
- - Gemfile.lock
48
+ - LICENSE.txt
49
+ - README.md
50
+ - Rakefile
51
+ - lib/active_record_slave.rb
24
52
  - lib/active_record_slave/active_record_slave.rb
25
53
  - lib/active_record_slave/instance_methods.rb
26
54
  - lib/active_record_slave/railtie.rb
27
55
  - lib/active_record_slave/slave.rb
28
56
  - lib/active_record_slave/version.rb
29
- - lib/active_record_slave.rb
30
- - LICENSE.txt
31
- - Rakefile
32
- - README.md
33
57
  - test/active_record_slave_test.rb
34
58
  - test/database.yml
35
- homepage: https://github.com/ClarityServices/active_record_slave
36
- licenses: []
59
+ homepage: https://github.com/reidmorrison/active_record_slave
60
+ licenses:
61
+ - Apache License V2.0
62
+ metadata: {}
37
63
  post_install_message:
38
64
  rdoc_options: []
39
65
  require_paths:
40
66
  - lib
41
67
  required_ruby_version: !ruby/object:Gem::Requirement
42
- none: false
43
68
  requirements:
44
- - - ! '>='
69
+ - - ">="
45
70
  - !ruby/object:Gem::Version
46
71
  version: '0'
47
- segments:
48
- - 0
49
- hash: -2259159571471524106
50
72
  required_rubygems_version: !ruby/object:Gem::Requirement
51
- none: false
52
73
  requirements:
53
- - - ! '>='
74
+ - - ">="
54
75
  - !ruby/object:Gem::Version
55
76
  version: '0'
56
77
  requirements: []
57
78
  rubyforge_project:
58
- rubygems_version: 1.8.24
79
+ rubygems_version: 2.2.2
59
80
  signing_key:
60
- specification_version: 3
61
- summary: ActiveRecord read from slave
62
- test_files: []
81
+ specification_version: 4
82
+ summary: ActiveRecord drop-in solution to efficiently redirect reads to slave databases
83
+ test_files:
84
+ - test/active_record_slave_test.rb
85
+ - test/database.yml
data/Gemfile DELETED
@@ -1,15 +0,0 @@
1
- source :rubygems
2
-
3
- group :test do
4
- gem "shoulda"
5
- end
6
-
7
- gem "activerecord"
8
- platforms :ruby do
9
- gem 'sqlite3'
10
- end
11
-
12
- platforms :jruby do
13
- gem 'jdbc-sqlite3'
14
- gem 'activerecord-jdbcsqlite3-adapter'
15
- end
data/Gemfile.lock DELETED
@@ -1,34 +0,0 @@
1
- GEM
2
- remote: http://rubygems.org/
3
- specs:
4
- activemodel (3.2.8)
5
- activesupport (= 3.2.8)
6
- builder (~> 3.0.0)
7
- activerecord (3.2.8)
8
- activemodel (= 3.2.8)
9
- activesupport (= 3.2.8)
10
- arel (~> 3.0.2)
11
- tzinfo (~> 0.3.29)
12
- activesupport (3.2.8)
13
- i18n (~> 0.6)
14
- multi_json (~> 1.0)
15
- arel (3.0.2)
16
- builder (3.0.4)
17
- i18n (0.6.1)
18
- multi_json (1.3.7)
19
- shoulda (3.3.2)
20
- shoulda-context (~> 1.0.1)
21
- shoulda-matchers (~> 1.4.1)
22
- shoulda-context (1.0.1)
23
- shoulda-matchers (1.4.1)
24
- activesupport (>= 3.0.0)
25
- sqlite3 (1.3.6)
26
- tzinfo (0.3.35)
27
-
28
- PLATFORMS
29
- ruby
30
-
31
- DEPENDENCIES
32
- activerecord
33
- shoulda
34
- sqlite3