my_stuff-multidb 0.0.3 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +1 -1
- data/bin/my_stuff-multidb-unmangle +2 -2
- data/lib/my_stuff/multidb.rb +15 -117
- data/lib/my_stuff/multidb/connection.rb +108 -0
- data/lib/my_stuff/multidb/core_ext/base.rb +59 -0
- data/lib/my_stuff/multidb/mangling.rb +38 -0
- data/lib/my_stuff/multidb/sharded.rb +27 -13
- data/lib/my_stuff/multidb/unsharded.rb +19 -9
- metadata +10 -17
- data/README.rdoc +0 -65
- data/lib/my_stuff/multidb/base.rb +0 -58
data/COPYING
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c) 2011-
|
1
|
+
Copyright (c) 2011-2012, Fred Emmott <copyright@fredemmott.co.uk>
|
2
2
|
|
3
3
|
Permission to use, copy, modify, and/or distribute this software for any
|
4
4
|
purpose with or without fee is hereby granted, provided that the above
|
@@ -5,11 +5,11 @@ _FILE = File.readlink(__FILE__) rescue __FILE__
|
|
5
5
|
_DIR = File.expand_path(File.dirname(_FILE))
|
6
6
|
$LOAD_PATH.push(File.expand_path(_DIR + '/../lib'))
|
7
7
|
|
8
|
-
require 'my_stuff/multidb'
|
8
|
+
require 'my_stuff/multidb/mangling'
|
9
9
|
|
10
10
|
def filter line
|
11
11
|
line.gsub(/MYSTUFF_MULTIDB_DB_[a-z0-9]+/){ |mangled|
|
12
|
-
data = MyStuff::MultiDB.unmangle(mangled)
|
12
|
+
data = MyStuff::MultiDB::Mangling.unmangle(mangled)
|
13
13
|
"<%s:%d/%s>" % [
|
14
14
|
data[:host],
|
15
15
|
data[:port],
|
data/lib/my_stuff/multidb.rb
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
# Copyright 2011-present Fred Emmott. See COPYING file.
|
2
2
|
|
3
|
+
require 'my_stuff/multidb/connection'
|
4
|
+
require 'my_stuff/multidb/core_ext/base'
|
5
|
+
|
3
6
|
require 'base64'
|
4
|
-
|
7
|
+
|
8
|
+
require 'rubygems'
|
9
|
+
require 'active_record'
|
5
10
|
|
6
11
|
module MyStuff
|
7
12
|
# =Example
|
@@ -55,135 +60,28 @@ module MyStuff
|
|
55
60
|
# So, you end up with an ActiveRecord class like:
|
56
61
|
# MyStuff::MultiDB::MANGLED_DATABASE_NAME::YourModule::YourClass
|
57
62
|
module MultiDB
|
58
|
-
def self.open_connections
|
59
|
-
Hash.new.tap do |result|
|
60
|
-
constants.each do |name|
|
61
|
-
if name.to_s.starts_with? 'MYSTUFF_MULTIDB_DB'
|
62
|
-
klass = const_get(name, false)
|
63
|
-
checked_out = klass.connection_pool.instance_eval { @checked_out.size }
|
64
|
-
result[name] = checked_out if checked_out > 0
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
# Takes a module name and converts it to connection details.
|
71
|
-
#
|
72
|
-
# Example:
|
73
|
-
# mangled:: <tt>MyStuff::MultiDB::MYSTUFF_MULTIDB_DB_747970686f6e2e66726564656d6d6f74742e636f2e756b2c333330362c747474::ServiceLocator::Tier</tt>
|
74
|
-
# unmanagled:: <tt>MyStuff::MultiDB::<mysql://typhon.fredemmott.co.uk:3306/ttt>::ServiceLocator::Tier</tt>
|
75
|
-
#
|
76
|
-
# The initscript cat-log and tail-log commands will do this unmanggling for you.
|
77
|
-
def self.unmangle name
|
78
|
-
db = name.sub /^:?MYSTUFF_MULTIDB_DB_/, ''
|
79
|
-
# Format: "MYSTUFF_MULTIDB_DB_" + hex("host,port,database")
|
80
|
-
junk, host, port, database = db.each_char.each_slice(2).reduce(String.new){ |m,*nibbles| m += "%c" % nibbles.join.hex }.split('!')
|
81
|
-
{
|
82
|
-
:host => host,
|
83
|
-
:port => port.to_i,
|
84
|
-
:database => database,
|
85
|
-
}
|
86
|
-
end
|
87
|
-
|
88
63
|
def self.included othermod # :nodoc:
|
89
64
|
class <<othermod
|
90
|
-
def
|
91
|
-
MyStuff::MultiDB.
|
92
|
-
end
|
93
|
-
def with_slave *args; raise NotImplementedError.new "Available in MyStuff::MultiDB::Unsharded"; end
|
94
|
-
def with_master *args; raise NotImplementedError.new "Available in MyStuff::MultiDB::Unsharded"; end
|
95
|
-
def with_master_for *args; raise NotImplementedError.new "Available in MyStuff::MultiDB::Sharded"; end
|
96
|
-
def with_master_for_new; raise NotImplementedError.new "Available in MyStuff::MultiDB::Sharded"; end
|
97
|
-
def with_slave_for *args; raise NotImplementedError.new "Available in MyStuff::MultiDB::Sharded"; end
|
98
|
-
def sharded?; raise NotImplementedError.new; end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
# Fetch/create the magic classes.
|
103
|
-
def self.for_spec spec, mod # :nodoc:
|
104
|
-
db_key = ("MYSTUFF_MULTIDB_DB_" + ("!%s!%s!%s" % [
|
105
|
-
spec[:host],
|
106
|
-
spec[:port],
|
107
|
-
spec[:database],
|
108
|
-
]
|
109
|
-
).each_byte.map{ |x| "%x" % x }.join).to_sym
|
110
|
-
|
111
|
-
# db: class representing the logical database
|
112
|
-
if self.const_defined? db_key
|
113
|
-
db = self.const_get(db_key)
|
114
|
-
else
|
115
|
-
l 'connecting to database: ', spec
|
116
|
-
db = Class.new ActiveRecord::Base
|
117
|
-
def db.abstract_class?; true; end
|
118
|
-
self.const_set(db_key, db)
|
119
|
-
db.establish_connection(spec)
|
120
|
-
end
|
121
|
-
|
122
|
-
mod_key = mod.name.split(':').last.to_sym
|
123
|
-
# db_mod: class representing the module from the AR definition
|
124
|
-
if db.const_defined? mod_key, false
|
125
|
-
db_mod = db.const_get(mod_key, false)
|
126
|
-
else
|
127
|
-
db_mod = Module.new
|
128
|
-
db.const_set(mod_key, db_mod)
|
129
|
-
db_mod.send(:define_singleton_method, :magic_database) { db }
|
130
|
-
db_mod.send(:define_singleton_method, :muggle) { mod }
|
131
|
-
|
132
|
-
# klass: a specific table's AR class
|
133
|
-
def db_mod.const_missing name
|
134
|
-
klass = muggle.const_get(name)
|
135
|
-
klass_sym = klass.name.split(':').last.to_sym
|
136
|
-
|
137
|
-
# subklass: klass tied to a specific DB
|
138
|
-
subklass = Class.new(klass)
|
139
|
-
const_set klass_sym, subklass
|
140
|
-
define_singleton_method(klass_sym) { subklass }
|
141
|
-
|
142
|
-
subklass.send :include, MyStuff::MultiDB::Base
|
143
|
-
|
144
|
-
# Make associations work.
|
145
|
-
klass.reflect_on_all_associations.each do |reflection|
|
146
|
-
subklass.send(
|
147
|
-
reflection.macro, # eg :has_one
|
148
|
-
reflection.name, # eg :some_table
|
149
|
-
reflection.options
|
150
|
-
)
|
151
|
-
end
|
152
|
-
|
153
|
-
return subklass
|
65
|
+
def with_spec spec, &block
|
66
|
+
MyStuff::MultiDB.with_spec(self, spec, &block)
|
154
67
|
end
|
155
68
|
end
|
156
|
-
|
157
|
-
return db_mod
|
158
69
|
end
|
159
70
|
|
160
|
-
def self.with_spec
|
161
|
-
|
162
|
-
|
163
|
-
klass.magic_database.connection_pool.with_connection do
|
164
|
-
yield klass, spec
|
165
|
-
end
|
166
|
-
end
|
71
|
+
def self.with_spec original_module, spec, &block # :nodoc:
|
72
|
+
ar_base = Connection.base_class_for_spec(spec)
|
73
|
+
rebased_module = ar_base.rebased_module(original_module)
|
167
74
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
if id == :new
|
172
|
-
spec = db.spec_for_new
|
75
|
+
ar_base.connection_pool.with_connection do
|
76
|
+
if block.arity == 1
|
77
|
+
block.call rebased_module
|
173
78
|
else
|
174
|
-
|
79
|
+
block.call rebased_module, spec
|
175
80
|
end
|
176
|
-
elsif writable == :read_only
|
177
|
-
spec = db.spec_for_slave(id)
|
178
|
-
end
|
179
|
-
|
180
|
-
with_spec(db, spec) do |*args|
|
181
|
-
yield *args
|
182
81
|
end
|
183
82
|
end
|
184
83
|
end
|
185
84
|
end
|
186
85
|
|
187
|
-
require 'my_stuff/multidb/base'
|
188
86
|
require 'my_stuff/multidb/sharded'
|
189
87
|
require 'my_stuff/multidb/unsharded'
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# Copyright 2011-present Fred Emmott. See COPYING file.
|
2
|
+
|
3
|
+
require 'my_stuff/multidb/mangling'
|
4
|
+
require 'my_stuff/multidb/core_ext/base'
|
5
|
+
|
6
|
+
require 'rubygems'
|
7
|
+
require 'active_record'
|
8
|
+
|
9
|
+
module MyStuff
|
10
|
+
module MultiDB
|
11
|
+
module Connections; end
|
12
|
+
|
13
|
+
class Connection < ActiveRecord::Base
|
14
|
+
class << self
|
15
|
+
def base_class_for_spec spec
|
16
|
+
name = MyStuff::MultiDB::Mangling.mangle(spec).to_sym
|
17
|
+
if Connections.const_defined?(name)
|
18
|
+
return Connections.const_get(name)
|
19
|
+
end
|
20
|
+
|
21
|
+
connection = Class.new(self)
|
22
|
+
Connections.const_set(name, connection)
|
23
|
+
connection.establish_connection(spec)
|
24
|
+
connection
|
25
|
+
end
|
26
|
+
|
27
|
+
def abstract_class?
|
28
|
+
true
|
29
|
+
end
|
30
|
+
|
31
|
+
def rebased_module original_module
|
32
|
+
name = original_module.name.gsub(':', '__').to_sym
|
33
|
+
|
34
|
+
if have_rebased_module?(name)
|
35
|
+
self.const_get(name)
|
36
|
+
else
|
37
|
+
self.rebase_module! name, original_module
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def rebased_model name, original_module, rebased_module
|
42
|
+
if rebased_module.const_defined? name
|
43
|
+
rebased_module.const_get(name)
|
44
|
+
else
|
45
|
+
self.rebase_model! name, original_module, rebased_module
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
protected
|
50
|
+
|
51
|
+
def rebase_model! name, original_module, rebased_module
|
52
|
+
name = name.to_sym
|
53
|
+
original = original_module.const_get(name)
|
54
|
+
|
55
|
+
rebased = Class.new(original)
|
56
|
+
rebased_module.const_set(name, rebased)
|
57
|
+
rebased.send :include, MyStuff::MultiDB::CoreExt::Base
|
58
|
+
|
59
|
+
# Make associations work.
|
60
|
+
rebased.reflect_on_all_associations.each do |reflection|
|
61
|
+
rebased.send(
|
62
|
+
reflection.macro, # eg :has_one
|
63
|
+
reflection.name, # eg :some_table
|
64
|
+
reflection.options
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
return rebased
|
69
|
+
end
|
70
|
+
|
71
|
+
def rebase_module! name, original_module
|
72
|
+
ar_base = self
|
73
|
+
rebased = Module.new
|
74
|
+
ar_base.const_set(name, rebased)
|
75
|
+
|
76
|
+
# Generate wrapper classes on demand
|
77
|
+
def rebased.const_missing (name)
|
78
|
+
MyStuff::MultiDB::Connection.rebased_model(
|
79
|
+
name,
|
80
|
+
muggle,
|
81
|
+
magic_database
|
82
|
+
)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Not using define_singleton_method, as that's not in 1.8.7
|
86
|
+
singleton = class << rebased; self; end
|
87
|
+
singleton.send(:define_method, :magic_database) { ar_base }
|
88
|
+
singleton.send(:define_method, :muggle) { original_module }
|
89
|
+
|
90
|
+
return rebased
|
91
|
+
end
|
92
|
+
|
93
|
+
def have_rebased_module? name
|
94
|
+
# 1.8.7: const_defined? does not include constants defined
|
95
|
+
# in other modules, and it only takes 1 arg
|
96
|
+
# 1.9: it does include, and needs a second argument to change
|
97
|
+
# this.
|
98
|
+
old_const_defined = self.method(:const_defined?).arity == 1
|
99
|
+
new_const_defined = !old_const_defined
|
100
|
+
return (
|
101
|
+
(old_const_defined && self.const_defined?(name)) ||
|
102
|
+
(new_const_defined && self.const_defined?(name, false))
|
103
|
+
)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# Copyright 2011-present Fred Emmott. See COPYING file.
|
2
|
+
|
3
|
+
module MyStuff
|
4
|
+
module MultiDB
|
5
|
+
module CoreExt
|
6
|
+
# Mixin for subclasses of ActiveRecord::Base.
|
7
|
+
#
|
8
|
+
# This deals with routing connections via the 'magic'
|
9
|
+
# ActiveRecord::Base subclass for the appropriate database.
|
10
|
+
module Base # :nodoc:
|
11
|
+
def connection
|
12
|
+
self.class.connection
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.included(klass)
|
16
|
+
klass.extend(ClassMethods)
|
17
|
+
end
|
18
|
+
|
19
|
+
module ClassMethods
|
20
|
+
def base_class
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def magic_database
|
25
|
+
@magic_database ||=
|
26
|
+
self.name.split('::')[0..-2].join('::').constantize
|
27
|
+
end
|
28
|
+
|
29
|
+
def arel_engine
|
30
|
+
magic_database.arel_engine
|
31
|
+
end
|
32
|
+
|
33
|
+
def connection
|
34
|
+
magic_database.connection
|
35
|
+
end
|
36
|
+
|
37
|
+
def connection_pool
|
38
|
+
magic_database.connection_pool
|
39
|
+
end
|
40
|
+
|
41
|
+
def abstract_class?; true; end
|
42
|
+
|
43
|
+
def model_name
|
44
|
+
# Rails form_for wants this
|
45
|
+
ActiveModel::Name.new(
|
46
|
+
self.name.split('::').last.tap{|s| def s.name; self; end}
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
def inherited(child)
|
51
|
+
def child.abstract_class?; false; end
|
52
|
+
def child.base_class; self; end
|
53
|
+
super
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# Copyright 2011-2012 Fred Emmott. See COPYING file.
|
2
|
+
|
3
|
+
module MyStuff
|
4
|
+
module MultiDB
|
5
|
+
module Mangling
|
6
|
+
# Takes a module name and converts it to connection details.
|
7
|
+
#
|
8
|
+
# Example:
|
9
|
+
# mangled:: <tt>MyStuff::MultiDB::MYSTUFF_MULTIDB_DB_747970686f6e2e66726564656d6d6f74742e636f2e756b2c333330362c747474::ServiceLocator::Tier</tt>
|
10
|
+
# unmanagled:: <tt>MyStuff::MultiDB::<mysql://typhon.fredemmott.co.uk:3306/ttt>::ServiceLocator::Tier</tt>
|
11
|
+
#
|
12
|
+
# The initscript cat-log and tail-log commands will do this unmanggling for you.
|
13
|
+
def self.unmangle name
|
14
|
+
db = name.sub /^:?MYSTUFF_MULTIDB_DB_/, ''
|
15
|
+
# Format: "MYSTUFF_MULTIDB_DB_" + hex("host,port,database")
|
16
|
+
host, port, database = db.each_char.each_slice(2).reduce(String.new){ |m,*nibbles| m += "%c" % nibbles.join.hex }.split('!')
|
17
|
+
{
|
18
|
+
:host => host,
|
19
|
+
:port => port.to_i,
|
20
|
+
:database => database,
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
# Create a valid constant name from an ActiveRecord spec.
|
25
|
+
#
|
26
|
+
# This can be revered by {#unmangle}.
|
27
|
+
def self.mangle spec
|
28
|
+
'MYSTUFF_MULTIDB_DB_' + (
|
29
|
+
"%s!%s!%s" % [
|
30
|
+
spec[:host] || spec['host'],
|
31
|
+
spec[:port] || spec['port'],
|
32
|
+
spec[:database] || spec['database'],
|
33
|
+
]
|
34
|
+
).each_byte.map{ |x| "%x" % x }.join
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -1,27 +1,41 @@
|
|
1
1
|
# Copyright 2011-present Fred Emmott. See COPYING file.
|
2
2
|
|
3
|
+
require 'my_stuff/multidb'
|
4
|
+
|
3
5
|
module MyStuff
|
4
6
|
module MultiDB
|
5
7
|
module Sharded
|
6
8
|
def self.included othermod # :nodoc:
|
7
9
|
othermod.send :include, MyStuff::MultiDB
|
10
|
+
|
8
11
|
class <<othermod
|
9
|
-
def
|
10
|
-
|
11
|
-
|
12
|
-
self
|
13
|
-
|
12
|
+
def with_master_for id, &block
|
13
|
+
MyStuff::MultiDB.with_spec(
|
14
|
+
self,
|
15
|
+
self.spec_for_master(id),
|
16
|
+
&block
|
17
|
+
)
|
14
18
|
end
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
+
|
20
|
+
def with_master_for_new &block
|
21
|
+
MyStuff::MultiDB.with_spec(
|
22
|
+
self,
|
23
|
+
self.spec_for_new,
|
24
|
+
&block
|
25
|
+
)
|
19
26
|
end
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
27
|
+
|
28
|
+
def with_slave_for id, &block
|
29
|
+
MyStuff::MultiDB.with_spec(
|
30
|
+
self,
|
31
|
+
self.spec_for_slave(id),
|
32
|
+
&block
|
33
|
+
)
|
24
34
|
end
|
35
|
+
|
36
|
+
def spec_for_new; raise NotImplementedError.new; end
|
37
|
+
def spec_for_master(shard_id); raise NotImplementedError.new; end
|
38
|
+
def spec_for_slave(shard_id); raise NotImplementedError.new; end
|
25
39
|
end
|
26
40
|
end
|
27
41
|
end
|
@@ -1,4 +1,6 @@
|
|
1
1
|
# Copyright 2011-present Fred Emmott. See COPYING file.
|
2
|
+
#
|
3
|
+
require 'my_stuff/multidb'
|
2
4
|
|
3
5
|
module MyStuff
|
4
6
|
module MultiDB
|
@@ -10,18 +12,26 @@ module MyStuff
|
|
10
12
|
module Unsharded
|
11
13
|
def self.included othermod # :nodoc:
|
12
14
|
othermod.send :include, MyStuff::MultiDB
|
15
|
+
|
13
16
|
class <<othermod
|
14
|
-
def
|
15
|
-
|
16
|
-
|
17
|
-
self,
|
18
|
-
|
17
|
+
def with_master &block
|
18
|
+
MyStuff::MultiDB.with_spec(
|
19
|
+
self,
|
20
|
+
self.spec_for_master,
|
21
|
+
&block
|
22
|
+
)
|
19
23
|
end
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
+
|
25
|
+
def with_slave &block
|
26
|
+
MyStuff::MultiDB.with_spec(
|
27
|
+
self,
|
28
|
+
self.spec_for_slave,
|
29
|
+
&block
|
30
|
+
)
|
24
31
|
end
|
32
|
+
|
33
|
+
def spec_for_master; raise NotImplementedError.new; end
|
34
|
+
def spec_for_slave; raise NotImplementedError.new; end
|
25
35
|
end
|
26
36
|
end
|
27
37
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: my_stuff-multidb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,30 +9,22 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2012-02-13 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
|
-
name:
|
16
|
-
requirement: &
|
15
|
+
name: activerecord
|
16
|
+
requirement: &70321517374640 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version:
|
22
|
-
type: :runtime
|
23
|
-
prerelease: false
|
24
|
-
version_requirements: *70289582101220
|
25
|
-
- !ruby/object:Gem::Dependency
|
26
|
-
name: activerecord
|
27
|
-
requirement: &70289582100640 !ruby/object:Gem::Requirement
|
28
|
-
none: false
|
29
|
-
requirements:
|
21
|
+
version: '3.2'
|
30
22
|
- - ! '>='
|
31
23
|
- !ruby/object:Gem::Version
|
32
|
-
version:
|
24
|
+
version: 3.2.1
|
33
25
|
type: :runtime
|
34
26
|
prerelease: false
|
35
|
-
version_requirements: *
|
27
|
+
version_requirements: *70321517374640
|
36
28
|
description: ''
|
37
29
|
email:
|
38
30
|
- mail@fredemmott.co.uk
|
@@ -42,9 +34,10 @@ extensions: []
|
|
42
34
|
extra_rdoc_files: []
|
43
35
|
files:
|
44
36
|
- COPYING
|
45
|
-
- README.rdoc
|
46
37
|
- bin/my_stuff-multidb-unmangle
|
47
|
-
- lib/my_stuff/multidb/
|
38
|
+
- lib/my_stuff/multidb/connection.rb
|
39
|
+
- lib/my_stuff/multidb/core_ext/base.rb
|
40
|
+
- lib/my_stuff/multidb/mangling.rb
|
48
41
|
- lib/my_stuff/multidb/sharded.rb
|
49
42
|
- lib/my_stuff/multidb/unsharded.rb
|
50
43
|
- lib/my_stuff/multidb.rb
|
data/README.rdoc
DELETED
@@ -1,65 +0,0 @@
|
|
1
|
-
= Overview
|
2
|
-
|
3
|
-
This provides an API that's relatively convenient for:
|
4
|
-
* Reading from slaves
|
5
|
-
* Using multiple shards
|
6
|
-
|
7
|
-
= Usage
|
8
|
-
|
9
|
-
module MySpecProvider
|
10
|
-
def spec_for_new
|
11
|
-
{ :adapter => 'mysql', :host => ...}
|
12
|
-
end
|
13
|
-
|
14
|
-
def spec_for_master shard_id
|
15
|
-
...
|
16
|
-
end
|
17
|
-
|
18
|
-
def spec_for_slave shard_id
|
19
|
-
...
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
module MyDB
|
24
|
-
class Widget < ActiveRecord::Base; end
|
25
|
-
include MyStuff::MultiDB::Sharded
|
26
|
-
extend MySpecProvider
|
27
|
-
end
|
28
|
-
|
29
|
-
MyDB.with_master_for_new do |db,spec|
|
30
|
-
db::Widget.create(...)
|
31
|
-
end
|
32
|
-
|
33
|
-
MyDB.with_slave_for(shard_id) do |db|
|
34
|
-
db::Widget.find(record_id);
|
35
|
-
...
|
36
|
-
end
|
37
|
-
|
38
|
-
Another option, if you only do primary key lookups, is to encode the shard
|
39
|
-
ID into the record id.
|
40
|
-
|
41
|
-
For more, see:
|
42
|
-
|
43
|
-
* ./examples/run
|
44
|
-
* comments at top of lib/mystuff/multidb.rb
|
45
|
-
|
46
|
-
= How It Works
|
47
|
-
|
48
|
-
For every spec, it defines a new sub-module of MyStuff::MultiDB, which
|
49
|
-
encodes the database details, and creates new subclasses of your
|
50
|
-
ActiveRecord::Base classes, within these modules. For example:
|
51
|
-
|
52
|
-
$ bin/my_stuff-multidb-unmangle MyStuff::MultiDB::MYSTUFF_MULTIDB_DB_216c6f63616c686f7374213333303621::MyDB::Widget
|
53
|
-
MyStuff::MultiDB::<localhost:3306/>::MyDB::Widget
|
54
|
-
$
|
55
|
-
|
56
|
-
There's also lots of deep voodoo making the ActiveRecord stack use the right
|
57
|
-
connection details :)
|
58
|
-
|
59
|
-
= Caveats
|
60
|
-
|
61
|
-
You can not do cross-shard operations simply, eg joins.
|
62
|
-
|
63
|
-
= Copying
|
64
|
-
|
65
|
-
See the COPYING file.
|
@@ -1,58 +0,0 @@
|
|
1
|
-
# Copyright 2011-present Fred Emmott. See COPYING file.
|
2
|
-
|
3
|
-
module MyStuff
|
4
|
-
module MultiDB
|
5
|
-
module Base # :nodoc:
|
6
|
-
def connection
|
7
|
-
self.class.connection
|
8
|
-
end
|
9
|
-
|
10
|
-
def self.included(klass)
|
11
|
-
klass.extend(ClassMethods)
|
12
|
-
end
|
13
|
-
|
14
|
-
module ClassMethods
|
15
|
-
def base_class
|
16
|
-
self
|
17
|
-
end
|
18
|
-
|
19
|
-
def with_spec spec
|
20
|
-
MyStuff::MultiDB.with_spec(
|
21
|
-
db, spec
|
22
|
-
) { |*args| yield *args }
|
23
|
-
end
|
24
|
-
|
25
|
-
def magic_database
|
26
|
-
@magic_database ||= self.name.split('::').tap(&:pop).join('::').constantize.magic_database
|
27
|
-
end
|
28
|
-
|
29
|
-
def arel_engine
|
30
|
-
magic_database.arel_engine
|
31
|
-
end
|
32
|
-
|
33
|
-
def connection
|
34
|
-
magic_database.connection
|
35
|
-
end
|
36
|
-
|
37
|
-
def connection_pool
|
38
|
-
magic_database.connection_pool
|
39
|
-
end
|
40
|
-
|
41
|
-
def abstract_class?; true; end
|
42
|
-
|
43
|
-
def model_name
|
44
|
-
# Rails form_for wants this
|
45
|
-
ActiveModel::Name.new(
|
46
|
-
self.name.split('::').last.tap{|s| def s.name; self; end}
|
47
|
-
)
|
48
|
-
end
|
49
|
-
|
50
|
-
def inherited(child)
|
51
|
-
def child.abstract_class?; false; end
|
52
|
-
def child.base_class; self; end
|
53
|
-
super
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|