my_stuff-multidb 0.0.3 → 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/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
|