ar-multidb 0.1.12 → 0.4.2

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: cedb676272cc87f3a21d66c15efd9058e0e13551
4
- data.tar.gz: b1dae22e38cfbe06ab71ccaf7abbe6dbfb459c9f
2
+ SHA256:
3
+ metadata.gz: 3ecd6501582114326efd8452ffe712c79b5667d1ce54a181eff6efeaf40d1897
4
+ data.tar.gz: 85c3e8b3dc155374ef7d7caed121bf5a140cf64030b760bb6cdcdb3123e8cddc
5
5
  SHA512:
6
- metadata.gz: 5662d638ad37ebd926c8a7c369ed60de18f8debbc121cad19c760a9f7301e73b68d1914f0d8920aa9b2e2dcb7a2af01e79b89a1bf65041b5a636b6e239417502
7
- data.tar.gz: cb9a4f11337c379216d295d579e1938266377c887e7b4608a07fbcb1dcc78d298502459f8ad3cfb97e0acda5209261e9a869560e926654d213747e3946e0b65b
6
+ metadata.gz: e2eefe25c89d914367afecce5ebc07f4be668985097969f5a1c9acb563a22c448778b4128caf64e5dd6029437e28b13188097d6c66c111f4b5911543858f50b1
7
+ data.tar.gz: c83c08a921575803b15ebf473e125ad6da3619b321208766e9f8d058bb8226154cd21255d14aa77be260bfd17da29926be8aa3bdeda547fed0c6f46624370283
data/.gitignore CHANGED
@@ -1,4 +1,7 @@
1
1
  /pkg
2
2
  *~
3
3
  \.DS_Store
