ar-multidb 0.1.13 → 0.5.0

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