active_record_slave 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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