4
- *.sqlite
4
+ *.sqlite
5
+ Gemfile.lock
6
+ Gemfile.local
7
+ .rspec
@@ -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
+
@@ -1,3 +1,15 @@
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
@@ -0,0 +1,16 @@
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.4.2]
8
+ ### Changed
9
+ - adjust rails restriction to exclude untested rails 6
10
+ - adjust minimum ruby version to 2.4
11
+ - add rubocop
12
+
13
+ ## [0.4.1]
14
+ ### Added
15
+ - Added support for database aliases ( PR #26 )
16
+
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
@@ -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
@@ -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.0'
22
+ s.add_runtime_dependency 'activesupport', '>= 5.1', '< 6.0'
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: '..'
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'multidb'
@@ -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,107 +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
94
+ end
95
+
96
+ def current_connection_name
97
+ if Thread.current[:multidb]
98
+ Thread.current[:multidb][:connection_name]
99
+ else
100
+ :default
101
+ end
101
102
  end
102
103
 
103
104
  class << self
104
- delegate :use, :current_connection, :disconnect!, to: :balancer
105
105
  def use(name, &block)
106
106
  Multidb.balancer.use(name, &block)
107
107
  end
@@ -110,11 +110,13 @@ module Multidb
110
110
  Multidb.balancer.current_connection
111
111
  end
112
112
 
113
+ def current_connection_name
114
+ Multidb.balancer.current_connection_name
115
+ end
116
+
113
117
  def disconnect!
114
118
  Multidb.balancer.disconnect!
115
119
  end
116
120
  end
117
-
118
121
  end
119
-
120
- 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,54 +1,35 @@
1
- module Multidb
2
-
3
- mattr_reader :configuration
1
+ # frozen_string_literal: true
4
2
 
3
+ module Multidb
5
4
  class << self
6
5
  delegate :use, :get, :disconnect!, to: :balancer
7
6
  end
8
7
 
8
+ def self.init(config)
9
+ activerecord_config = config.dup.with_indifferent_access
10
+ default_adapter = activerecord_config
11
+ configuration_hash = activerecord_config.delete(:multidb)
12
+
13
+ @balancer = Balancer.new(Configuration.new(default_adapter, configuration_hash || {}))
14
+ end
15
+
9
16
  def self.balancer
10
- @balancer ||= create_balancer
17
+ @balancer || raise(NotInitializedError, 'Balancer not initialized. You need to run Multidb.init first')
11
18
  end
12
19
 
13
20
  def self.reset!
14
- @balancer, @configuration = nil, nil
21
+ @balancer = nil
15
22
  end
16
23
 
24
+ class NotInitializedError < StandardError; end
25
+
17
26
  class Configuration
18
27
  def initialize(default_adapter, configuration_hash)
19
- @default_pool = ActiveRecord::Base.connection_pool
28
+ @default_handler = ActiveRecord::Base.connection_handler
20
29
  @default_adapter = default_adapter
21
30
  @raw_configuration = configuration_hash
22
31
  end
23
32
 
24
- attr_reader :default_pool
25
- attr_reader :default_adapter
26
- attr_reader :raw_configuration
33
+ attr_reader :default_handler, :default_adapter, :raw_configuration
27
34
  end
28
-
29
- private
30
-
31
- def self.create_balancer
32
- unless @configuration
33
- begin
34
- connection_pool = ActiveRecord::Base.connection_pool
35
- rescue ActiveRecord::ConnectionNotEstablished
36
- # Ignore
37
- else
38
- connection = connection_pool.connection
39
-
40
- # FIXME: This is hacky, but apparently the only way to get at
41
- # the internal configuration hash.
42
- activerecord_config = connection.instance_variable_get(:@config).dup.with_indifferent_access
43
-
44
- default_adapter, configuration_hash = activerecord_config, activerecord_config.delete(:multidb)
45
-
46
- @configuration = Configuration.new(default_adapter, configuration_hash || {})
47
- end
48
- end
49
- if @configuration
50
- Balancer.new(@configuration)
51
- end
52
- end
53
-
54
- 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,24 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_record/base'
4
+
1
5
  module Multidb
2
- module ModelExtensions
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
3
18
 
19
+ module ModelExtensions
4
20
  extend ActiveSupport::Concern
5
21
 
6
22
  included do
7
23
  class << self
8
- alias_method_chain :connection, :multidb
9
- end
10
- end
11
-
12
- module ClassMethods
13
- def connection_with_multidb
14
- if (balancer = Multidb.balancer)
15
- balancer.current_connection
16
- else
17
- connection_without_multidb
18
- end
24
+ prepend Multidb::Connection
19
25
  end
20
26
  end
21
-
22
27
  end
23
28
  end
24
29
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Multidb
2
- VERSION = '0.1.12'
3
- end
4
+ VERSION = '0.4.2'
5
+ end
@@ -1,16 +1,17 @@
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
- it 'returns nothing' do
7
- Multidb.balancer.should eq nil
7
+ it 'raises exception' do
8
+ -> { Multidb.balancer }.should raise_error(Multidb::NotInitializedError)
8
9
  end
9
10
  end
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,25 +27,54 @@ describe 'Multidb.balancer' do
26
27
 
27
28
  Multidb.balancer.current_connection.should eq conn
28
29
  end
29
- end
30
30
 
31
- describe '#use' do
32
- context 'with no configuration' do
33
- it 'raises exception' do
34
- -> {
35
- Multidb.use(:something) do
36
- end
37
- }.should raise_error(ArgumentError)
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
38
57
  end
39
58
  end
59
+ end
40
60
 
61
+ describe '#use' do
41
62
  context 'with configuration' do
42
63
  before do
43
- ActiveRecord::Base.establish_connection(configuration_with_slaves)
64
+ ActiveRecord::Base.establish_connection(configuration_with_replicas)
65
+ end
66
+
67
+ context 'undefined connection' do
68
+ it 'raises exception' do
69
+ lambda {
70
+ Multidb.use(:something) do
71
+ end
72
+ }.should raise_error(ArgumentError)
73
+ end
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'
95
+ end
96
+ end
97
+
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)
65
104
  end
105
+ res.should eq []
66
106
  end
67
107
 
68
- it 'returns supports nested slave connection' do
69
- Multidb.use(:slave1) do
70
- Multidb.use(:slave2) do
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
@@ -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
@@ -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,91 +1,138 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ar-multidb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.12
4
+ version: 0.4.2
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-03-31 00:00:00.000000000 Z
12
+ date: 2020-07-20 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.0'
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.0'
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.0'
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.0'
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
- - Gemfile.lock
81
124
  - LICENSE
