ar-multidb 0.1.0

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