fresh_connection 2.3.0 → 2.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5209747ee5ebffd0721b5c2dbcf20940721867c4
4
- data.tar.gz: 022c9dce7d03c2ca0bc0732022ea70099df547df
3
+ metadata.gz: deb0b581ea9aecc3c27b3d32a6a14c93ee3a543f
4
+ data.tar.gz: 0323e5cd09c34895529a49d339c00797c45cfa07
5
5
  SHA512:
6
- metadata.gz: 700f794d9cb774036025d53b6cf471ffd83c8a9de8b1188c1d0bf153e292c98c69804473a1e20bf535e18fc4be771d264585db463affeeda1b7b2303942aa868
7
- data.tar.gz: d4f6fbea8f6debe411aa22f603a3e463bc03986dbefd05ded39374fe645641811e862814f09c34de2d38881ceb6e946ab8b4b8cdaed638bbf226337c0bbb0505
6
+ metadata.gz: 5163c26ac8a920f5b5f3485d4049b147ae826075ba8cfb6c2b1f6adf2b3e9966c046304932851c2b0b64228892c08569c2b5d81d7ef0bd21ae5db025e2f28bbc
7
+ data.tar.gz: 71e16efe75624fd56cf02374bb8db2fa5e94c3038c3b044dbb8df1ba689ed797f1e5254bb65fd31a9d4276125c876f13edb3db4d5dc8142ce002ae5ec18f125d
data/README.md CHANGED
@@ -4,16 +4,16 @@
4
4
  **FreshConnection** provides access to one or more configured database replicas.
5
5
 
6
6
  For example:
7
- ```
7
+
8
+ ```text
8
9
  Rails ------------ DB Master
9
10
  |
10
11
  +---- DB Replica
11
-
12
12
  ```
13
13
 
14
- or:
14
+ or
15
15
 
16
- ```
16
+ ```text
17
17
  Rails -------+---- DB Master
18
18
  |
19
19
  | +------ DB Replica1
@@ -23,16 +23,13 @@ Rails -------+---- DB Master
23
23
  +------ DB Replica2
24
24
  ```
25
25
 
26
- FreshConnction connects one or more configured DB replicas, or with multiple
27
- replicas behind a DB query load balancer.
26
+ FreshConnction connects one or more configured DB replicas, or with multiple replicas behind a DB query load balancer.
28
27
 
29
28
  - Read queries go to the DB replica.
30
29
  - Write queries go to the DB master.
31
30
  - Within a transaction, all queries go to the DB master.
32
31
 
