db-charmer 1.8.4 → 1.9.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.
- checksums.yaml +15 -0
- data/CHANGES +19 -0
- data/README.rdoc +12 -537
- data/lib/db_charmer.rb +17 -52
- data/lib/db_charmer/active_record/class_attributes.rb +42 -27
- data/lib/db_charmer/active_record/connection_switching.rb +17 -14
- data/lib/db_charmer/active_record/db_magic.rb +3 -2
- data/lib/db_charmer/connection_factory.rb +13 -5
- data/lib/db_charmer/connection_proxy.rb +30 -1
- data/lib/db_charmer/force_slave_reads.rb +27 -8
- data/lib/db_charmer/sharding/stub_connection.rb +6 -0
- data/lib/db_charmer/version.rb +2 -2
- data/lib/db_charmer/with_remapped_databases.rb +49 -0
- data/lib/tasks/databases.rake +5 -1
- metadata +14 -26
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
MDA4YjJhNmIzN2FlMzUzZDM5MzMwOWNhMTI0NTk3MmJhNDY1YjA3OA==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
MmFiMTJhMjE0MTk2ZTIyOTJmYzZmMDZjZTUwZWI5OWIwZTY3YzE4MQ==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
NTBkMzNmZGY3Yjc5ZDFmYTU4Y2YwMGFiNzlkZGQ2YTk3YmMwODAyNTQxMzZl
|
10
|
+
NWVhNzg3NjI3NDU4ZWM5ZjU2MDI5NzM4MzM4NDg4OWZhM2MyODFlNGIwNDBj
|
11
|
+
ZTAwMDJkMDEyNDY1OWI2Y2FkYzQ5MjMyNjBkODYxOTBjMTVhMmY=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
ZjIyOTNhZjIxYTc3YjM2ZTc2ZDY2NzJlMDAwODZlMzYxYTEyYzEyMjE2NzVj
|
14
|
+
NjMwMjQ0MDk0NzI2ZjM0OTE0Yjg0MzYzYTM4OGZlMGM2YmZlZjhiOTcwNGEy
|
15
|
+
ZTM3Y2EyOTIxZGQ5ZWJkZjhhYWU4NDQ4OGY2ZGE1YzczMmMwODI=
|
data/CHANGES
CHANGED
@@ -1,3 +1,22 @@
|
|
1
|
+
1.9.0 (2013-10-09):
|
2
|
+
|
3
|
+
Most of the major changes in this version are related to our initial push towards making
|
4
|
+
DbCharmer thread-safe and making sure it works correctly in multi-threaded environments.
|
5
|
+
|
6
|
+
Please note, that even though we now test DbCharmer in multi-threaded environments, we still
|
7
|
+
consider multi-threaded support experimental.
|
8
|
+
|
9
|
+
Bug fix: Improved Rails environment detection (sometimes DbCharmer would use
|
10
|
+
Rails-specific code while running in non-rails projects).
|
11
|
+
|
12
|
+
Bug fix: Make sure on_db() method could restore original connection after an exception is
|
13
|
+
raised from a DB driver during connection switching (Thanks to Dmytro Shteflyuk for finding
|
14
|
+
the issue and helping with debugging).
|
15
|
+
|
16
|
+
This is the first release that does not have a really strict constraint for point-releases
|
17
|
+
within the Rails 3.2.x branch.
|
18
|
+
|
19
|
+
----------------------------------------------------------------------------------------
|
1
20
|
1.8.4 (2013-03-18):
|
2
21
|
|
3
22
|
Bumped up rails dependencies up to 3.2.13.
|
data/README.rdoc
CHANGED
@@ -32,539 +32,6 @@ before using any of its connection management methods. Correct value here is a v
|
|
32
32
|
first-level section name.
|
33
33
|
|
34
34
|
|
35
|
-
== Easy ActiveRecord Connection Management
|
36
|
-
|
37
|
-
As a part of this plugin we've added +switch_connection_to+ method that accepts many different kinds
|
38
|
-
of db connections specifications and uses them on a model. We support:
|
39
|
-
|
40
|
-
1. Strings and symbols as the names of connection configuration blocks in database.yml.
|
41
|
-
2. ActiveRecord models (we'd use connection currently set up on a model).
|
42
|
-
3. Database connections (<tt>Model.connection</tt>)
|
43
|
-
4. Nil values to reset model to default connection.
|
44
|
-
|
45
|
-
Sample code:
|
46
|
-
|
47
|
-
class Foo < ActiveRecord::Model; end
|
48
|
-
|
49
|
-
Foo.switch_connection_to(:blah)
|
50
|
-
Foo.switch_connection_to('foo')
|
51
|
-
Foo.switch_connection_to(Bar)
|
52
|
-
Foo.switch_connection_to(Baz.connection)
|
53
|
-
Foo.switch_connection_to(nil)
|
54
|
-
|
55
|
-
Sample <tt>database.yml</tt> configuration:
|
56
|
-
|
57
|
-
production:
|
58
|
-
blah:
|
59
|
-
adapter: mysql
|
60
|
-
username: blah
|
61
|
-
host: blah.local
|
62
|
-
database: blah
|
63
|
-
|
64
|
-
foo:
|
65
|
-
adapter: mysql
|
66
|
-
username: foo
|
67
|
-
host: foo.local
|
68
|
-
database: foo
|
69
|
-
|
70
|
-
The +switch_connection_to+ method has an optional second parameter +should_exist+ which is true
|
71
|
-
by default. This parameter is used when the method is called with a string or a symbol connection
|
72
|
-
name and there is no such connection configuration in the database.yml file. If this parameter
|
73
|
-
is +true+, an exception would be raised, otherwise, the error would be ignored and no connection
|
74
|
-
change would happen.
|
75
|
-
|
76
|
-
This is really useful when in development mode or in a tests you do not want to create many different
|
77
|
-
databases on your local machine and just want to put all your tables in a single database.
|
78
|
-
|
79
|
-
*Warning*: All the connection switching calls would switch connection *only* for those classes the
|
80
|
-
method called on. You can't call the +switch_connection_to+ method and switch connection for a
|
81
|
-
base class in some hierarchy (for example, you can't switch AR::Base connection and see all your
|
82
|
-
models switched to the new connection, use the classic +establish_connection+ instead).
|
83
|
-
|
84
|
-
|
85
|
-
== Multiple DB Migrations
|
86
|
-
|
87
|
-
In every application that works with many databases, there is need in a convenient schema migrations mechanism.
|
88
|
-
|
89
|
-
All Rails users already have this mechanism - rails migrations. So in +DbCharmer+, we've made it possible
|
90
|
-
to seamlessly use multiple databases in Rails migrations.
|
91
|
-
|
92
|
-
There are two methods available in migrations to operate on more than one database:
|
93
|
-
|
94
|
-
1. Global connection change method - used to switch whole migration to a non-default database.
|
95
|
-
2. Block-level connection change method - could be used to do only a part of a migration on a non-default db.
|
96
|
-
|
97
|
-
Migration class example (global connection rewrite):
|
98
|
-
|
99
|
-
class MultiDbTest < ActiveRecord::Migration
|
100
|
-
db_magic :connection => :second_db
|
101
|
-
|
102
|
-
def self.up
|
103
|
-
create_table :test_table, :force => true do |t|
|
104
|
-
t.string :test_string
|
105
|
-
t.timestamps
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
def self.down
|
110
|
-
drop_table :test_table
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
Migration class example (block-level connection rewrite):
|
115
|
-
|
116
|
-
class MultiDbTest < ActiveRecord::Migration
|
117
|
-
def self.up
|
118
|
-
on_db :second_db do
|
119
|
-
create_table :test_table, :force => true do |t|
|
120
|
-
t.string :test_string
|
121
|
-
t.timestamps
|
122
|
-
end
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
def self.down
|
127
|
-
on_db :second_db { drop_table :test_table }
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
Migration class example (global connection rewrite, multiple connections with the same table):
|
132
|
-
(NOTE: both :connection and :connections can take an array of connections)
|
133
|
-
|
134
|
-
class MultiDbTest < ActiveRecord::Migration
|
135
|
-
db_magic :connections => [:second_db, :default]
|
136
|
-
|
137
|
-
def self.up
|
138
|
-
create_table :test_table, :force => true do |t|
|
139
|
-
t.string :test_string
|
140
|
-
t.timestamps
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
def self.down
|
145
|
-
drop_table :test_table
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
=== Default Migrations Connection
|
150
|
-
|
151
|
-
Starting with DbCharmer version 1.6.10 it is possible to call <tt>ActiveRecord::Migration.db_magic</tt>
|
152
|
-
and specify default migration connection that would be used by all migrations without
|
153
|
-
excplicitly switched connections. If you want to switch your migration to the default ActiveRecord
|
154
|
-
connection, just use <tt>db_magic :connection => :default</tt>.
|
155
|
-
|
156
|
-
=== Invalid Connection Names Handling
|
157
|
-
|
158
|
-
By default in all environments <tt>on_db</tt> and <tt>db_magic</tt> statments would fail if
|
159
|
-
specified connection does not exist in database.yml. It is possible to make +DbCharmer+
|
160
|
-
ignore such situations in non-production environments so that rails would create the tables
|
161
|
-
in your single database (especially useful in test databases).
|
162
|
-
|
163
|
-
This behaviour is controlled by the <tt>DbCharmer.connections_should_exist</tt>
|
164
|
-
configuration attribute which could be set from a rails initializer.
|
165
|
-
|
166
|
-
Warning: if in test environment you use separate connections and master-slave support
|
167
|
-
in DbCharmer, make sure you disable transactional fixtures support in Rails. Without
|
168
|
-
this change you're going to see all kinds of weird data visibility problems in your tests.
|
169
|
-
|
170
|
-
|
171
|
-
== Using Models in Master-Slave Environments
|
172
|
-
|
173
|
-
Master-slave replication is the most popular scale-out technique in a medium-sized and
|
174
|
-
large database-centric applications today. There are some rails plugins out there that help
|
175
|
-
developers to use slave servers in their models but none of them were flexible enough
|
176
|
-
for us to start using them in a huge application we work on.
|
177
|
-
|
178
|
-
So, we've been using ActsAsReadonlyable plugin for a long time and have made tons
|
179
|
-
of changes in its code over the time. But since that plugin has been abandoned
|
180
|
-
by its authors, we've decided to collect all of our master-slave code in one plugin
|
181
|
-
and release it for rails 2.2+. +DbCharmer+ adds the following features to Rails models:
|
182
|
-
|
183
|
-
|
184
|
-
=== Auto-Switching all Reads to the Slave(s)
|
185
|
-
|
186
|
-
When you create a model, you could use <tt>db_magic :slave => :blah</tt> or
|
187
|
-
<tt>db_magic :slaves => [ :foo, :bar ]</tt> commands in your model to set up reads
|
188
|
-
redirection mode when all your find/count/exist/etc methods will be reading data
|
189
|
-
from your slave (or a bunch of slaves in a round-robin manner). Here is an example:
|
190
|
-
|
191
|
-
class Foo < ActiveRecord::Base
|
192
|
-
db_magic :slave => :slave01
|
193
|
-
end
|
194
|
-
|
195
|
-
class Bar < ActiveRecord::Base
|
196
|
-
db_magic :slaves => [ :slave01, :slave02 ]
|
197
|
-
end
|
198
|
-
|
199
|
-
|
200
|
-
=== Default Connection Switching
|
201
|
-
|
202
|
-
If you have more than one master-slave cluster (or simply more than one database)
|
203
|
-
in your database environment, then you might want to change the default database
|
204
|
-
connection of some of your models. You could do that by using
|
205
|
-
<tt>db_magic :connection => :foo</tt> call from your models. Example:
|
206
|
-
|
207
|
-
class Foo < ActiveRecord::Base
|
208
|
-
db_magic :connection => :foo
|
209
|
-
end
|
210
|
-
|
211
|
-
Sample model on a separate master-slave cluster (so, separate main connection +
|
212
|
-
a slave connection):
|
213
|
-
|
214
|
-
class Bar < ActiveRecord::Base
|
215
|
-
db_magic :connection => :bar, :slave => :bar_slave
|
216
|
-
end
|
217
|
-
|
218
|
-
=== Per-Query Connection Management
|
219
|
-
|
220
|
-
Sometimes you have select queries that you know you want to run on the master.
|
221
|
-
This could happen for example when you have just added some data and need to read
|
222
|
-
it back and not sure if it made it all the way to the slave yet or no. For this
|
223
|
-
situation and a few others there is a set of methods we've added to ActiveRecord models:
|
224
|
-
|
225
|
-
1) +on_master+ - this method could be used in two forms: block form and proxy form.
|
226
|
-
In the block form you could force connection switch for a block of code:
|
227
|
-
|
228
|
-
User.on_master do
|
229
|
-
user = User.find_by_login('foo')
|
230
|
-
user.update_attributes!(:activated => true)
|
231
|
-
end
|
232
|
-
|
233
|
-
In the proxy form this method could be used to force one query to be performed on
|
234
|
-
the master database server:
|
235
|
-
|
236
|
-
Comment.on_master.last(:limit => 5)
|
237
|
-
User.on_master.find_by_activation_code(code)
|
238
|
-
User.on_master.exists?(:login => login, :password => password)
|
239
|
-
|
240
|
-
2) +on_slave+ - this method is used to force a query to be run on a slave even in
|
241
|
-
situations when it's been previously forced to use the master. If there is more
|
242
|
-
than one slave, one would be selected randomly. Tis method has two forms as
|
243
|
-
well: block and proxy.
|
244
|
-
|
245
|
-
3) <tt>on_db(connection)</tt> - this method is what makes two previous methods
|
246
|
-
possible. It is used to switch a model's connection to some db for a short block
|
247
|
-
of code or even for one statement (two forms). It accepts the same range of values
|
248
|
-
as the +switch_connection_to+ method does. Example:
|
249
|
-
|
250
|
-
Comment.on_db(:olap).count
|
251
|
-
Post.on_db(:foo).find(:first)
|
252
|
-
|
253
|
-
By default in development and test environments you could use non-existing connections in your
|
254
|
-
<tt>on_db</tt> calls and rails would send all your queries to a single default database. In
|
255
|
-
production <tt>on_db</tt> won't accept non-existing names.
|
256
|
-
|
257
|
-
This behaviour is controlled by the <tt>DbCharmer.connections_should_exist</tt>
|
258
|
-
configuration attribute which could be set from a rails initializer.
|
259
|
-
|
260
|
-
|
261
|
-
=== Forced Slave Reads
|
262
|
-
|
263
|
-
In some cases we could have models that are too important to be used in default "send all
|
264
|
-
reads to the slave" mode, but we still would like to be able to switch them to this mode
|
265
|
-
sometimes. For example, you could have +User+ model, which you would like to keep from
|
266
|
-
lagging with your slaves because users do not like to see outdated information about their
|
267
|
-
accounts. But in some cases (like logged-out profile page views, etc) it would be perfectly
|
268
|
-
reasonable to switch all reads to the slave.
|
269
|
-
|
270
|
-
For this use-case starting with +DbCharmer+ release 1.7.0 we have a feature called forced
|
271
|
-
slave reads. It consists of a few separate small features that together make it really
|
272
|
-
powerful:
|
273
|
-
|
274
|
-
1) <tt>:force_slave_reads => false</tt> option for +ActiveRecord+'s <tt>db_magic</tt> method.
|
275
|
-
This option could be used to disable automated slave reads on your models so that you could
|
276
|
-
call <tt>on_slave</tt> or use other methods to enable slave reads when you need it. Example:
|
277
|
-
|
278
|
-
class User < ActiveRecord::Base
|
279
|
-
db_magic :slave => slave01, :force_slave_reads => false
|
280
|
-
end
|
281
|
-
|
282
|
-
2) <tt>force_slave_reads</tt> +ActionController+ class method. This method could be used to
|
283
|
-
enable per-controller (when called with no arguments), or per-action (<tt>:only</tt> and
|
284
|
-
<tt>:except</tt> params) forced reads from slaves. This is really useful for actions in
|
285
|
-
which you know you could tolerate some slave lag so all your models with slaves defined will
|
286
|
-
send their reads to slaves. Example:
|
287
|
-
|
288
|
-
class ProfilesController < Application
|
289
|
-
force_slave_reads :except => [ :login, :logout ]
|
290
|
-
...
|
291
|
-
end
|
292
|
-
|
293
|
-
3) <tt>force_slave_reads!</tt> +ActionController+ instance method, that could be used within
|
294
|
-
your actions or in controller filters to temporarily switch your models to forced slave reads
|
295
|
-
mode. This method could be useful for cases when the same actions could be called by logged-in
|
296
|
-
and anonymous users. Then you could authorize users in <tt>before_filter</tt> and call
|
297
|
-
<tt>force_slave_reads!</tt> method for anonymous page views.
|
298
|
-
|
299
|
-
class ProfilesController < Application
|
300
|
-
before_filter do
|
301
|
-
force_slave_reads! unless current_user
|
302
|
-
end
|
303
|
-
...
|
304
|
-
end
|
305
|
-
|
306
|
-
Notice: Before using this method you need to enable +ActionController+ support in +DbCharmer+.
|
307
|
-
You need to call <tt>DbCharmer.enable_controller_magic!</tt> method from your project
|
308
|
-
initialization code.
|
309
|
-
|
310
|
-
4) <tt>DbCharmer.force_slave_reads</tt> method that could be used with a block of ruby code
|
311
|
-
and would enable forced slave reads mode until the end of the block execution. This is really
|
312
|
-
powerful feature allowing high granularity in your control of forced slave reads mode. Example:
|
313
|
-
|
314
|
-
DbCharmer.force_slave_reads do
|
315
|
-
...
|
316
|
-
total_users = User.count
|
317
|
-
...
|
318
|
-
end
|
319
|
-
|
320
|
-
Notice: At this point the feature considered beta and should be used with caution. It is fully covered
|
321
|
-
with tests, but there still could be unexpected issues when used in real-world applications.
|
322
|
-
|
323
|
-
|
324
|
-
=== Associations Connection Management
|
325
|
-
|
326
|
-
ActiveRecord models can have an associations with each other and since every model has its
|
327
|
-
own database connections, it becomes pretty hard to manage connections in a chained calls
|
328
|
-
like <tt>User.posts.count</tt>. With a class-only connection switching methods this call
|
329
|
-
would look like the following if we'd want to count posts on a separate database:
|
330
|
-
|
331
|
-
Post.on_db(:olap) { User.posts.count }
|
332
|
-
|
333
|
-
Apparently this is not the best way to write the code and we've implemented an <tt>on_*</tt>
|
334
|
-
methods on associations as well so you could do things like this:
|
335
|
-
|
336
|
-
@user.posts.on_db(:olap).count
|
337
|
-
@user.posts.on_slave.find(:title => 'Hello, world!')
|
338
|
-
|
339
|
-
Notice: Since ActiveRecord associations implemented as proxies for resulting
|
340
|
-
objects/collections, it is possible to use our connection switching methods even without
|
341
|
-
chained methods:
|
342
|
-
|
343
|
-
@post.user.on_slave - would return post's author
|
344
|
-
@photo.owner.on_slave - would return photo's owner
|
345
|
-
|
346
|
-
|
347
|
-
Starting with +DbCharmer+ release 1.4 it is possible to use prefix notation for has_many
|
348
|
-
and HABTM associations connection switching:
|
349
|
-
|
350
|
-
@user.on_db(:foo).posts
|
351
|
-
@user.on_slave.posts
|
352
|
-
|
353
|
-
|
354
|
-
=== Named Scopes Support
|
355
|
-
|
356
|
-
To make it easier for +DbCharmer+ users to use connections switching methods with named scopes,
|
357
|
-
we've added <tt>on_*</tt> methods support on the scopes as well. All the following scope chains
|
358
|
-
would do exactly the same way (the query would be executed on the :foo database connection):
|
359
|
-
|
360
|
-
Post.on_db(:foo).published.with_comments.spam_marked.count
|
361
|
-
Post.published.on_db(:foo).with_comments.spam_marked.count
|
362
|
-
Post.published.with_comments.on_db(:foo).spam_marked.count
|
363
|
-
Post.published.with_comments.spam_marked.on_db(:foo).count
|
364
|
-
|
365
|
-
And now, add this feature to our associations support and here is what we could do:
|
366
|
-
|
367
|
-
@user.on_db(:archive).posts.published.all
|
368
|
-
@user.posts.on_db(:olap).published.count
|
369
|
-
@user.posts.published.on_db(:foo).first
|
370
|
-
|
371
|
-
|
372
|
-
=== Bulk Connection Management
|
373
|
-
|
374
|
-
Sometimes you want to run code where a large number of tables may be used, and you'd like
|
375
|
-
them all to use an alternate database. You can now do this:
|
376
|
-
|
377
|
-
DbCharmer.with_remapped_databases(:logs => :big_logs_slave) { ... }
|
378
|
-
|
379
|
-
Any model whose default database is +:logs+ (e.g., <tt>db_charmer :connection => :logs</tt>)
|
380
|
-
will now have its connection switched to +:big_logs_slave+ in that block. This is lower
|
381
|
-
precedence than any other +DbCharmer+ method, so <tt>Model.on_db(:foo).find(...)</tt> and
|
382
|
-
such things will still use the database they specify, not the one that model was remapped
|
383
|
-
to.
|
384
|
-
|
385
|
-
You can specify any number of remappings at once, and you can also use +:master+ as a database
|
386
|
-
name that matches any model that has not had its connection set by +DbCharmer+ at all.
|
387
|
-
|
388
|
-
*Note*: +DbCharmer+ works via +alias_method_chain+ in model classes. It is very careful
|
389
|
-
to only patch the models it needs to. However, if you use +with_remapped_databases+ and
|
390
|
-
remap the default database (+:master+), then it has no choice but to patch all subclasses
|
391
|
-
of +ActiveRecord::Base+. This should not cause any serious problems or any big performance
|
392
|
-
impact, but it is worth noting.
|
393
|
-
|
394
|
-
|
395
|
-
== Simple Sharding Support
|
396
|
-
|
397
|
-
Starting with the release 1.6.0 of +DbCharmer+ we have added support for simple database sharding
|
398
|
-
to our ActiveRecord extensions. Even though this feature is tested in production, we do not recommend
|
399
|
-
using it in your applications without complete understanding of the principles of its work.
|
400
|
-
|
401
|
-
At this point we support four sharding methods:
|
402
|
-
|
403
|
-
1) +range+ - really simple sharding method that allows you to take a table, slice is to a set of
|
404
|
-
smaller tables with pre-defined ranges of primary keys and then put those smaller tables to
|
405
|
-
different databases/servers. This could be useful for situations where you have a huge table that
|
406
|
-
is slowly growing and you just want to keep it simple and split the table load into a few servers
|
407
|
-
without building any complex sharding schemes.
|
408
|
-
|
409
|
-
2) +hash_map+ - pretty simple sharding method that allows you to take a table and slice it to a set
|
410
|
-
of smaller tables by some key that has a pre-defined key of values. For example, list of US mailing
|
411
|
-
addresses could be sharded by states, where you'd be able to define which states are stored in which
|
412
|
-
databases/servers.
|
413
|
-
|
414
|
-
3) +db_block_map+ - this is a really complex sharding method that allows you to shard your table into a
|
415
|
-
set of small fixed-size blocks that then would be assigned to a set of shards (databases/servers).
|
416
|
-
Whenever you would need an additional blocks they would be allocated automatically and then balanced
|
417
|
-
across the shards you have defined in your database. This method could be used to scale out huge
|
418
|
-
tables with hundreds of millions to billions of rows and allows relatively easy re-sharding techniques
|
419
|
-
to be implemented on top.
|
420
|
-
|
421
|
-
4) +db_block_group_map+ - really similar to the +db_block_map+ method with a single difference: this method
|
422
|
-
allows you to have a set of databases (table groups) on each server and every group would be handled as a
|
423
|
-
separate shard of data. This approach is really useful for pre-sharding of your data before scaling your
|
424
|
-
application out. You can easily start with one server, having 10-20-50 separate databases, and then
|
425
|
-
move those databases to different servers as you see your database outgrow one machine.
|
426
|
-
|
427
|
-
|
428
|
-
=== How to enable sharding?
|
429
|
-
|
430
|
-
To enable sharding extensions you need to take a few things:
|
431
|
-
|
432
|
-
1) Create a Rails initializer (on run this code when you initialize your script/application) with a
|
433
|
-
set of sharded connections defined. Each connection would have a name, sharding method and an optional
|
434
|
-
set of parameters to initialize the sharding method of your choice.
|
435
|
-
|
436
|
-
2) Specify sharding connection you want to use in your models.
|
437
|
-
|
438
|
-
3) Specify the shard you want to use before doing any operations on your models.
|
439
|
-
|
440
|
-
For more details please check out the following documentation sections.
|
441
|
-
|
442
|
-
|
443
|
-
=== Sharded Connections
|
444
|
-
|
445
|
-
Sharded connection is a simple abstractions that allows you to specify all sharding parameters for a
|
446
|
-
cluster in one place and then use this centralized configuration in your models. Here are a few examples
|
447
|
-
of sharded connections initizlization calls:
|
448
|
-
|
449
|
-
1) Sample range-based sharded connection:
|
450
|
-
|
451
|
-
TEXTS_SHARDING_RANGES = {
|
452
|
-
0...100 => :shard1,
|
453
|
-
100..200 => :shard2,
|
454
|
-
:default => :shard3
|
455
|
-
}
|
456
|
-
|
457
|
-
DbCharmer::Sharding.register_connection(
|
458
|
-
:name => :texts,
|
459
|
-
:method => :range,
|
460
|
-
:ranges => TEXTS_SHARDING_RANGES
|
461
|
-
)
|
462
|
-
|
463
|
-
2) Sample hash map sharded connection:
|
464
|
-
|
465
|
-
SHARDING_MAP = {
|
466
|
-
'US' => :us_users,
|
467
|
-
'CA' => :ca_users,
|
468
|
-
:default => :other_users
|
469
|
-
}
|
470
|
-
|
471
|
-
DbCharmer::Sharding.register_connection(
|
472
|
-
:name => :users,
|
473
|
-
:method => :hash_map,
|
474
|
-
:map => SHARDING_MAP
|
475
|
-
)
|
476
|
-
|
477
|
-
3) Sample database block map sharded connection:
|
478
|
-
|
479
|
-
DbCharmer::Sharding.register_connection(
|
480
|
-
:name => :social,
|
481
|
-
:method => :db_block_map,
|
482
|
-
:block_size => 10000, # Number of keys per block
|
483
|
-
:map_table => :event_shards_map, # Table with blocks to shards mapping
|
484
|
-
:shards_table => :event_shards_info, # Shards connection information table
|
485
|
-
:connection => :social_shard_info # What connection to use to read the map
|
486
|
-
)
|
487
|
-
|
488
|
-
After your sharded connection is defined, you could use it in your models:
|
489
|
-
|
490
|
-
class Text < ActiveRecord::Base
|
491
|
-
db_magic :sharded => {
|
492
|
-
:key => :id,
|
493
|
-
:sharded_connection => :texts
|
494
|
-
}
|
495
|
-
end
|
496
|
-
|
497
|
-
class Event < ActiveRecord::Base
|
498
|
-
set_table_name :timeline_events
|
499
|
-
|
500
|
-
db_magic :sharded => {
|
501
|
-
:key => :to_uid,
|
502
|
-
:sharded_connection => :social
|
503
|
-
}
|
504
|
-
end
|
505
|
-
|
506
|
-
|
507
|
-
=== Switching connections in sharded models
|
508
|
-
|
509
|
-
Every time you need to perform an operation on a sharded model, you need to specify on which shard
|
510
|
-
you want to do it. We have a method for this which would look familiar for the people that use
|
511
|
-
+DbCharmer+ for non-sharded environments since it looks and works just like those per-query
|
512
|
-
connection management methods:
|
513
|
-
|
514
|
-
Event.shard_for(10).find(:conditions => { :to_uid => 123 }, :limit => 5)
|
515
|
-
Text.shard_for(123).find_by_id(123)
|
516
|
-
|
517
|
-
There is another method that could be used with range and hash_map sharding methods, this method
|
518
|
-
allows you to switch to the default shard:
|
519
|
-
|
520
|
-
Text.on_default_shard.create(:body => 'hello', :user_id => 123)
|
521
|
-
|
522
|
-
And finally, there is a method that allows you to run your code on each shard in the system (at this
|
523
|
-
point the method is supported in db_block_map and db_block_group_map methods only):
|
524
|
-
|
525
|
-
Event.on_each_shard { |event| event.delete_all }
|
526
|
-
|
527
|
-
|
528
|
-
=== Defining your own sharding methods
|
529
|
-
|
530
|
-
It is possible with +DbCharmer+ for the users to define their own sharding methods. You need to do a
|
531
|
-
few things to implement your very own sharding scheme:
|
532
|
-
|
533
|
-
1) Create a class with a name <tt>DbCharmer::Sharding::Method::YourOwnName</tt>
|
534
|
-
|
535
|
-
2) Implement at least a constructor <tt>initialize(config)</tt> and a lookup instance
|
536
|
-
method <tt>shard_for_key(key)</tt> that would return either a connection name from <tt>database.yml</tt>
|
537
|
-
file or just a hash of connection parameters for rails connection adapters.
|
538
|
-
|
539
|
-
3) Register your sharded connection using the following call:
|
540
|
-
|
541
|
-
DbCharmer::Sharding.register_connection(
|
542
|
-
:name => :some_name,
|
543
|
-
:method => :your_own_name, # your sharding method class name in lower case
|
544
|
-
... some additional parameters if needed ...
|
545
|
-
)
|
546
|
-
|
547
|
-
4) Use your sharded connection as any standard one.
|
548
|
-
|
549
|
-
|
550
|
-
=== Adding support for default shards in your custom sharding methods
|
551
|
-
|
552
|
-
If you want to be able to use +on_default_shard+ method on your custom-sharded models, you
|
553
|
-
need to do two things:
|
554
|
-
|
555
|
-
1) implement <tt>support_default_shard?</tt> instance method on your sharded class that
|
556
|
-
would return +true+ if you do support default shard specification and +false+ otherwise.
|
557
|
-
|
558
|
-
2) implement <tt>:default</tt> symbol support as a key in your +shard_for_key+ method.
|
559
|
-
|
560
|
-
|
561
|
-
=== Adding support for shards enumeration in your custom sharding methods
|
562
|
-
|
563
|
-
To add shards enumeration support to your custom-sharded models you need to implement
|
564
|
-
an instance method +shard_connections+ on your class. This method should return an array of
|
565
|
-
sharding connection names or connection configurations to be used to establish connections in a loop.
|
566
|
-
|
567
|
-
|
568
35
|
== Documentation/Questions
|
569
36
|
|
570
37
|
For more information about the library, please visit our site at http://dbcharmer.net.
|
@@ -589,14 +56,14 @@ Build status is: {<img src="https://secure.travis-ci.org/kovyrin/db-charmer.png?
|
|
589
56
|
|
590
57
|
At the moment we have the following build matrix:
|
591
58
|
* Rails versions:
|
592
|
-
- 2.
|
59
|
+
- 2.3
|
593
60
|
- 3.0
|
594
61
|
- 3.1
|
595
62
|
- 3.2
|
596
63
|
* Ruby versions:
|
597
64
|
- 1.8.7
|
598
|
-
-
|
599
|
-
-
|
65
|
+
- 1.9.3 (Rails 3.0+ only)
|
66
|
+
- 2.0.0 (Rails 3.2+ only)
|
600
67
|
* Databases:
|
601
68
|
- MySQL
|
602
69
|
|
@@ -604,11 +71,19 @@ In addition to CI testing, this gem is used in production on Scribd.com (one of
|
|
604
71
|
sites in the world) with Ruby Enterprise Edition and Rails 2.2, Rails 2.3, Sinatra and plain
|
605
72
|
Rack applications.
|
606
73
|
|
607
|
-
Starting with version 1.8.0 we support Rails versions
|
74
|
+
Starting with version 1.8.0 we support Rails versions 3.2.8 and higher. Please note, that Rails 3.2.4
|
608
75
|
is not officially supported. Your code may work on that version, but no bug reports will be
|
609
76
|
accepted about this version.
|
610
77
|
|
611
78
|
|
79
|
+
== Is it Thread-Safe?
|
80
|
+
|
81
|
+
Starting with version 1.9.0 we have started working on making the code thread-safe and making sure
|
82
|
+
DbCharmer works correctly in multi-threaded environments. At this moment we consider multi-threaded
|
83
|
+
mode experimental. If you use it and it works for you - please let us know, if it does not - please
|
84
|
+
make sure to file a ticket so that we could improve the code and make it work in your situation.
|
85
|
+
|
86
|
+
|
612
87
|
== Who are the authors?
|
613
88
|
|
614
89
|
This plugin has been created in Scribd.com for our internal use and then the sources were opened for
|