ar-multidb 0.1.13 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: ebc5c80b4af196924f76bb368ffec424b94d6090
4
- data.tar.gz: 50c67359cdce94f25a6c64f251423281f79e53b7
2
+ SHA256:
3
+ metadata.gz: 211ef81c6242f5424dbd0574b717bcd70d8f8cb4a7130198189d778491288000
4
+ data.tar.gz: 884334c8cd11d7a1715260f958304e08af23b6d7ce1129d88454b2acf979669f
5
5
  SHA512:
6
- metadata.gz: fe6db0140c93565223db1739c68916ab4334da42ed8b451f75d024456f5aea5194a5885a714b9f29625002e348ed7d60e53f5dba3caaf1e41317194a3a4573aa
7
- data.tar.gz: b304471c77b2cc4ef243f21db533fa09a921ad57c4cc183a97ae108fdd54724ab942bd24d1bbd42ca35c77c0fdcbd9d704c635647ff1260c50d1ebae7b40a817
6
+ metadata.gz: 9f7f7bb26d97fe1ec7a835e6a3ddc9e360e3d444b69c3a0b49c4001b0d06887f8f62ee88bdfc15e336e967fc913bc4e040c761788560c46b4291990edcf7fcc3
7
+ data.tar.gz: 0d29556db56b3686666fe3378cb7fa7e2b5a3b8cca4798be7412af4dc93b685275e09118d187768168efc72a4f983170bd2a84d5f26a0b6f79c19d618431995c
data/.gitignore CHANGED
@@ -3,3 +3,8 @@
3
3
  \.DS_Store
4
4
  *.sqlite
5
5
  Gemfile.lock