33
- If you wish to use multiple DB replicas on any given connection but not have
34
- a load balancer (such as [`pgbouncer`](https://pgbouncer.github.io) for Posgres
35
- databases), you can use [EbisuConnection](https://github.com/tsukasaoishi/ebisu_connection).
32
+ If you wish to use multiple DB replicas on any given connection but do not have a load balancer (such as [`pgbouncer`](https://pgbouncer.github.io) for Posgres databases), you can use [EbisuConnection](https://github.com/tsukasaoishi/ebisu_connection).
36
33
 
37
34
  ## Usage
38
35
  ### Access to the DB Replica
@@ -46,7 +43,8 @@ Account.count
46
43
 
47
44
  ### Access to the DB Master
48
45
  If you wish to ensure that queries are directed to the DB master, call `read_master`.
49
- Before version 0.4.3, `readonly(false)` must be used.
46
+
47
+ > Note: Before version 0.4.3, `readonly(false)` must be used.
50
48
 
51
49
  ```ruby
52
50
  Article.where(id: 1).read_master
@@ -73,9 +71,10 @@ old_article.destroy
73
71
  ```
74
72
 
75
73
  ## ActiveRecord Versions Supported
76
- FreshConnection supports ActiveRecord version 4.2 or later.
77
- If you are using Rails 4.1 or 4.0, you can use FreshConnection version 2.1.2 or before.
78
- If you are using Rails 3.2, you can use FreshConnection version 1.0.0 or before.
74
+
75
+ - FreshConnection supports ActiveRecord version 4.2 or later.
76
+ - If you are using Rails 4.1 or 4.0, you can use FreshConnection version 2.1.2 or before.
77
+ - If you are using Rails 3.2, you can use FreshConnection version 1.0.0 or before.
79
78
 
80
79
  ## Databases Supported
81
80
  FreshConnection currently supports MySQL and PostgreSQL.
@@ -100,9 +99,8 @@ $ gem install fresh_connection
100
99
  ```
101
100
 
102
101
  ### Variant Installation For Use With Some Other ActiveRecord Gems
103
- If you are using NewRelic or other gems that insert themselves into the
104
- ActiveRecord call-chain using `method_alias`, then a slight variation on the
105
- installation and configuration is required.
102
+
103
+ If you are using NewRelic or other gems that insert themselves into the ActiveRecord call-chain using `method_alias`, then a slight variation on the installation and configuration is required.
106
104
 
107
105
  In the `Gemfile`, use:
108
106
 
@@ -154,12 +152,10 @@ production:
154
152
  host: <%= ENV['DB_REPLICA_HOST'] %>
155
153
  ```
156
154
 
157
- `replica` is the configuration used for connecting read-only queries to the
158
- database replica. All other connections will use the database master settings.
155
+ `replica` is the configuration used for connecting read-only queries to the database replica. All other connections will use the database master settings.
159
156
 
160
157
  ### Multiple DB Replicas
161
- If you want to use multiple configured DB replicas, the configuration can
162
- contain multiple `replica` stanzas in the configuration file `config/database.yml`.
158
+ If you want to use multiple configured DB replicas, the configuration can contain multiple `replica` stanzas in the configuration file `config/database.yml`.
163
159
 
164
160
  For example:
165
161
 
@@ -186,9 +182,7 @@ production:
186
182
  host: <%= ENV['DB_ADMIN_REPLICA_HOST'] %>
187
183
  ```
188
184
 
189
- The custom replica stanza can then be applied as an argument to the
190
- `establish_fresh_connection` method in the models that should use it. For
191
- example:
185
+ The custom replica stanza can then be applied as an argument to the `establish_fresh_connection` method in the models that should use it. For example:
192
186
 
193
187
  ```ruby
194
188
  class AdminUser < ActiveRecord::Base
@@ -196,8 +190,7 @@ class AdminUser < ActiveRecord::Base
196
190
  end
197
191
  ```
198
192
 
199
- The child (sub) classes of the configured model will inherit the same access
200
- as the parent class. Example:
193
+ The child (sub) classes of the configured model will inherit the same access as the parent class. Example:
201
194
 
202
195
  ```ruby
203
196
  class AdminBase < ActiveRecord::Base
@@ -214,18 +207,54 @@ class Customer < ActiveRecord::Base
214
207
  end
215
208
  ```
216
209
 
217
- The `AdminUser` and `Benefit` models will access the database configured for
218
- the `admin_replica` group.
210
+ The `AdminUser` and `Benefit` models will access the database configured for the `admin_replica` group.
211
+
212
+ The `Customer` model will use the default connections: read-only queries will connect to the standard DB replica, and state-changing queries will connect to the DB master.
213
+
214
+ ### Replica Configuration With Environment Variables
215
+
216
+
217
+ Alternative to using a configuration in the `database.yml` file, it is possible to completely specify the replica access components using environment variables.
218
+
219
+ The environment variables corresponding to the `:replica` group are `DATABASE_REPLICA_URL` and `DATABASE_REPLICA1_URL`, with the latter being used only if the former is not defined.
220
+
221
+ The URL value is a [URL](https://en.wikipedia.org/wiki/URL) string with the following components:
222
+
223
+ _adapter://dbuser:dbpass@dbhost:dbport/dbname?querypath_
224
+
225
+ where the components are:
226
+
227
+ | Component | Purpose | Examples |
228
+ | --------- | ------- | --------- |
229
+ | _adapter_ | Declares the database adapter | `mysql`, `postgresql` |
230
+ | _dbuser_ | The login for the database | `root`, `postgres` |
231
+ | _dbpass_ | The password for the database | |
232
+ | _dbhost_ | The hostname for the database | `db1`, `localhost` |
233
+ | _dbport_ | The port for the database connection | `3306`, `5432` |
234
+ | _dbname_ | The name of the database | `mydb`, `appdb`, etc. |
235
+ | _querypath_ | One or more variable assignments: _var1=val1_, separated by '&' | `pools=5&reconnect=true` |
236
+
237
+
238
+ For example, here is a URL appropriate for a connection to a test database on the local host, on a custom port:
239
+
240
+ export DATABASE_REPLICA_URL='postgresql://root:somepw@localhost:6432/test_db?pool=5&reconnect=true
241
+
242
+ ### Multiple Replica Environment Variables
243
+
244
+ To specific URLs for multiple replicas, replace the string `REPLICA` in the environment variable name with the replica name, in upper case. See the examples for replicas: `:replica1`, `:replica2`, and `:admin_replica`
245
+
246
+
247
+ ENVIRONMENT_REPLICA1_URL='mysql://localhost/dbreplica1?pool=5&reconnect=true'
248
+ ENVIRONMENT_REPLICA2_URL='postgresql://localhost:6432/ro_db?pool=5&reconnect=true'
249
+ ENVIRONMENT_ADMIN_REPLICA_URL='postgresql://localhost:6432/admin_db?pool=5&reconnect=true'
219
250
 
220
- The `Customer` model will use the default connections: read-only queries will
221
- connect to the standard DB replica, and state-changing queries will connect to
222
- the DB master.
251
+ ### Special-Case Replica URL
223
252
 
253
+ When `:replica` is used but `DATABASE_REPLICA_URL` is *not* defined, then the value of `DATABASE_REPLICA1_URL` will be used if it is defined.
224
254
 
225
255
  ### Master-only Models
226
256
 
227
- It is possible to declare that specific models always use the DB master for all connections, using
228
- the `master_db_only!` method:
257
+ It is possible to declare that specific models always use the DB master for all connections, using the `master_db_only!` method:
229
258
 
230
259
  ```ruby
231
260
  class CustomerState < ActiveRecord::Base
@@ -237,9 +266,7 @@ All queries generated by methods on the `CustomerState` model will be directed t
237
266
 
238
267
  ### Using FreshConnection With Unicorn
239
268
 
240
- When using FreshConnection with Unicorn (or any other multi-processing web
241
- server which restarts processes on the fly), connection management needs
242
- special attention during startup:
269
+ When using FreshConnection with Unicorn (or any other multi-processing web server which restarts processes on the fly), connection management needs special attention during startup:
243
270
 
244
271
  ```ruby
245
272
  before_fork do |server, worker|
@@ -256,9 +283,7 @@ end
256
283
  ```
257
284
 
258
285
  ### Replica Connection Manager
259
- The default replica connection manager is `FreshConnection::ConnectionManager`.
260
- If an alternative (custom) replica connection manager is desired, this can be done
261
- with a simple assignment within a Rails initializer:
286
+ The default replica connection manager is `FreshConnection::ConnectionManager`. If an alternative (custom) replica connection manager is desired, this can be done with a simple assignment within a Rails initializer:
262
287
 
263
288
  `config/initializers/fresh_connection.rb`:
264
289
 
@@ -266,8 +291,7 @@ with a simple assignment within a Rails initializer:
266
291
  FreshConnection.connection_manager = MyOwnReplicaConnection
267
292
  ```
268
293
 
269
- The `MyOwnReplicaConnection` class should inherit from
270
- `FreshConnection::AbstractConnectionManager`, which has this interface:
294
+ The `MyOwnReplicaConnection` class should inherit from `FreshConnection::AbstractConnectionManager`, which has this interface:
271
295
 
272
296
  ```ruby
273
297
  class MyOwnReplicaConnection < FreshConnection::AbstractConnectionManager
@@ -305,12 +329,12 @@ end
305
329
 
306
330
  ## Test
307
331
 
308
- I'm glad that you would test!
309
- To run the test suite, `mysql` must be installed.
332
+ I'm glad that you would like to test!
333
+ To run the test suite, both `mysql` and `postgresql` must be installed.
310
334
 
311
335
  ### Test Configuration
312
336
 
313
- First, configure the test `mysql` server in `spec/database.yml`.
337
+ First, configure the test servers in `test/config/*.yml`
314
338
 
315
339
  Then, run:
316
340
 
data/Rakefile CHANGED
@@ -1,18 +1,20 @@
1
1
  require "bundler/gem_tasks"
2
2
  require 'rake/testtask'
3
3
 
4
- desc 'Run mysql2 and postgresql test'
4
+ desc 'Run mysql2 and postgresql tests'
5
5
  task :test do
6
6
  Rake::Task["test:mysql2"].invoke
7
+ Rake::Task["test:mysql2_url"].invoke
7
8
  Rake::Task["test:postgresql"].invoke
9
+ Rake::Task["test:postgresql_url"].invoke
8
10
  end
9
11
 
10
12
  namespace :test do
11
- %w(mysql2 postgresql).each do |db|
12
- Rake::TestTask.new(db) do |t|
13
+ %w(mysql2 postgresql mysql2_url postgresql_url).each do |test_name|
14
+ Rake::TestTask.new(test_name) do |t|
13
15
  t.libs << "test"
14
16
  t.libs << "lib"
15
- t.test_files = FileList["test/config/prepare_#{db}", 'test/**/*_test.rb']
17
+ t.test_files = FileList["test/config/prepare_#{test_name}", 'test/**/*_test.rb']
16
18
  t.verbose = true
17
19
  end
18
20
  end
@@ -7,7 +7,7 @@ module FreshConnection
7
7
  return unless name == :slave_connection
8
8
 
9
9
  ActiveSupport::Deprecation.warn(
10
- "'slave_connection' has been deprecated. use 'replica_connection' insted."
10
+ "'slave_connection' has been deprecated. use 'replica_connection' instead."
11
11
  )
12
12
  end
13
13
  end
@@ -22,7 +22,7 @@ module FreshConnection
22
22
 
23
23
  def slave_group
24
24
  ActiveSupport::Deprecation.warn(
25
- "'slave_group' is deprecated and will removed from version 2.4.0. use 'replica_group' insted."
25
+ "'slave_group' is deprecated and will removed from version 2.4.0. use 'replica_group' instead."
26
26
  )
27
27
 
28
28
  replica_group
@@ -1,11 +1,14 @@
1
1
  require 'active_support/deprecation'
2
2
  require 'active_support/core_ext/hash/keys'
3
+ require 'active_support/hash_with_indifferent_access'
4
+ require 'active_support/core_ext/hash/indifferent_access'
3
5
 
4
6
  module FreshConnection
5
7
  class ConnectionFactory
6
- def initialize(group, modify_spec = {})
8
+ def initialize(group, modify_spec = nil)
7
9
  @group = group.to_sym
8
- @modify_spec = modify_spec.symbolize_keys
10
+ @modify_spec = modify_spec || {}.with_indifferent_access
11
+ @spec = nil
9
12
  end
10
13
 
11
14
  def new_connection
@@ -22,20 +25,44 @@ module FreshConnection
22
25
  @spec ||= build_spec
23
26
  end
24
27
 
28
+ # when building a spec from envars, there may be no database.yml file, so
29
+ # be careful to avoid implicit dependency on it or derived contents.
30
+
25
31
  def build_spec
26
- config = ar_spec.config.symbolize_keys
27
- group_config = config[@group]
32
+ url = database_group_url(@group.to_s)
33
+ if url
34
+ build_group_spec_from_url(url)
35
+ else
36
+ build_spec_from_config
37
+ end
38
+ end
39
+
40
+ def database_group_url(group)
41
+ ENV['DATABASE_' + group.upcase + '_URL']
42
+ end
43
+
44
+ def build_group_spec_from_url(url)
45
+ config = ar_spec.config.with_indifferent_access
46
+ spec = build_spec_from_url(url)
47
+ group_config = (config[@group] ||= {}.with_indifferent_access)
48
+ config.merge(group_config).merge(spec).merge(@modify_spec)
49
+ end
50
+
51
+ def build_spec_from_url(url)
52
+ ActiveRecord::ConnectionAdapters::ConnectionSpecification::ConnectionUrlResolver.new(url).to_hash
53
+ end
28
54
 
29
- # provide backward compatibility for older :slave usage
30
- if !group_config && @group == :replica && config.key?(:slave)
55
+ def build_spec_from_config
56
+ config = ar_spec.config.with_indifferent_access
57
+ group_config = config[@group] || {}.with_indifferent_access
58
+
59
+ if group_config.size == 0 && @group == :replica && config.key?(:slave)
60
+ # provide backward-compatibility with :slave profile
31
61
  ActiveSupport::Deprecation.warn(
32
- "'slave' in database.yml is deprecated and will ignored from version 2.4.0. use 'replica' insted."
62
+ "'slave' in database.yml is deprecated and will ignored from version 2.4.0. use 'replica' instead."
33
63
  )
34
- group_config = config[:slave]
64
+ group_config = (config[:slave] || {}).with_indifferent_access
35
65
  end
36
-
37
- group_config = (group_config || {}).symbolize_keys
38
-
39
66
  config.merge(group_config).merge(@modify_spec)
40
67
  end
41
68
 
@@ -25,7 +25,7 @@ module FreshConnection
25
25
  all.manage_access(false, &block)
26
26
  end
27
27
 
28
- def establish_fresh_connection(replica_group = nil)
28
+ def establish_fresh_connection(replica_group = "replica")
29
29
  replica_connection_handler.establish_connection(name, replica_group)
30
30
  end
31
31
 
@@ -35,7 +35,7 @@ module FreshConnection
35
35
 
36
36
  def slave_connection
37
37
  ActiveSupport::Deprecation.warn(
38
- "'slave_connection' is deprecated and will removed from version 2.4.0. use 'replica_connection' insted."
38
+ "'slave_connection' is deprecated and will removed from version 2.4.0. use 'replica_connection' instead."
39
39
  )
40
40
 
41
41
  replica_connection
@@ -47,7 +47,7 @@ module FreshConnection
47
47
 
48
48
  def clear_all_slave_connections!
49
49
  ActiveSupport::Deprecation.warn(
50
- "'clear_all_slave_connections!' is deprecated and will removed from version 2.4.0. use 'clear_all_replica_connections!' insted."
50
+ "'clear_all_slave_connections!' is deprecated and will removed from version 2.4.0. use 'clear_all_replica_connections!' instead."
51
51
  )
52
52
 
53
53
  clear_all_replica_connections!
@@ -68,7 +68,7 @@ module FreshConnection
68
68
 
69
69
  def slave_connection_put_aside!
70
70
  ActiveSupport::Deprecation.warn(
71
- "'slave_connection_put_aside!' is deprecated and will removed from version 2.4.0. use 'replica_connection_put_aside!' insted."
71
+ "'slave_connection_put_aside!' is deprecated and will removed from version 2.4.0. use 'replica_connection_put_aside!' instead."
72
72
  )
73
73
 
74
74
  replica_connection_put_aside!
@@ -80,7 +80,7 @@ module FreshConnection
80
80
 
81
81
  def slave_connection_recovery?
82
82
  ActiveSupport::Deprecation.warn(
83
- "'slave_connection_recovery?' is deprecated and will removed from version 2.4.0. use 'replica_connection_recovery?' insted."
83
+ "'slave_connection_recovery?' is deprecated and will removed from version 2.4.0. use 'replica_connection_recovery?' instead."
84
84
  )
85
85
 
86
86
  replica_connection_recovery?
@@ -92,7 +92,7 @@ module FreshConnection
92
92
 
93
93
  def slave_group
94
94
  ActiveSupport::Deprecation.warn(
95
- "'slave_connection_recovery?' is deprecated and will removed from version 2.4.0. use 'replica_connection_recovery?' insted."
95
+ "'slave_connection_recovery?' is deprecated and will removed from version 2.4.0. use 'replica_connection_recovery?' instead."
96
96
  )
97
97
 
98
98
  replica_group
@@ -60,7 +60,7 @@ module FreshConnection
60
60
 
61
61
  def enable_slave_access
62
62
  ActiveSupport::Deprecation.warn(
63
- "'enable_slave_access' is deprecated and will removed from version 2.4.0. use 'enable_replica_access' insted."
63
+ "'enable_slave_access' is deprecated and will removed from version 2.4.0. use 'enable_replica_access' instead."
64
64
  )
65
65
 
66
66
  enable_replica_access
@@ -3,15 +3,17 @@ require 'concurrent'
3
3
  module FreshConnection
4
4
  class ReplicaConnectionHandler
5
5
  def initialize
6
+ @replica_group_to_pool = Concurrent::Map.new
6
7
  @class_to_pool = Concurrent::Map.new
7
8
  end
8
9
 
9
10
  def establish_connection(name, replica_group)
10
- if cm = class_to_pool[name]
11
+ if cm = @class_to_pool[name]
11
12
  cm.put_aside!
13
+ @class_to_pool.delete(name)
12
14
  end
13
15
 
14
- class_to_pool[name] = FreshConnection.connection_manager.new(replica_group)
16
+ @replica_group_to_pool[name] = replica_group
15
17
  end
16
18
 
17
19
  def connection(klass)
@@ -41,20 +43,23 @@ module FreshConnection
41
43
  private
42
44
 
43
45
  def all_connection_managers
44
- class_to_pool.each_value do |connection_manager|
46
+ @class_to_pool.each_value do |connection_manager|
45
47
  yield(connection_manager)
46
48
  end
47
49
  end
48
50
 
49
51
  def detect_connection_manager(klass)
50
- c = class_to_pool[klass.name]
52
+ c = class_to_pool(klass.name)
51
53
  return c if c
52
54
  return nil if ActiveRecord::Base == klass
53
55
  detect_connection_manager(klass.superclass)
54
56
  end
55
57
 
56
- def class_to_pool
57
- @class_to_pool
58
+ def class_to_pool(name)
59
+ return @class_to_pool[name] if @class_to_pool.key?(name)
60
+ g = @replica_group_to_pool[name]
61
+ return nil unless g
62
+ @class_to_pool[name] = FreshConnection.connection_manager.new(g)
58
63
  end
59
64
  end
60
65
  end
@@ -1,4 +1,4 @@
1
1
  module FreshConnection
2
- VERSION = "2.3.0"
2
+ VERSION = "2.3.1"
3
3
  end
4
4
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fresh_connection
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.0
4
+ version: 2.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tsukasa OISHI
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-04-18 00:00:00.000000000 Z
11
+ date: 2017-05-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord