changed SQLT::Parser::DBIC so that in FK constraints, both the field order for the...
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Storage / DBI / Replicated.pm
CommitLineData
2156bbdd 1package DBIx::Class::Storage::DBI::Replicated;
f5d3a5de 2
2bf79155 3use Moose;
26ab719a 4use DBIx::Class::Storage::DBI;
2bf79155 5use DBIx::Class::Storage::DBI::Replicated::Pool;
26ab719a 6use DBIx::Class::Storage::DBI::Replicated::Balancer;
7use Scalar::Util qw(blessed);
2bf79155 8
26ab719a 9extends 'DBIx::Class::Storage::DBI', 'Moose::Object';
2bf79155 10
11=head1 NAME
12
13DBIx::Class::Storage::DBI::Replicated - ALPHA Replicated database support
14
15=head1 SYNOPSIS
16
17The Following example shows how to change an existing $schema to a replicated
18storage type, add some replicated (readonly) databases, and perform reporting
955a6df6 19tasks.
2bf79155 20
21 ## Change storage_type in your schema class
106d5f3b 22 $schema->storage_type( ['::DBI::Replicated', {balancer=>'::Random'}] );
2bf79155 23
24 ## Add some slaves. Basically this is an array of arrayrefs, where each
25 ## arrayref is database connect information
26
955a6df6 27 $schema->storage->connect_replicants(
2bf79155 28 [$dsn1, $user, $pass, \%opts],
29 [$dsn1, $user, $pass, \%opts],
30 [$dsn1, $user, $pass, \%opts],
2bf79155 31 );
2bf79155 32
33=head1 DESCRIPTION
34
35Warning: This class is marked ALPHA. We are using this in development and have
36some basic test coverage but the code hasn't yet been stressed by a variety
37of databases. Individual DB's may have quirks we are not aware of. Please
38use this in development and pass along your experiences/bug fixes.
39
40This class implements replicated data store for DBI. Currently you can define
41one master and numerous slave database connections. All write-type queries
42(INSERT, UPDATE, DELETE and even LAST_INSERT_ID) are routed to master
43database, all read-type queries (SELECTs) go to the slave database.
44
45Basically, any method request that L<DBIx::Class::Storage::DBI> would normally
46handle gets delegated to one of the two attributes: L</master_storage> or to
47L</current_replicant_storage>. Additionally, some methods need to be distributed
48to all existing storages. This way our storage class is a drop in replacement
49for L<DBIx::Class::Storage::DBI>.
50
51Read traffic is spread across the replicants (slaves) occuring to a user
52selected algorithm. The default algorithm is random weighted.
53
54TODO more details about the algorithm.
55
56=head1 ATTRIBUTES
57
58This class defines the following attributes.
59
26ab719a 60=head2 pool_type
2bf79155 61
26ab719a 62Contains the classname which will instantiate the L</pool> object. Defaults
63to: L<DBIx::Class::Storage::DBI::Replicated::Pool>.
2bf79155 64
65=cut
66
26ab719a 67has 'pool_type' => (
2bf79155 68 is=>'ro',
69 isa=>'ClassName',
106d5f3b 70 lazy_build=>1,
26ab719a 71 handles=>{
72 'create_pool' => 'new',
2bf79155 73 },
74);
75
f068a139 76=head2 pool_args
77
78Contains a hashref of initialized information to pass to the Balancer object.
79See L<DBIx::Class::Storage::Replicated::Pool> for available arguments.
80
81=cut
82
83has 'pool_args' => (
84 is=>'ro',
85 isa=>'HashRef',
86 lazy=>1,
87 required=>1,
88 default=>sub { {} },
89);
90
91
26ab719a 92=head2 balancer_type
2bf79155 93
94The replication pool requires a balance class to provider the methods for
95choose how to spread the query load across each replicant in the pool.
96
97=cut
98
26ab719a 99has 'balancer_type' => (
2bf79155 100 is=>'ro',
101 isa=>'ClassName',
106d5f3b 102 lazy_build=>1,
26ab719a 103 handles=>{
104 'create_balancer' => 'new',
2bf79155 105 },
106);
107
17b05c13 108=head2 balancer_args
109
110Contains a hashref of initialized information to pass to the Balancer object.
f068a139 111See L<DBIx::Class::Storage::Replicated::Balancer> for available arguments.
17b05c13 112
113=cut
114
115has 'balancer_args' => (
116 is=>'ro',
117 isa=>'HashRef',
f068a139 118 lazy=>1,
119 required=>1,
120 default=>sub { {} },
17b05c13 121);
122
26ab719a 123=head2 pool
2bf79155 124
26ab719a 125Is a <DBIx::Class::Storage::DBI::Replicated::Pool> or derived class. This is a
126container class for one or more replicated databases.
2bf79155 127
128=cut
129
26ab719a 130has 'pool' => (
2bf79155 131 is=>'ro',
132 isa=>'DBIx::Class::Storage::DBI::Replicated::Pool',
133 lazy_build=>1,
26ab719a 134 handles=>[qw/
cb6ec758 135 connect_replicants
26ab719a 136 replicants
137 has_replicants
26ab719a 138 /],
2bf79155 139);
140
26ab719a 141=head2 balancer
2bf79155 142
26ab719a 143Is a <DBIx::Class::Storage::DBI::Replicated::Balancer> or derived class. This
144is a class that takes a pool (<DBIx::Class::Storage::DBI::Replicated::Pool>)
2bf79155 145
26ab719a 146=cut
2bf79155 147
26ab719a 148has 'balancer' => (
149 is=>'ro',
150 isa=>'DBIx::Class::Storage::DBI::Replicated::Balancer',
151 lazy_build=>1,
17b05c13 152 handles=>[qw/auto_validate_every/],
26ab719a 153);
2bf79155 154
cb6ec758 155=head2 master
156
157The master defines the canonical state for a pool of connected databases. All
158the replicants are expected to match this databases state. Thus, in a classic
159Master / Slaves distributed system, all the slaves are expected to replicate
160the Master's state as quick as possible. This is the only database in the
161pool of databases that is allowed to handle write traffic.
162
163=cut
164
165has 'master' => (
166 is=> 'ro',
167 isa=>'DBIx::Class::Storage::DBI',
168 lazy_build=>1,
169);
170
cb6ec758 171=head1 ATTRIBUTES IMPLEMENTING THE DBIx::Storage::DBI INTERFACE
172
173The following methods are delegated all the methods required for the
174L<DBIx::Class::Storage::DBI> interface.
175
176=head2 read_handler
177
178Defines an object that implements the read side of L<BIx::Class::Storage::DBI>.
179
180=cut
181
182has 'read_handler' => (
183 is=>'rw',
184 isa=>'Object',
185 lazy_build=>1,
186 handles=>[qw/
187 select
188 select_single
189 columns_info_for
190 /],
191);
192
cb6ec758 193=head2 write_handler
194
195Defines an object that implements the write side of L<BIx::Class::Storage::DBI>.
196
197=cut
198
199has 'write_handler' => (
200 is=>'ro',
201 isa=>'Object',
202 lazy_build=>1,
203 lazy_build=>1,
204 handles=>[qw/
205 on_connect_do
206 on_disconnect_do
207 connect_info
208 throw_exception
209 sql_maker
210 sqlt_type
211 create_ddl_dir
212 deployment_statements
213 datetime_parser
214 datetime_parser_type
215 last_insert_id
216 insert
217 insert_bulk
218 update
219 delete
220 dbh
221 txn_do
222 txn_commit
223 txn_rollback
224 sth
225 deploy
226 schema
227 /],
228);
229
26ab719a 230=head1 METHODS
2bf79155 231
26ab719a 232This class defines the following methods.
2bf79155 233
cb6ec758 234=head2 new
2bf79155 235
cb6ec758 236L<DBIx::Class::Schema> when instantiating it's storage passed itself as the
237first argument. We need to invoke L</new> on the underlying parent class, make
238sure we properly give it a L<Moose> meta class, and then correctly instantiate
239our attributes. Basically we pass on whatever the schema has in it's class
240data for 'storage_type_args' to our replicated storage type.
2bf79155 241
242=cut
243
cb6ec758 244sub new {
245 my $class = shift @_;
246 my $schema = shift @_;
247 my $storage_type_args = shift @_;
248 my $obj = $class->SUPER::new($schema, $storage_type_args, @_);
106d5f3b 249
250 ## Hate to do it this way, but can't seem to get advice on the attribute working right
251 ## maybe we can do a type and coercion for it.
252 if( $storage_type_args->{balancer_type} && $storage_type_args->{balancer_type}=~m/^::/) {
253 $storage_type_args->{balancer_type} = 'DBIx::Class::Storage::DBI::Replicated::Balancer'.$storage_type_args->{balancer_type};
254 eval "require $storage_type_args->{balancer_type}";
255 }
256
cb6ec758 257 return $class->meta->new_object(
258 __INSTANCE__ => $obj,
259 %$storage_type_args,
260 @_,
261 );
2bf79155 262}
263
cb6ec758 264=head2 _build_master
2bf79155 265
cb6ec758 266Lazy builder for the L</master> attribute.
2bf79155 267
268=cut
269
cb6ec758 270sub _build_master {
271 DBIx::Class::Storage::DBI->new;
2bf79155 272}
273
106d5f3b 274=head2 _build_pool_type
275
276Lazy builder for the L</pool_type> attribute.
277
278=cut
279
280sub _build_pool_type {
281 return 'DBIx::Class::Storage::DBI::Replicated::Pool';
282}
283
26ab719a 284=head2 _build_pool
2bf79155 285
26ab719a 286Lazy builder for the L</pool> attribute.
2bf79155 287
288=cut
289
26ab719a 290sub _build_pool {
4a607d7a 291 my $self = shift @_;
f068a139 292 $self->create_pool(%{$self->pool_args});
2bf79155 293}
294
106d5f3b 295=head2 _build_balancer_type
296
297Lazy builder for the L</balancer_type> attribute.
298
299=cut
300
301sub _build_balancer_type {
17b05c13 302 return 'DBIx::Class::Storage::DBI::Replicated::Balancer::First';
106d5f3b 303}
304
26ab719a 305=head2 _build_balancer
2bf79155 306
cb6ec758 307Lazy builder for the L</balancer> attribute. This takes a Pool object so that
308the balancer knows which pool it's balancing.
2bf79155 309
310=cut
311
26ab719a 312sub _build_balancer {
313 my $self = shift @_;
106d5f3b 314 $self->create_balancer(
315 pool=>$self->pool,
17b05c13 316 master=>$self->master,
317 %{$self->balancer_args},);
2bf79155 318}
319
cb6ec758 320=head2 _build_write_handler
2bf79155 321
cb6ec758 322Lazy builder for the L</write_handler> attribute. The default is to set this to
323the L</master>.
50336325 324
325=cut
326
cb6ec758 327sub _build_write_handler {
328 return shift->master;
329}
50336325 330
cb6ec758 331=head2 _build_read_handler
2bf79155 332
cb6ec758 333Lazy builder for the L</read_handler> attribute. The default is to set this to
334the L</balancer>.
2bf79155 335
336=cut
337
cb6ec758 338sub _build_read_handler {
339 return shift->balancer;
340}
50336325 341
cb6ec758 342=head2 around: connect_replicants
2bf79155 343
cb6ec758 344All calls to connect_replicants needs to have an existing $schema tacked onto
345top of the args, since L<DBIx::Storage::DBI> needs it.
955a6df6 346
cb6ec758 347=cut
955a6df6 348
cb6ec758 349around 'connect_replicants' => sub {
350 my ($method, $self, @args) = @_;
351 $self->$method($self->schema, @args);
955a6df6 352};
2bf79155 353
2bf79155 354=head2 all_storages
355
356Returns an array of of all the connected storage backends. The first element
357in the returned array is the master, and the remainings are each of the
358replicants.
359
360=cut
361
362sub all_storages {
363 my $self = shift @_;
364
26ab719a 365 return grep {defined $_ && blessed $_} (
366 $self->master,
367 $self->replicants,
2bf79155 368 );
369}
370
cb6ec758 371=head2 set_reliable_storage
372
373Sets the current $schema to be 'reliable', that is all queries, both read and
374write are sent to the master
375
376=cut
377
378sub set_reliable_storage {
379 my $self = shift @_;
380 my $schema = $self->schema;
381 my $write_handler = $self->schema->storage->write_handler;
382
383 $schema->storage->read_handler($write_handler);
384}
385
386=head2 set_balanced_storage
387
388Sets the current $schema to be use the </balancer> for all reads, while all
389writea are sent to the master only
390
391=cut
392
393sub set_balanced_storage {
394 my $self = shift @_;
395 my $schema = $self->schema;
396 my $write_handler = $self->schema->storage->balancer;
397
398 $schema->storage->read_handler($write_handler);
399}
2bf79155 400
401=head2 connected
402
403Check that the master and at least one of the replicants is connected.
404
405=cut
406
407sub connected {
408 my $self = shift @_;
409
410 return
26ab719a 411 $self->master->connected &&
412 $self->pool->connected_replicants;
2bf79155 413}
414
2bf79155 415=head2 ensure_connected
416
417Make sure all the storages are connected.
418
419=cut
420
421sub ensure_connected {
422 my $self = shift @_;
26ab719a 423 foreach my $source ($self->all_storages) {
2bf79155 424 $source->ensure_connected(@_);
425 }
426}
427
2bf79155 428=head2 limit_dialect
429
430Set the limit_dialect for all existing storages
431
432=cut
433
434sub limit_dialect {
435 my $self = shift @_;
26ab719a 436 foreach my $source ($self->all_storages) {
437 $source->limit_dialect(@_);
2bf79155 438 }
439}
440
2bf79155 441=head2 quote_char
442
443Set the quote_char for all existing storages
444
445=cut
446
447sub quote_char {
448 my $self = shift @_;
26ab719a 449 foreach my $source ($self->all_storages) {
450 $source->quote_char(@_);
2bf79155 451 }
452}
453
2bf79155 454=head2 name_sep
455
456Set the name_sep for all existing storages
457
458=cut
459
460sub name_sep {
461 my $self = shift @_;
26ab719a 462 foreach my $source ($self->all_storages) {
2bf79155 463 $source->name_sep(@_);
464 }
465}
466
2bf79155 467=head2 set_schema
468
469Set the schema object for all existing storages
470
471=cut
472
473sub set_schema {
474 my $self = shift @_;
26ab719a 475 foreach my $source ($self->all_storages) {
2bf79155 476 $source->set_schema(@_);
477 }
478}
479
2bf79155 480=head2 debug
481
482set a debug flag across all storages
483
484=cut
485
486sub debug {
487 my $self = shift @_;
26ab719a 488 foreach my $source ($self->all_storages) {
2bf79155 489 $source->debug(@_);
490 }
491}
492
2bf79155 493=head2 debugobj
494
495set a debug object across all storages
496
497=cut
498
499sub debugobj {
500 my $self = shift @_;
26ab719a 501 foreach my $source ($self->all_storages) {
2bf79155 502 $source->debugobj(@_);
503 }
504}
505
2bf79155 506=head2 debugfh
507
508set a debugfh object across all storages
509
510=cut
511
512sub debugfh {
513 my $self = shift @_;
26ab719a 514 foreach my $source ($self->all_storages) {
2bf79155 515 $source->debugfh(@_);
516 }
517}
518
2bf79155 519=head2 debugcb
520
521set a debug callback across all storages
522
523=cut
524
525sub debugcb {
526 my $self = shift @_;
26ab719a 527 foreach my $source ($self->all_storages) {
2bf79155 528 $source->debugcb(@_);
529 }
530}
531
2bf79155 532=head2 disconnect
533
534disconnect everything
535
536=cut
537
538sub disconnect {
539 my $self = shift @_;
26ab719a 540 foreach my $source ($self->all_storages) {
2bf79155 541 $source->disconnect(@_);
542 }
543}
544
f5d3a5de 545=head1 AUTHOR
546
547Norbert Csongrádi <bert@cpan.org>
548
549Peter Siklósi <einon@einon.hu>
550
2156bbdd 551John Napiorkowski <john.napiorkowski@takkle.com>
552
f5d3a5de 553=head1 LICENSE
554
555You may distribute this code under the same terms as Perl itself.
556
557=cut
558
5591;