82
125
  - README.markdown
83
126
  - Rakefile
84
127
  - ar-multidb.gemspec
128
+ - gemfiles/activerecord51.gemfile
129
+ - gemfiles/activerecord52.gemfile
85
130
  - lib/ar-multidb.rb
86
131
  - lib/multidb.rb
87
132
  - lib/multidb/balancer.rb
133
+ - lib/multidb/candidate.rb
88
134
  - lib/multidb/configuration.rb
135
+ - lib/multidb/log_subscriber.rb
89
136
  - lib/multidb/model_extensions.rb
90
137
  - lib/multidb/version.rb
91
138
  - spec/balancer_spec.rb
@@ -94,29 +141,27 @@ files:
94
141
  homepage: ''
95
142
  licenses: []
96
143
  metadata: {}
97
- post_install_message:
144
+ post_install_message:
98
145
  rdoc_options: []
99
146
  require_paths:
100
147
  - lib
101
148
  required_ruby_version: !ruby/object:Gem::Requirement
102
149
  requirements:
103
- - - '>='
150
+ - - ">="
104
151
  - !ruby/object:Gem::Version
105
- version: '0'
152
+ version: 2.4.0
106
153
  required_rubygems_version: !ruby/object:Gem::Requirement
107
154
  requirements:
108
- - - '>='
155
+ - - ">="
109
156
  - !ruby/object:Gem::Version
110
157
  version: '0'
111
158
  requirements: []
112
- rubyforge_project: ar-multidb
113
- rubygems_version: 2.0.3
114
- signing_key:
159
+ rubygems_version: 3.0.6
160
+ signing_key:
115
161
  specification_version: 4
116
162
  summary: Multidb is an ActiveRecord extension for switching between multiple database
117
- connections, such as master/slave setups.
163
+ connections, such as primary/replica setups.
118
164
  test_files:
119
165
  - spec/balancer_spec.rb
120
166
  - spec/helpers.rb
121
167
  - spec/spec_helper.rb
122
- has_rdoc:
@@ -1,54 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- ar-multidb (0.1.11)
5
- activerecord (>= 3.0)
6
- activesupport (>= 3.0)
7
-
8
- GEM
9
- remote: http://rubygems.org/
10
- specs:
11
- activemodel (4.0.2)
12
- activesupport (= 4.0.2)
13
- builder (~> 3.1.0)
14
- activerecord (4.0.2)
15
- activemodel (= 4.0.2)
16
- activerecord-deprecated_finders (~> 1.0.2)
17
- activesupport (= 4.0.2)
18
- arel (~> 4.0.0)
19
- activerecord-deprecated_finders (1.0.3)
20
- activesupport (4.0.2)
21
- i18n (~> 0.6, >= 0.6.4)
22
- minitest (~> 4.2)
23
- multi_json (~> 1.3)
24
- thread_safe (~> 0.1)
25
- tzinfo (~> 0.3.37)
26
- arel (4.0.1)
27
- atomic (1.1.14)
28
- builder (3.1.4)
29
- diff-lcs (1.2.5)
30
- i18n (0.6.9)
31
- minitest (4.7.5)
32
- multi_json (1.8.4)
33
- rake (10.1.1)
34
- rspec (2.14.1)
35
- rspec-core (~> 2.14.0)
36
- rspec-expectations (~> 2.14.0)
37
- rspec-mocks (~> 2.14.0)
38
- rspec-core (2.14.7)
39
- rspec-expectations (2.14.5)
40
- diff-lcs (>= 1.1.3, < 2.0)
41
- rspec-mocks (2.14.5)
42
- sqlite3 (1.3.8)
43
- thread_safe (0.1.3)
44
- atomic
45
- tzinfo (0.3.38)
46
-
47
- PLATFORMS
48
- ruby
49
-
50
- DEPENDENCIES
51
- ar-multidb!
52
- rake
53
- rspec
54
- sqlite3