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 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
+