ar-multidb 0.1.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.
- data/LICENSE +20 -0
- data/README.markdown +125 -0
- data/VERSION +1 -0
- data/lib/ar-multidb.rb +1 -0
- data/lib/multidb.rb +25 -0
- data/lib/multidb/balancer.rb +83 -0
- data/lib/multidb/configuration.rb +25 -0
- data/lib/multidb/model_extensions.rb +26 -0
- metadata +134 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 [name of plugin creator]
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
Multidb
|
2
|
+
=======
|
3
|
+
|
4
|
+
A simple, no-nonsense ActiveRecord extension which allows the application to switch
|
5
|
+
between multiple database connections, such as in a master/slave environment. For example:
|
6
|
+
|
7
|
+
Multidb.use(:slave) do
|
8
|
+
@posts = Post.all
|
9
|
+
end
|
10
|
+
|
11
|
+
The extension was developed in order to support PostgreSQL 9.0's new hot standby
|
12
|
+
support in a production environment.
|
13
|
+
|
14
|
+
Randomized balancing of multiple connections within a group is supported. In the
|
15
|
+
future, some kind of automatic balancing of read/write queries might be implemented.
|
16
|
+
|
17
|
+
Testet with Rails 2.3.11. No guarantees about Rails 3.
|
18
|
+
|
19
|
+
|
20
|
+
Comparison to other ActiveRecord extensions
|
21
|
+
===========================================
|
22
|
+
|
23
|
+
Unlike other, more full-featured extensions such as Octopus and Seamless Database Pool,
|
24
|
+
Multidb aims to be:
|
25
|
+
|
26
|
+
* Implemented using a minimal amount of
|
27
|
+
monkeypatching magic. The only part of ActiveRecord that is overriden is
|
28
|
+
`ActiveRecord::Base#connection`.
|
29
|
+
|
30
|
+
* Non-invasive. Very small amounts of configuration and changes to the client
|
31
|
+
application are required.
|
32
|
+
|
33
|
+
* Orthogonal. Unlike Octopus, for example, connections follow context:
|
34
|
+
|
35
|
+
Multidb.use(:master) do
|
36
|
+
@post = Post.find(1)
|
37
|
+
Multidb.use(:slave) do
|
38
|
+
@post.authors # This will use the slave
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
* Low-overhead. Since `connection` is called on every single
|
43
|
+
database operation, it needs to be fast. Which it is: Multidb's implementation of
|
44
|
+
`connection` incurs only a single hash lookup in `Thread.current`.
|
45
|
+
|
46
|
+
However, Multidb also has fewer features. At the moment it will _not_ automatically
|
47
|
+
split reads and writes between database backends.
|
48
|
+
|
49
|
+
|
50
|
+
Getting started
|
51
|
+
===============
|
52
|
+
|
53
|
+
In Rails 2.x applications without a `Gemfile`, add this to `environment.rb`:
|
54
|
+
|
55
|
+
config.gem 'ar-multidb'
|
56
|
+
|
57
|
+
In Bundler-based on Rails apps, add this to your `Gemfile`:
|
58
|
+
|
59
|
+
gem 'ar-multidb', :require => 'multidb'
|
60
|
+
|
61
|
+
You may also install it as a plugin:
|
62
|
+
|
63
|
+
script/plugin install git://github.com/alexstaubo/multidb.git
|
64
|
+
|
65
|
+
All that is needed is to set up your `database.yml` file:
|
66
|
+
|
67
|
+
production:
|
68
|
+
adapter: postgresql
|
69
|
+
database: myapp_production
|
70
|
+
username: ohoh
|
71
|
+
password: mymy
|
72
|
+
host: db1
|
73
|
+
multidb:
|
74
|
+
databases:
|
75
|
+
slave:
|
76
|
+
host: db-slave
|
77
|
+
|
78
|
+
Each database entry may be a hash or an array. So this also works:
|
79
|
+
|
80
|
+
production:
|
81
|
+
adapter: postgresql
|
82
|
+
database: myapp_production
|
83
|
+
username: ohoh
|
84
|
+
password: mymy
|
85
|
+
host: db1
|
86
|
+
multidb:
|
87
|
+
databases:
|
88
|
+
slave:
|
89
|
+
- host: db-slave1
|
90
|
+
- host: db-slave2
|
91
|
+
|
92
|
+
The database hashes follow the same format as the top-level adapter configuration. In
|
93
|
+
other words, each database connection may override the adapter, database name, username
|
94
|
+
and so on.
|
95
|
+
|
96
|
+
To use the connection, modify your code by wrapping database access logic in blocks:
|
97
|
+
|
98
|
+
Multidb.use(:slave) do
|
99
|
+
@posts = Post.all
|
100
|
+
end
|
101
|
+
|
102
|
+
To wrap entire controller requests, for example:
|
103
|
+
|
104
|
+
class PostsController < ApplicationController
|
105
|
+
around_filter :run_using_slave
|
106
|
+
|
107
|
+
def run_using_slave(&block)
|
108
|
+
Multidb.use(:slave, &block)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
You can also set the current connection for the remainder of the thread's execution:
|
113
|
+
|
114
|
+
Multidb.use(:slave)
|
115
|
+
# Do work
|
116
|
+
Multidb.use(:master)
|
117
|
+
|
118
|
+
Note that the symbol `:default` will (unless you override it) refer to the default
|
119
|
+
top-level ActiveRecord configuration.
|
120
|
+
|
121
|
+
|
122
|
+
Legal
|
123
|
+
=====
|
124
|
+
|
125
|
+
Copyright (c) 2011 Alexander Staubo. Released under the MIT license. See the file LICENSE.
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/lib/ar-multidb.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'multidb'
|
data/lib/multidb.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'multidb/configuration'
|
2
|
+
require 'multidb/model_extensions'
|
3
|
+
require 'multidb/balancer'
|
4
|
+
|
5
|
+
module Multidb
|
6
|
+
class << self
|
7
|
+
|
8
|
+
def install!
|
9
|
+
configure!
|
10
|
+
if @configuration and @configuration.raw_configuration[:databases].any?
|
11
|
+
ActiveRecord::Base.class_eval do
|
12
|
+
include Multidb::ModelExtensions
|
13
|
+
end
|
14
|
+
@balancer = Balancer.new(@configuration)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :balancer
|
19
|
+
|
20
|
+
delegate :use, :get, :to => :balancer
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
Multidb.install!
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Multidb
|
2
|
+
|
3
|
+
class Candidate
|
4
|
+
def initialize(config)
|
5
|
+
adapter = config[:adapter]
|
6
|
+
begin
|
7
|
+
require "active_record/connection_adapters/#{adapter}_adapter"
|
8
|
+
rescue LoadError
|
9
|
+
raise "Please install the #{adapter} adapter: `gem install activerecord-#{adapter}-adapter` (#{$!})"
|
10
|
+
end
|
11
|
+
@connection_pool = ActiveRecord::ConnectionAdapters::ConnectionPool.new(
|
12
|
+
ActiveRecord::Base::ConnectionSpecification.new(config, "#{adapter}_connection"))
|
13
|
+
end
|
14
|
+
|
15
|
+
def connection
|
16
|
+
@connection_pool.connection
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Balancer
|
21
|
+
|
22
|
+
def initialize(configuration)
|
23
|
+
@candidates = {}.with_indifferent_access
|
24
|
+
@configuration = configuration
|
25
|
+
@configuration.raw_configuration[:databases].each_pair do |name, config|
|
26
|
+
configs = config.is_a?(Array) ? config : [config]
|
27
|
+
configs.each do |config|
|
28
|
+
candidate = Candidate.new(@configuration.default_adapter.merge(config))
|
29
|
+
@candidates[name] ||= []
|
30
|
+
@candidates[name].push(candidate)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
@default_candidate = Candidate.new(@configuration.default_adapter)
|
34
|
+
unless @candidates.include?(:default)
|
35
|
+
@candidates[:default] = [@default_candidate]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def get(name, &block)
|
40
|
+
candidates = @candidates[name] || []
|
41
|
+
raise ArgumentError, "No such database connection '#{name}'" if candidates.blank?
|
42
|
+
candidate = candidates.respond_to?(:sample) ?
|
43
|
+
candidates.sample : candidates[rand(candidates.length)]
|
44
|
+
block_given? ? yield(candidate) : candidate
|
45
|
+
end
|
46
|
+
|
47
|
+
def use(name, &block)
|
48
|
+
result = nil
|
49
|
+
get(name) do |candidate|
|
50
|
+
connection = candidate.connection
|
51
|
+
if block_given?
|
52
|
+
previous_connection, Thread.current[:multidb_connection] =
|
53
|
+
Thread.current[:multidb_connection], connection
|
54
|
+
begin
|
55
|
+
result = yield
|
56
|
+
ensure
|
57
|
+
Thread.current[:multidb_connection] = previous_connection
|
58
|
+
end
|
59
|
+
result
|
60
|
+
else
|
61
|
+
result = Thread.current[:multidb_connection] = connection
|
62
|
+
end
|
63
|
+
end
|
64
|
+
result
|
65
|
+
end
|
66
|
+
|
67
|
+
def current_connection
|
68
|
+
Thread.current[:multidb_connection] ||= @default_candidate.connection
|
69
|
+
end
|
70
|
+
|
71
|
+
class << self
|
72
|
+
def use(name, &block)
|
73
|
+
Multidb.balancer.use(name, &block)
|
74
|
+
end
|
75
|
+
|
76
|
+
def current_connection
|
77
|
+
Multidb.balancer.current_connection
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Multidb
|
2
|
+
|
3
|
+
class << self
|
4
|
+
|
5
|
+
def configure!
|
6
|
+
activerecord_config = ActiveRecord::Base.connection_pool.connection.instance_variable_get(:@config).dup.with_indifferent_access
|
7
|
+
default_adapter, configuration_hash = activerecord_config, activerecord_config.delete(:multidb)
|
8
|
+
@configuration = Configuration.new(default_adapter, configuration_hash)
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :configuration
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
class Configuration
|
16
|
+
def initialize(default_adapter, configuration_hash)
|
17
|
+
@default_adapter = default_adapter
|
18
|
+
@raw_configuration = configuration_hash
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_reader :default_adapter
|
22
|
+
attr_reader :raw_configuration
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Multidb
|
2
|
+
module ModelExtensions
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def append_features(base)
|
6
|
+
base.extend(ClassMethods)
|
7
|
+
base.class_eval do
|
8
|
+
include Multidb::ModelExtensions::InstanceMethods
|
9
|
+
class << self
|
10
|
+
alias_method_chain :connection, :multidb
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module ClassMethods
|
17
|
+
def connection_with_multidb
|
18
|
+
Multidb.balancer.current_connection
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module InstanceMethods
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ar-multidb
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Alexander Staubo
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-05-18 00:00:00 +02:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
type: :runtime
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 7
|
29
|
+
segments:
|
30
|
+
- 2
|
31
|
+
- 2
|
32
|
+
version: "2.2"
|
33
|
+
name: activesupport
|
34
|
+
version_requirements: *id001
|
35
|
+
prerelease: false
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
type: :runtime
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 7
|
44
|
+
segments:
|
45
|
+
- 2
|
46
|
+
- 2
|
47
|
+
version: "2.2"
|
48
|
+
name: activerecord
|
49
|
+
version_requirements: *id002
|
50
|
+
prerelease: false
|
51
|
+
- !ruby/object:Gem::Dependency
|
52
|
+
type: :runtime
|
53
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
hash: 7
|
59
|
+
segments:
|
60
|
+
- 2
|
61
|
+
- 2
|
62
|
+
version: "2.2"
|
63
|
+
name: activesupport
|
64
|
+
version_requirements: *id003
|
65
|
+
prerelease: false
|
66
|
+
- !ruby/object:Gem::Dependency
|
67
|
+
type: :runtime
|
68
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
hash: 7
|
74
|
+
segments:
|
75
|
+
- 2
|
76
|
+
- 2
|
77
|
+
version: "2.2"
|
78
|
+
name: activerecord
|
79
|
+
version_requirements: *id004
|
80
|
+
prerelease: false
|
81
|
+
description: Multidb is an ActiveRecord extension for switching between multiple database connections, such as master/slave setups.
|
82
|
+
email: alex@bengler.no
|
83
|
+
executables: []
|
84
|
+
|
85
|
+
extensions: []
|
86
|
+
|
87
|
+
extra_rdoc_files:
|
88
|
+
- LICENSE
|
89
|
+
- README.markdown
|
90
|
+
files:
|
91
|
+
- LICENSE
|
92
|
+
- README.markdown
|
93
|
+
- VERSION
|
94
|
+
- lib/ar-multidb.rb
|
95
|
+
- lib/multidb.rb
|
96
|
+
- lib/multidb/balancer.rb
|
97
|
+
- lib/multidb/configuration.rb
|
98
|
+
- lib/multidb/model_extensions.rb
|
99
|
+
has_rdoc: true
|
100
|
+
homepage: http://github.com/alexstaubo/multidb
|
101
|
+
licenses: []
|
102
|
+
|
103
|
+
post_install_message:
|
104
|
+
rdoc_options: []
|
105
|
+
|
106
|
+
require_paths:
|
107
|
+
- lib
|
108
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
109
|
+
none: false
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
hash: 3
|
114
|
+
segments:
|
115
|
+
- 0
|
116
|
+
version: "0"
|
117
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
|
+
none: false
|
119
|
+
requirements:
|
120
|
+
- - ">="
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
hash: 3
|
123
|
+
segments:
|
124
|
+
- 0
|
125
|
+
version: "0"
|
126
|
+
requirements: []
|
127
|
+
|
128
|
+
rubyforge_project:
|
129
|
+
rubygems_version: 1.5.0
|
130
|
+
signing_key:
|
131
|
+
specification_version: 3
|
132
|
+
summary: Multidb is an ActiveRecord extension for switching between multiple database connections, such as master/slave setups.
|
133
|
+
test_files: []
|
134
|
+
|