6
+ Gemfile.local
7
+ gemfiles/*.lock
8
+ .ruby-version
9
+ .ruby-gemset
10
+ .rspec
data/.rubocop.yml ADDED
@@ -0,0 +1,62 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.4
3
+ Exclude:
4
+ - '**/vendor/**/*'
5
+
6
+ Layout/EmptyLinesAroundAttributeAccessor:
7
+ Enabled: true
8
+ Layout/SpaceAroundMethodCallOperator:
9
+ Enabled: true
10
+ Layout/LineLength:
11
+ Exclude:
12
+ - 'ar-multidb.gemspec'
13
+ Lint/DeprecatedOpenSSLConstant:
14
+ Enabled: true
15
+ Lint/DuplicateElsifCondition:
16
+ Enabled: true
17
+ Lint/MixedRegexpCaptureTypes:
18
+ Enabled: true
19
+ Lint/RaiseException:
20
+ Enabled: true
21
+ Lint/StructNewOverride:
22
+ Enabled: true
23
+ Naming/FileName:
24
+ Exclude:
25
+ - 'lib/ar-multidb.rb'
26
+ Metrics:
27
+ Enabled: false
28
+ Style/AccessorGrouping:
29
+ Enabled: true
30
+ Style/ArrayCoercion:
31
+ Enabled: true
32
+ Style/BisectedAttrAccessor:
33
+ Enabled: true
34
+ Style/CaseLikeIf:
35
+ Enabled: true
36
+ Style/Documentation:
37
+ Enabled: false
38
+ Style/ExponentialNotation:
39
+ Enabled: true
40
+ Style/HashAsLastArrayItem:
41
+ Enabled: true
42
+ Style/HashEachMethods:
43
+ Enabled: true
44
+ Style/HashLikeCase:
45
+ Enabled: true
46
+ Style/HashTransformKeys:
47
+ Enabled: true
48
+ Style/HashTransformValues:
49
+ Enabled: true
50
+ Style/RedundantAssignment:
51
+ Enabled: true
52
+ Style/RedundantFetchBlock:
53
+ Enabled: true
54
+ Style/RedundantFileExtensionInRequire:
55
+ Enabled: true
56
+ Style/RedundantRegexpCharacterClass:
57
+ Enabled: true
58
+ Style/RedundantRegexpEscape:
59
+ Enabled: true
60
+ Style/SlicingWithRange:
61
+ Enabled: true
62
+
data/.travis.yml CHANGED
@@ -1,3 +1,17 @@
1
- rvm:
2
- - 1.9.3
3
- - 2.0
1
+ sudo: false
2
+ language: ruby
3
+ cache: bundler
4
+
5
+ script:
6
+ - bundle exec rubocop
7
+ - bundle exec rspec
8
+
9
+ matrix:
10
+ fast_finish: true
11
+ include:
12
+ - rvm: 2.5
13
+ gemfile: gemfiles/activerecord51.gemfile
14
+ - rvm: 2.5
15
+ gemfile: gemfiles/activerecord52.gemfile
16
+ - rvm: 2.5
17
+ gemfile: gemfiles/activerecord60.gemfile
data/CHANGELOG.md ADDED
@@ -0,0 +1,20 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
5
+ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html)
6
+
7
+ ## [0.5.0]
8
+ ### Changed
9
+ - added rails 6 support
10
+
11
+ ## [0.4.2]
12
+ ### Changed
13
+ - adjust rails restriction to exclude untested rails 6
14
+ - adjust minimum ruby version to 2.4
15
+ - add rubocop
16
+
17
+ ## [0.4.1]
18
+ ### Added
19
+ - Added support for database aliases ( PR #26 )
20
+
data/Gemfile CHANGED
@@ -1,9 +1,12 @@
1
- source "http://rubygems.org"
1
+ # frozen_string_literal: true
2
+
3
+ source 'http://rubygems.org'
2
4
 
3
5
  # Specify your gem's dependencies in ar-multidb.gemspec
4
6
  gemspec
5
7
 
6
- group :test do
7
- # For travis-ci.org
8
- gem "rake"
8
+ local_gemfile = 'Gemfile.local'
9
+
10
+ if File.exist?(local_gemfile)
11
+ eval(File.read(local_gemfile)) # rubocop:disable Security/Eval
9
12
  end
data/README.markdown CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  # Multidb
4
4
 
5
- A simple, no-nonsense ActiveRecord extension which allows the application to switch between multiple database connections, such as in a master/slave environment. For example:
5
+ A simple, no-nonsense ActiveRecord extension which allows the application to switch between multiple database connections, such as in a primary/replica environment. For example:
6
6
 
7
- Multidb.use(:slave) do
7
+ Multidb.use(:replica) do
8
8
  @posts = Post.all
9
9
  end
10
10
 
@@ -14,8 +14,13 @@ Randomized balancing of multiple connections within a group is supported. In the
14
14
 
15
15
  ## Requirements
16
16
 
17
- * Ruby 1.9.3 or later.
18
- * ActiveRecord 3.0 or later. (Earlier versions can use the gem version 0.1.10.)
17
+ * Ruby 2.4 or later.
18
+ * ActiveRecord 5.1 or later.
19
+
20
+ ## Older releases
21
+ For ActiveRecord 4. through 5.0 use version 0.3
22
+ For ActiveRecord older than 4.0 use the gem version 0.1.13
23
+ For ActiveRecord older than 3.0 use 0.1.10
19
24
 
20
25
  ## Comparison to other ActiveRecord extensions
21
26
 
@@ -27,10 +32,10 @@ Compared to other, more full-featured extensions such as Octopus and Seamless Da
27
32
 
28
33
  **Orthogonal**. Unlike Octopus, for example, connections follow context:
29
34
 
30
- Multidb.use(:master) do
35
+ Multidb.use(:primary) do
31
36
  @post = Post.find(1)
32
- Multidb.use(:slave) do
33
- @post.authors # This will use the slave
37
+ Multidb.use(:replica) do
38
+ @post.authors # This will use the replica
34
39
  end
35
40
  end
36
41
 
@@ -55,8 +60,8 @@ All that is needed is to set up your `database.yml` file:
55
60
  host: db1
56
61
  multidb:
57
62
  databases:
58
- slave:
59
- host: db-slave
63
+ replica:
64
+ host: db-replica
60
65
 
61
66
  Each database entry may be a hash or an array. So this also works:
62
67
 
@@ -68,24 +73,41 @@ Each database entry may be a hash or an array. So this also works:
68
73
  host: db1
69
74
  multidb:
70
75
  databases:
71
- slave:
72
- - host: db-slave1
73
- - host: db-slave2
76
+ replica:
77
+ - host: db-replica1
78
+ - host: db-replica2
74
79
 
75
80
  If multiple elements are specified, Multidb will use the list to pick a random candidate connection.
76
81
 
77
82
  The database hashes follow the same format as the top-level adapter configuration. In other words, each database connection may override the adapter, database name, username and so on.
78
83
 
84
+ You may also add an "alias" record to the configuration to support more than one name for a given database configuration.
85
+
86
+ production:
87
+ adapter: postgresql
88
+ database: myapp_production
89
+ username: ohoh
90
+ password: mymy
91
+ host: db1
92
+ multidb:
93
+ databases:
94
+ main_db:
95
+ host: db1-a
96
+ secondary_db:
97
+ alias: main_db
98
+
99
+ With the above, `Multidb.use(:main_db)` and `Multidb.use(:secondary_db)` will work identically. This can be useful to support naming scheme migrations transparently: once your application is updated to use `secondary_db` where necessary, you can swap out the configuration.
100
+
79
101
  To use the connection, modify your code by wrapping database access logic in blocks:
80
102
 
81
- Multidb.use(:slave) do
103
+ Multidb.use(:replica) do
82
104
  @posts = Post.all
83
105
  end
84
106
 
85
107
  To wrap entire controller requests, for example:
86
108
 
87
109
  class PostsController < ApplicationController
88
- around_filter :run_using_slave, only: [:index]
110
+ around_filter :run_using_replica, only: [:index]
89
111
 
90
112
  def index
91
113
  @posts = Post.all
@@ -95,22 +117,22 @@ To wrap entire controller requests, for example:
95
117
  # Won't be wrapped
96
118
  end
97
119
 
98
- def run_using_slave(&block)
99
- Multidb.use(:slave, &block)
120
+ def run_using_replica(&block)
121
+ Multidb.use(:replica, &block)
100
122
  end
101
123
  end
102
124
 
103
125
  You can also set the current connection for the remainder of the thread's execution:
104
126
 
105
- Multidb.use(:slave)
127
+ Multidb.use(:replica)
106
128
  # Do work
107
- Multidb.use(:master)
129
+ Multidb.use(:primary)
108
130
 
109
131
  Note that the symbol `:default` will (unless you override it) refer to the default top-level ActiveRecord configuration.
110
132
 
111
133
  ## Development mode
112
134
 
113
- In development you will typically want `Multidb.use(:slave)` to still work, but you probably don't want to run multiple databases on your development box. To make `use` silently fall back to using the default connection, Multidb can run in fallback mode.
135
+ In development you will typically want `Multidb.use(:replica)` to still work, but you probably don't want to run multiple databases on your development box. To make `use` silently fall back to using the default connection, Multidb can run in fallback mode.
114
136
 
115
137
  If you are using Rails, this will be automatically enabled in `development` and `test` environments. Otherwise, simply set `fallback: true` in `database.yml`:
116
138
 
data/Rakefile CHANGED
@@ -1,26 +1,27 @@
1
- require "bundler/gem_tasks"
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
2
4
  require 'rspec/core/rake_task'
3
5
 
4
6
  RSpec::Core::RakeTask.new
5
7
 
6
- task :default => :spec
8
+ task default: :spec
7
9
 
8
10
  desc 'Bump version'
9
11
  task :bump do
10
- if `git status -uno -s --porcelain | wc -l`.to_i > 0
11
- abort "You have uncommitted changed."
12
- end
12
+ abort 'You have uncommitted changed.' if `git status -uno -s --porcelain | wc -l`.to_i.positive?
13
+
13
14
  text = File.read('lib/multidb/version.rb')
14
15
  if text =~ /VERSION = '(.*)'/
15
- old_version = $1
16
+ old_version = Regexp.last_match(1)
16
17
  version_parts = old_version.split('.')
17
18
  version_parts[-1] = version_parts[-1].to_i + 1
18
19
  new_version = version_parts.join('.')
19
20
  text.gsub!(/VERSION = '(.*)'/, "VERSION = '#{new_version}'")
20
21
  File.open('lib/multidb/version.rb', 'w') { |f| f << text }
21
- (system("git add lib/multidb/version.rb") and
22
- system("git commit -m 'Bump to #{new_version}.'")) or abort "Failed to commit."
22
+ (system('git add lib/multidb/version.rb') &&
23
+ system("git commit -m 'Bump to #{new_version}.'")) || abort('Failed to commit.')
23
24
  else
24
- abort "Could not find version number"
25
+ abort 'Could not find version number'
25
26
  end
26
27
  end
data/ar-multidb.gemspec CHANGED
@@ -1,25 +1,28 @@
1
- # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
3
- require "multidb/version"
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.push File.expand_path('lib', __dir__)
4
+ require 'multidb/version'
4
5
 
5
6
  Gem::Specification.new do |s|
6
- s.name = "ar-multidb"
7
+ s.name = 'ar-multidb'
7
8
  s.version = Multidb::VERSION
8
- s.authors = ["Alexander Staubo"]
9
- s.email = ["alex@bengler.no"]
10
- s.homepage = ""
11
- s.summary = s.description = %q{Multidb is an ActiveRecord extension for switching between multiple database connections, such as master/slave setups.}
12
-
13
- s.rubyforge_project = "ar-multidb"
9
+ s.authors = ['Alexander Staubo', 'Edward Rudd']
10
+ s.email = ['alex@bengler.no', 'urkle@outoforder.cc']
11
+ s.homepage = ''
12
+ s.summary = s.description = 'Multidb is an ActiveRecord extension for switching between multiple database connections, such as primary/replica setups.'
14
13
 
15
14
  s.files = `git ls-files`.split("\n")
16
15
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
- s.require_paths = ["lib"]
16
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
17
+ s.require_paths = ['lib']
18
+
19
+ s.required_ruby_version = '>= 2.4.0'
19
20
 
20
- s.add_runtime_dependency 'activesupport', '>= 3.0'
21
- s.add_runtime_dependency 'activerecord', '>= 3.0'
21
+ s.add_runtime_dependency 'activerecord', '>= 5.1', '< 6.1'
22
+ s.add_runtime_dependency 'activesupport', '>= 5.1', '< 6.1'
22
23
 
23
- s.add_development_dependency 'rspec'
24
- s.add_development_dependency 'sqlite3'
24
+ s.add_development_dependency 'rake', '~> 12.0'
25
+ s.add_development_dependency 'rspec', '~> 3.8'
26
+ s.add_development_dependency 'rubocop', '~> 0.88.0'
27
+ s.add_development_dependency 'sqlite3', '~> 1.3'
25
28
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gem 'activerecord', '~> 5.1.0'
6
+
7
+ gemspec path: '..'
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gem 'activerecord', '~> 5.2.0'
6
+
7
+ gemspec path: '..'
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gem 'activerecord', '~> 6.0.0'
6
+
7
+ gemspec path: '..'
data/lib/ar-multidb.rb CHANGED
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'multidb'
data/lib/multidb.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_record'
2
4
 
3
5
  require 'active_support/core_ext/module/delegation'
@@ -6,5 +8,7 @@ require 'active_support/core_ext/module/aliasing'
6
8
 
7
9
  require_relative 'multidb/configuration'
8
10
  require_relative 'multidb/model_extensions'
11
+ require_relative 'multidb/log_subscriber'
12
+ require_relative 'multidb/candidate'
9
13
  require_relative 'multidb/balancer'
10
14
  require_relative 'multidb/version'
@@ -1,108 +1,107 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Multidb
4
+ class Balancer
5
+ attr_accessor :fallback
2
6
 
3
- class Candidate
4
- def initialize(target)
5
- if target.is_a?(Hash)
6
- adapter = target[:adapter]
7
- begin
8
- require "active_record/connection_adapters/#{adapter}_adapter"
9
- rescue LoadError
10
- raise "Please install the #{adapter} adapter: `gem install activerecord-#{adapter}-adapter` (#{$!})"
11
- end
12
- if defined?(ActiveRecord::ConnectionAdapters::ConnectionSpecification)
13
- spec_class = ActiveRecord::ConnectionAdapters::ConnectionSpecification
14
- else
15
- spec_class = ActiveRecord::Base::ConnectionSpecification
16
- end
17
- @connection_pool = ActiveRecord::ConnectionAdapters::ConnectionPool.new(
18
- spec_class.new(target, "#{adapter}_connection"))
19
- else
20
- @connection_pool = target
21
- end
22
- end
7
+ def initialize(configuration)
8
+ @candidates = {}.with_indifferent_access
9
+ @default_configuration = configuration
23
10
 
24
- def connection(&block)
25
- if block_given?
26
- @connection_pool.with_connection(&block)
27
- else
28
- @connection_pool.connection
29
- end
30
- end
11
+ return unless @default_configuration
31
12
 
32
- attr_reader :connection_pool
33
- end
13
+ append(@default_configuration.raw_configuration[:databases] || {})
34
14
 
35
- class Balancer
15
+ @fallback = if @default_configuration.raw_configuration.include?(:fallback)
16
+ @default_configuration.raw_configuration[:fallback]
17
+ elsif defined?(Rails)
18
+ %w[development test].include?(Rails.env)
19
+ else
20
+ false
21
+ end
36
22
 
37
- def initialize(configuration)
38
- @candidates = {}.with_indifferent_access
39
- @configuration = configuration
40
- if @configuration
41
- (@configuration.raw_configuration[:databases] || {}).each_pair do |name, config|
42
- configs = config.is_a?(Array) ? config : [config]
43
- configs.each do |config|
44
- candidate = Candidate.new(@configuration.default_adapter.merge(config))
45
- @candidates[name] ||= []
46
- @candidates[name].push(candidate)
23
+ @default_candidate = Candidate.new('default', @default_configuration.default_handler)
24
+
25
+ @candidates[:default] = [@default_candidate] unless @candidates.include?(:default)
26
+ end
27
+
28
+ def append(databases)
29
+ databases.each_pair do |name, config|
30
+ configs = config.is_a?(Array) ? config : [config]
31
+ configs.each do |cfg|
32
+ if cfg['alias']
33
+ @candidates[name] = @candidates[cfg['alias']]
34
+ next
47
35
  end
48
- end
49
- if @configuration.raw_configuration.include?(:fallback)
50
- @fallback = @configuration.raw_configuration[:fallback]
51
- elsif defined?(Rails)
52
- @fallback = %w(development test).include?(Rails.env)
53
- else
54
- @fallback = false
55
- end
56
- @default_candidate = Candidate.new(@configuration.default_pool)
57
- unless @candidates.include?(:default)
58
- @candidates[:default] = [@default_candidate]
36
+
37
+ candidate = Candidate.new(name, @default_configuration.default_adapter.merge(cfg))
38
+ @candidates[name] ||= []
39
+ @candidates[name].push(candidate)
59
40
  end
60
41
  end
61
42
  end
62
43
 
63
44
  def disconnect!
64
- @candidates.values.flatten.each do |candidate|
65
- candidate.connection_pool.disconnect!
66
- end
45
+ @candidates.values.flatten.each(&:disconnect!)
67
46
  end
68
47
 
69
- def get(name, &block)
48
+ def get(name, &_block)
70
49
  candidates = @candidates[name]
71
50
  candidates ||= @fallback ? @candidates[:default] : []
51
+
72
52
  raise ArgumentError, "No such database connection '#{name}'" if candidates.empty?
73
- candidate = candidates.respond_to?(:sample) ?
74
- candidates.sample : candidates[rand(candidates.length)]
53
+
54
+ candidate = candidates.respond_to?(:sample) ? candidates.sample : candidates[rand(candidates.length)]
55
+
75
56
  block_given? ? yield(candidate) : candidate
76
57
  end
77
58
 
78
- def use(name, &block)
59
+ def use(name, &_block)
79
60
  result = nil
80
61
  get(name) do |candidate|
81
62
  if block_given?
82
63
  candidate.connection do |connection|
83
- previous_connection, Thread.current[:multidb_connection] =
84
- Thread.current[:multidb_connection], connection
64
+ previous_configuration = Thread.current[:multidb]
65
+ Thread.current[:multidb] = {
66
+ connection: connection,
67
+ connection_name: name
68
+ }
85
69
  begin
86
70
  result = yield
71
+ result = result.to_a if result.is_a?(ActiveRecord::Relation)
87
72
  ensure
88
- Thread.current[:multidb_connection] = previous_connection
73
+ Thread.current[:multidb] = previous_configuration
89
74
  end
90
75
  result
91
76
  end
92
77
  else
93
- result = Thread.current[:multidb_connection] = candidate.connection
78
+ Thread.current[:multidb] = {
79
+ connection: candidate.connection,
80
+ connection_name: name
81
+ }
82
+ result = candidate.connection
94
83
  end
95
84
  end
96
85
  result
97
86
  end
98
87
 
99
88
  def current_connection
100
- Thread.current[:multidb_connection] || @default_candidate.connection
89
+ if Thread.current[:multidb]
90
+ Thread.current[:multidb][:connection]
91
+ else
92
+ @default_candidate.connection
93
+ end
101
94
  end
102
95
 
103
- class << self
104
- delegate :use, :current_connection, :disconnect!, to: :balancer
96
+ def current_connection_name
97
+ if Thread.current[:multidb]
98
+ Thread.current[:multidb][:connection_name]
99
+ else
100
+ :default
101
+ end
102
+ end
105
103
 
104
+ class << self
106
105
  def use(name, &block)
107
106
  Multidb.balancer.use(name, &block)
108
107
  end
@@ -111,11 +110,13 @@ module Multidb
111
110
  Multidb.balancer.current_connection
112
111
  end
113
112
 
113
+ def current_connection_name
114
+ Multidb.balancer.current_connection_name
115
+ end
116
+
114
117
  def disconnect!
115
118
  Multidb.balancer.disconnect!
116
119
  end
117
120
  end
118
-
119
121
  end
120
-
121
- end
122
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Multidb
4
+ class Candidate
5
+ def initialize(name, target)
6
+ @name = name
7
+
8
+ case target
9
+ when Hash
10
+ @connection_handler = ActiveRecord::ConnectionAdapters::ConnectionHandler.new
11
+ @connection_handler.establish_connection(target.merge(name: 'primary'))
12
+ when ActiveRecord::ConnectionAdapters::ConnectionHandler
13
+ @connection_handler = target
14
+ else
15
+ raise ArgumentError, 'Connection handler not passed to target'
16
+ end
17
+ end
18
+
19
+ def connection(&block)
20
+ if block_given?
21
+ @connection_handler.retrieve_connection_pool('primary').with_connection(&block)
22
+ else
23
+ @connection_handler.retrieve_connection('primary')
24
+ end
25
+ end
26
+
27
+ def disconnect!
28
+ @connection_handler.clear_all_connections!
29
+ end
30
+
31
+ attr_reader :name
32
+ end
33
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Multidb
2
4
  class << self
3
5
  delegate :use, :get, :disconnect!, to: :balancer
@@ -5,34 +7,29 @@ module Multidb
5
7
 
6
8
  def self.init(config)
7
9
  activerecord_config = config.dup.with_indifferent_access
8
- default_adapter, configuration_hash = activerecord_config, activerecord_config.delete(:multidb)
10
+ default_adapter = activerecord_config
11
+ configuration_hash = activerecord_config.delete(:multidb)
9
12
 
10
13
  @balancer = Balancer.new(Configuration.new(default_adapter, configuration_hash || {}))
11
14
  end
12
15
 
13
16
  def self.balancer
14
- if @balancer
15
- @balancer
16
- else
17
- raise NotInitializedError, "Balancer not initialized. You need to run Multidb.setup first"
18
- end
17
+ @balancer || raise(NotInitializedError, 'Balancer not initialized. You need to run Multidb.init first')
19
18
  end
20
19
 
21
20
  def self.reset!
22
21
  @balancer = nil
23
22
  end
24
23
 
25
- class NotInitializedError < StandardError; end;
24
+ class NotInitializedError < StandardError; end
26
25
 
27
26
  class Configuration
28
27
  def initialize(default_adapter, configuration_hash)
29
- @default_pool = ActiveRecord::Base.connection_pool
28
+ @default_handler = ActiveRecord::Base.connection_handler
30
29
  @default_adapter = default_adapter
31
30
  @raw_configuration = configuration_hash
32
31
  end
33
32
 
34
- attr_reader :default_pool
35
- attr_reader :default_adapter
36
- attr_reader :raw_configuration
33
+ attr_reader :default_handler, :default_adapter, :raw_configuration
37
34
  end
38
- end
35
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Multidb
4
+ module LogSubscriberExtension
5
+ def sql(event)
6
+ name = Multidb.balancer.current_connection_name
7
+ event.payload[:db_name] = name if name
8
+ super
9
+ end
10
+
11
+ def debug(msg)
12
+ name = Multidb.balancer.current_connection_name
13
+ if name
14
+ db = color("[DB: #{name}]", ActiveSupport::LogSubscriber::GREEN, true)
15
+ super(db + msg)
16
+ else
17
+ super
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ ActiveRecord::LogSubscriber.prepend(Multidb::LogSubscriberExtension)
@@ -1,26 +1,27 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_record/base'
2
4
 
3
5
  module Multidb
6
+ module Connection
7
+ def establish_connection(spec = nil)
8
+ super(spec)
9
+ Multidb.init(connection_pool.spec.config)
10
+ end
11
+
12
+ def connection
13
+ Multidb.balancer.current_connection
14
+ rescue Multidb::NotInitializedError
15
+ super
16
+ end
17
+ end
18
+
4
19
  module ModelExtensions
5
20
  extend ActiveSupport::Concern
6
21
 
7
22
  included do
8
23
  class << self
9
- alias_method_chain :establish_connection, :multidb
10
- alias_method_chain :connection, :multidb
11
- end
12
- end
13
-
14
- module ClassMethods
15
- def establish_connection_with_multidb(spec = nil)
16
- establish_connection_without_multidb(spec)
17
- Multidb.init(connection_pool.spec.config)
18
- end
19
-
20
- def connection_with_multidb
21
- Multidb.balancer.current_connection
22
- rescue Multidb::NotInitializedError
23
- connection_without_multidb
24
+ prepend Multidb::Connection
24
25
  end
25
26
  end
26
27
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Multidb
2
- VERSION = '0.1.13'
3
- end
4
+ VERSION = '0.5.0'
5
+ end
@@ -1,7 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'spec_helper'
2
4
 
3
5
  describe 'Multidb.balancer' do
4
-
5
6
  context 'with no configuration' do
6
7
  it 'raises exception' do
7
8
  -> { Multidb.balancer }.should raise_error(Multidb::NotInitializedError)
@@ -10,7 +11,7 @@ describe 'Multidb.balancer' do
10
11
 
11
12
  context 'with configuration' do
12
13
  before do
13
- ActiveRecord::Base.establish_connection(configuration_with_slaves)
14
+ ActiveRecord::Base.establish_connection(configuration_with_replicas)
14
15
  end
15
16
 
16
17
  it 'returns balancer' do
@@ -26,17 +27,46 @@ describe 'Multidb.balancer' do
26
27
 
27
28
  Multidb.balancer.current_connection.should eq conn
28
29
  end
30
+
31
+ it 'returns default connection name for default connection' do
32
+ ActiveRecord::Base.connection
33
+
34
+ Multidb.balancer.current_connection_name.should eq :default
35
+ end
36
+
37
+ context 'with additional configurations' do
38
+ before do
39
+ additional_configuration = { replica4: { database: 'spec/test-replica4.sqlite' } }
40
+ Multidb.balancer.append(additional_configuration)
41
+ end
42
+
43
+ it 'makes the new database available' do
44
+ Multidb.use(:replica4) do
45
+ conn = ActiveRecord::Base.connection
46
+ conn.should eq Multidb.balancer.current_connection
47
+ list = conn.execute('pragma database_list')
48
+ list.length.should eq 1
49
+ File.basename(list[0]['file']).should eq 'test-replica4.sqlite'
50
+ end
51
+ end
52
+
53
+ it 'returns the connection name' do
54
+ Multidb.use(:replica4) do
55
+ Multidb.balancer.current_connection_name.should eq :replica4
56
+ end
57
+ end
58
+ end
29
59
  end
30
60
 
31
61
  describe '#use' do
32
62
  context 'with configuration' do
33
63
  before do
34
- ActiveRecord::Base.establish_connection(configuration_with_slaves)
64
+ ActiveRecord::Base.establish_connection(configuration_with_replicas)
35
65
  end
36
66
 
37
67
  context 'undefined connection' do
38
68
  it 'raises exception' do
39
- -> {
69
+ lambda {
40
70
  Multidb.use(:something) do
41
71
  end
42
72
  }.should raise_error(ArgumentError)
@@ -44,7 +74,7 @@ describe 'Multidb.balancer' do
44
74
  end
45
75
 
46
76
  it 'returns default connection on :default' do
47
- conn = ActiveRecord::Base.connection
77
+ ActiveRecord::Base.connection
48
78
  Multidb.use(:default) do
49
79
  conn2 = ActiveRecord::Base.connection
50
80
  conn2.should eq Multidb.balancer.current_connection
@@ -55,61 +85,75 @@ describe 'Multidb.balancer' do
55
85
  end
56
86
  end
57
87
 
58
- it 'returns slave connection' do
59
- Multidb.use(:slave1) do
88
+ it 'returns replica connection' do
89
+ Multidb.use(:replica1) do
60
90
  conn = ActiveRecord::Base.connection
61
91
  conn.should eq Multidb.balancer.current_connection
62
92
  list = conn.execute('pragma database_list')
63
93
  list.length.should eq 1
64
- File.basename(list[0]['file']).should eq 'test-slave1.sqlite'
94
+ File.basename(list[0]['file']).should eq 'test-replica1.sqlite'
65
95
  end
66
96
  end
67
97
 
68
- it 'returns supports nested slave connection' do
69
- Multidb.use(:slave1) do
70
- Multidb.use(:slave2) do
98
+ it 'returns results instead of relation' do
99
+ class FooBar < ActiveRecord::Base; end
100
+ res = Multidb.use(:replica1) do
101
+ ActiveRecord::Migration.verbose = false
102
+ ActiveRecord::Schema.define(version: 1) { create_table :foo_bars }
103
+ FooBar.where(id: 42)
104
+ end
105
+ res.should eq []
106
+ end
107
+
108
+ it 'returns supports nested replica connection' do
109
+ Multidb.use(:replica1) do
110
+ Multidb.use(:replica2) do
71
111
  conn = ActiveRecord::Base.connection
72
112
  conn.should eq Multidb.balancer.current_connection
73
113
  list = conn.execute('pragma database_list')
74
114
  list.length.should eq 1
75
- File.basename(list[0]['file']).should eq 'test-slave2.sqlite'
115
+ File.basename(list[0]['file']).should eq 'test-replica2.sqlite'
76
116
  end
77
117
  end
78
118
  end
79
119
 
80
120
  it 'returns preserves state when nesting' do
81
- Multidb.use(:slave1) do
82
- Multidb.use(:slave2) do
121
+ Multidb.use(:replica1) do
122
+ Multidb.use(:replica2) do
83
123
  conn = ActiveRecord::Base.connection
84
124
  conn.should eq Multidb.balancer.current_connection
85
125
  list = conn.execute('pragma database_list')
86
126
  list.length.should eq 1
87
- File.basename(list[0]['file']).should eq 'test-slave2.sqlite'
127
+ File.basename(list[0]['file']).should eq 'test-replica2.sqlite'
88
128
  end
89
129
 
90
130
  conn = ActiveRecord::Base.connection
91
131
  conn.should eq Multidb.balancer.current_connection
92
132
  list = conn.execute('pragma database_list')
93
133
  list.length.should eq 1
94
- File.basename(list[0]['file']).should eq 'test-slave1.sqlite'
134
+ File.basename(list[0]['file']).should eq 'test-replica1.sqlite'
95
135
  end
96
136
  end
97
137
 
138
+ it 'returns the parent connection for aliases' do
139
+ Multidb.use(:replica1).should_not eq Multidb.use(:replica_alias)
140
+ Multidb.use(:replica2).should eq Multidb.use(:replica_alias)
141
+ end
142
+
98
143
  it 'returns random candidate' do
99
144
  names = []
100
145
  100.times do
101
- Multidb.use(:slave3) do
146
+ Multidb.use(:replica3) do
102
147
  list = ActiveRecord::Base.connection.execute('pragma database_list')
103
148
  list.length.should eq 1
104
149
  names.push(File.basename(list[0]['file']))
105
150
  end
106
151
  end
107
152
  names.sort.uniq.should eq [
108
- 'test-slave3-1.sqlite',
109
- 'test-slave3-2.sqlite'
153
+ 'test-replica3-1.sqlite',
154
+ 'test-replica3-2.sqlite'
110
155
  ]
111
156
  end
112
157
  end
113
158
  end
114
-
115
- end
159
+ end
data/spec/helpers.rb CHANGED
@@ -1,20 +1,23 @@
1
- module Helpers
1
+ # frozen_string_literal: true
2
2
 
3
- def configuration_with_slaves
4
- return YAML.load(<<-end)
5
- adapter: sqlite3
6
- database: spec/test.sqlite
7
- encoding: utf-8
8
- multidb:
9
- databases:
10
- slave1:
11
- database: spec/test-slave1.sqlite
12
- slave2:
13
- database: spec/test-slave2.sqlite
14
- slave3:
15
- - database: spec/test-slave3-1.sqlite
16
- - database: spec/test-slave3-2.sqlite
17
- end
3
+ module Helpers
4
+ def configuration_with_replicas
5
+ YAML.safe_load(<<~YAML)
6
+ adapter: sqlite3
7
+ database: spec/test.sqlite
8
+ encoding: utf-8
9
+ multidb:
10
+ databases:
11
+ replica1:
12
+ database: spec/test-replica1.sqlite
13
+ replica2:
14
+ database: spec/test-replica2.sqlite
15
+ replica3:
16
+ - database: spec/test-replica3-1.sqlite
17
+ - database: spec/test-replica3-2.sqlite
18
+ replica_alias:
19
+ database: spec/test-replica2.sqlite
20
+ alias: replica2
21
+ YAML
18
22
  end
19
-
20
- end
23
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,23 +1,26 @@
1
- environment = ENV['RACK_ENV'] ||= 'test'
1
+ # frozen_string_literal: true
2
+
3
+ ENV['RACK_ENV'] ||= 'test'
2
4
 
3
5
  require 'rspec'
4
6
  require 'yaml'
5
7
  require 'active_record'
6
8
  require 'fileutils'
7
9
 
8
- $LOAD_PATH.unshift(File.expand_path('../lib', __FILE__))
10
+ $LOAD_PATH.unshift(File.expand_path('lib', __dir__))
9
11
  require 'multidb'
10
12
 
11
13
  require_relative 'helpers'
12
14
 
13
15
  RSpec.configure do |config|
14
16
  config.include Helpers
17
+ config.expect_with(:rspec) { |c| c.syntax = :should }
15
18
  config.before :each do
16
19
  ActiveRecord::Base.clear_all_connections!
17
20
  Multidb.reset!
18
21
  end
19
22
  config.after :each do
20
- Dir.glob(File.expand_path('../test*.sqlite', __FILE__)).each do |f|
23
+ Dir.glob(File.expand_path('test*.sqlite', __dir__)).each do |f|
21
24
  FileUtils.rm(f)
22
25
  end
23
26
  end
metadata CHANGED
@@ -1,90 +1,139 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ar-multidb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.13
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexander Staubo
8
- autorequire:
8
+ - Edward Rudd
9
+ autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2014-05-15 00:00:00.000000000 Z
12
+ date: 2021-06-08 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
- name: activesupport
15
+ name: activerecord
15
16
  requirement: !ruby/object:Gem::Requirement
16
17
  requirements:
17
- - - '>='
18
+ - - ">="
18
19
  - !ruby/object:Gem::Version
19
- version: '3.0'
20
+ version: '5.1'
21
+ - - "<"
22
+ - !ruby/object:Gem::Version
23
+ version: '6.1'
20
24
  type: :runtime
21
25
  prerelease: false
22
26
  version_requirements: !ruby/object:Gem::Requirement
23
27
  requirements:
24
- - - '>='
28
+ - - ">="
29
+ - !ruby/object:Gem::Version
30
+ version: '5.1'
31
+ - - "<"
25
32
  - !ruby/object:Gem::Version
26
- version: '3.0'
33
+ version: '6.1'
27
34
  - !ruby/object:Gem::Dependency
28
- name: activerecord
35
+ name: activesupport
29
36
  requirement: !ruby/object:Gem::Requirement
30
37
  requirements:
31
- - - '>='
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '5.1'
41
+ - - "<"
32
42
  - !ruby/object:Gem::Version
33
- version: '3.0'
43
+ version: '6.1'
34
44
  type: :runtime
35
45
  prerelease: false
36
46
  version_requirements: !ruby/object:Gem::Requirement
37
47
  requirements:
38
- - - '>='
48
+ - - ">="
39
49
  - !ruby/object:Gem::Version
40
- version: '3.0'
50
+ version: '5.1'
51
+ - - "<"
52
+ - !ruby/object:Gem::Version
53
+ version: '6.1'
54
+ - !ruby/object:Gem::Dependency
55
+ name: rake
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '12.0'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '12.0'
41
68
  - !ruby/object:Gem::Dependency
42
69
  name: rspec
43
70
  requirement: !ruby/object:Gem::Requirement
44
71
  requirements:
45
- - - '>='
72
+ - - "~>"
46
73
  - !ruby/object:Gem::Version
47
- version: '0'
74
+ version: '3.8'
48
75
  type: :development
49
76
  prerelease: false
50
77
  version_requirements: !ruby/object:Gem::Requirement
51
78
  requirements:
52
- - - '>='
79
+ - - "~>"
53
80
  - !ruby/object:Gem::Version
54
- version: '0'
81
+ version: '3.8'
82
+ - !ruby/object:Gem::Dependency
83
+ name: rubocop
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: 0.88.0
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: 0.88.0
55
96
  - !ruby/object:Gem::Dependency
56
97
  name: sqlite3
57
98
  requirement: !ruby/object:Gem::Requirement
58
99
  requirements:
59
- - - '>='
100
+ - - "~>"
60
101
  - !ruby/object:Gem::Version
61
- version: '0'
102
+ version: '1.3'
62
103
  type: :development
63
104
  prerelease: false
64
105
  version_requirements: !ruby/object:Gem::Requirement
65
106
  requirements:
66
- - - '>='
107
+ - - "~>"
67
108
  - !ruby/object:Gem::Version
68
- version: '0'
109
+ version: '1.3'
69
110
  description: Multidb is an ActiveRecord extension for switching between multiple database
70
- connections, such as master/slave setups.
111
+ connections, such as primary/replica setups.
71
112
  email:
72
113
  - alex@bengler.no
114
+ - urkle@outoforder.cc
73
115
  executables: []
74
116
  extensions: []
75
117
  extra_rdoc_files: []
76
118
  files:
77
- - .gitignore
78
- - .travis.yml
119
+ - ".gitignore"
120
+ - ".rubocop.yml"
121
+ - ".travis.yml"
122
+ - CHANGELOG.md
79
123
  - Gemfile
80
124
  - LICENSE
81
125
  - README.markdown
82
126
  - Rakefile
83
127
  - ar-multidb.gemspec
128
+ - gemfiles/activerecord51.gemfile
129
+ - gemfiles/activerecord52.gemfile
130
+ - gemfiles/activerecord60.gemfile
84
131
  - lib/ar-multidb.rb
85
132
  - lib/multidb.rb
86
133
  - lib/multidb/balancer.rb
134
+ - lib/multidb/candidate.rb
87
135
  - lib/multidb/configuration.rb
136
+ - lib/multidb/log_subscriber.rb
88
137
  - lib/multidb/model_extensions.rb
89
138
  - lib/multidb/version.rb
90
139
  - spec/balancer_spec.rb
@@ -93,29 +142,27 @@ files:
93
142
  homepage: ''
94
143
  licenses: []
95
144
  metadata: {}
96
- post_install_message:
145
+ post_install_message:
97
146
  rdoc_options: []
98
147
  require_paths:
99
148
  - lib
100
149
  required_ruby_version: !ruby/object:Gem::Requirement
101
150
  requirements:
102
- - - '>='
151
+ - - ">="
103
152
  - !ruby/object:Gem::Version
104
- version: '0'
153
+ version: 2.4.0
105
154
  required_rubygems_version: !ruby/object:Gem::Requirement
106
155
  requirements:
107
- - - '>='
156
+ - - ">="
108
157
  - !ruby/object:Gem::Version
109
158
  version: '0'
110
159
  requirements: []
111
- rubyforge_project: ar-multidb
112
- rubygems_version: 2.0.3
113
- signing_key:
160
+ rubygems_version: 3.1.6
161
+ signing_key:
114
162
  specification_version: 4
115
163
  summary: Multidb is an ActiveRecord extension for switching between multiple database
116
- connections, such as master/slave setups.
164
+ connections, such as primary/replica setups.
117
165
  test_files:
118
166
  - spec/balancer_spec.rb
119
167
  - spec/helpers.rb
120
168
  - spec/spec_helper.rb
121
- has_rdoc: