Enable pg test disabled god knows why, minor cleanup.
[dbsrgits/DBIx-Class.git] / t / storage / replicated.t
CommitLineData
e4dc89b3 1use strict;
2use warnings;
3use lib qw(t/lib);
e4dc89b3 4use Test::More;
c4d3fae2 5use Test::Exception;
857d66ca 6use DBICTest;
9901aad7 7use List::Util 'first';
b2e4d522 8use Scalar::Util 'reftype';
3da4f736 9use File::Spec;
f404f53c 10use IO::Handle;
8f7986d6 11
86583fa7 12BEGIN {
a34b0c89 13 eval { require Test::Moose; Test::Moose->import() };
14 plan skip_all => "Need Test::Moose to run this test" if $@;
15 require DBIx::Class;
16
17 plan skip_all => 'Test needs ' . DBIx::Class::Optional::Dependencies->req_missing_for ('replicated')
18 unless DBIx::Class::Optional::Dependencies->req_ok_for ('replicated');
26ab719a 19}
20
21use_ok 'DBIx::Class::Storage::DBI::Replicated::Pool';
22use_ok 'DBIx::Class::Storage::DBI::Replicated::Balancer';
23use_ok 'DBIx::Class::Storage::DBI::Replicated::Replicant';
24use_ok 'DBIx::Class::Storage::DBI::Replicated';
0f83441a 25
6a151f58 26use Moose();
27use MooseX::Types();
a57958b4 28note "Using Moose version $Moose::VERSION and MooseX::Types version $MooseX::Types::VERSION";
6a151f58 29
89cf6a70 30=head1 HOW TO USE
31
32 This is a test of the replicated storage system. This will work in one of
33 two ways, either it was try to fake replication with a couple of SQLite DBs
34 and creative use of copy, or if you define a couple of %ENV vars correctly
35 will try to test those. If you do that, it will assume the setup is properly
36 replicating. Your results may vary, but I have demonstrated this to work with
37 mysql native replication.
d59cf2f2 38
89cf6a70 39=cut
40
41
0f83441a 42## ----------------------------------------------------------------------------
43## Build a class to hold all our required testing data and methods.
44## ----------------------------------------------------------------------------
45
857d66ca 46TESTSCHEMACLASSES: {
2bf79155 47
857d66ca 48 ## --------------------------------------------------------------------- ##
49 ## Create an object to contain your replicated stuff.
50 ## --------------------------------------------------------------------- ##
d59cf2f2 51
2bf79155 52 package DBIx::Class::DBI::Replicated::TestReplication;
d59cf2f2 53
2bf79155 54 use DBICTest;
55 use base qw/Class::Accessor::Fast/;
d59cf2f2 56
857d66ca 57 __PACKAGE__->mk_accessors( qw/schema/ );
2bf79155 58
59 ## Initialize the object
d59cf2f2 60
61 sub new {
62 my ($class, $schema_method) = (shift, shift);
63 my $self = $class->SUPER::new(@_);
64
65 $self->schema( $self->init_schema($schema_method) );
66 return $self;
67 }
68
26ab719a 69 ## Get the Schema and set the replication storage type
d59cf2f2 70
2bf79155 71 sub init_schema {
f6ace689 72 # current SQLT SQLite producer does not handle DROP TABLE IF EXISTS, trap warnings here
2361982d 73 local $SIG{__WARN__} = sub { warn @_ unless $_[0] =~ /no such table.+DROP TABLE/s };
f6ace689 74
dcdf7b2c 75 my ($class, $schema_method) = @_;
76
77 my $method = "get_schema_$schema_method";
78 my $schema = $class->$method;
cb6ec758 79
2bf79155 80 return $schema;
81 }
dcdf7b2c 82
83 sub get_schema_by_storage_type {
84 DBICTest->init_schema(
85 sqlite_use_file => 1,
86 storage_type=>{
87 '::DBI::Replicated' => {
88 balancer_type=>'::Random',
89 balancer_args=>{
90 auto_validate_every=>100,
d59cf2f2 91 master_read_weight => 1
dcdf7b2c 92 },
93 }
94 },
95 deploy_args=>{
96 add_drop_table => 1,
97 },
98 );
99 }
100
101 sub get_schema_by_connect_info {
102 DBICTest->init_schema(
103 sqlite_use_file => 1,
104 storage_type=> '::DBI::Replicated',
105 balancer_type=>'::Random',
106 balancer_args=> {
107 auto_validate_every=>100,
d59cf2f2 108 master_read_weight => 1
dcdf7b2c 109 },
110 deploy_args=>{
111 add_drop_table => 1,
112 },
113 );
114 }
115
857d66ca 116 sub generate_replicant_connect_info {}
117 sub replicate {}
118 sub cleanup {}
119
b2e4d522 120 ## --------------------------------------------------------------------- ##
121 ## Add a connect_info option to test option merging.
122 ## --------------------------------------------------------------------- ##
123 {
124 package DBIx::Class::Storage::DBI::Replicated;
125
126 use Moose;
127
128 __PACKAGE__->meta->make_mutable;
129
130 around connect_info => sub {
131 my ($next, $self, $info) = @_;
132 $info->[3]{master_option} = 1;
133 $self->$next($info);
134 };
135
136 __PACKAGE__->meta->make_immutable;
137
138 no Moose;
139 }
d59cf2f2 140
857d66ca 141 ## --------------------------------------------------------------------- ##
142 ## Subclass for when you are using SQLite for testing, this provides a fake
143 ## replication support.
144 ## --------------------------------------------------------------------- ##
d59cf2f2 145
857d66ca 146 package DBIx::Class::DBI::Replicated::TestReplication::SQLite;
147
148 use DBICTest;
d59cf2f2 149 use File::Copy;
857d66ca 150 use base 'DBIx::Class::DBI::Replicated::TestReplication';
d59cf2f2 151
3da4f736 152 __PACKAGE__->mk_accessors(qw/master_path slave_paths/);
d59cf2f2 153
3da4f736 154 ## Set the master path from DBICTest
d59cf2f2 155
156 sub new {
157 my $class = shift @_;
158 my $self = $class->SUPER::new(@_);
159
160 $self->master_path( DBICTest->_sqlite_dbfilename );
161 $self->slave_paths([
162 File::Spec->catfile(qw/t var DBIxClass_slave1.db/),
163 File::Spec->catfile(qw/t var DBIxClass_slave2.db/),
164 ]);
165
166 return $self;
167 }
168
26ab719a 169 ## Return an Array of ArrayRefs where each ArrayRef is suitable to use for
170 ## $storage->connect_info to be used for connecting replicants.
d59cf2f2 171
26ab719a 172 sub generate_replicant_connect_info {
857d66ca 173 my $self = shift @_;
26ab719a 174 my @dsn = map {
175 "dbi:SQLite:${_}";
176 } @{$self->slave_paths};
d59cf2f2 177
9901aad7 178 my @connect_infos = map { [$_,'','',{AutoCommit=>1}] } @dsn;
179
d59cf2f2 180 ## Make sure nothing is left over from a failed test
181 $self->cleanup;
3da4f736 182
d59cf2f2 183 ## try a hashref too
9901aad7 184 my $c = $connect_infos[0];
185 $connect_infos[0] = {
186 dsn => $c->[0],
187 user => $c->[1],
188 password => $c->[2],
189 %{ $c->[3] }
190 };
191
192 @connect_infos
26ab719a 193 }
9901aad7 194
26ab719a 195 ## Do a 'good enough' replication by copying the master dbfile over each of
50336325 196 ## the slave dbfiles. If the master is SQLite we do this, otherwise we
197 ## just do a one second pause to let the slaves catch up.
d59cf2f2 198
26ab719a 199 sub replicate {
200 my $self = shift @_;
201 foreach my $slave (@{$self->slave_paths}) {
202 copy($self->master_path, $slave);
203 }
204 }
d59cf2f2 205
26ab719a 206 ## Cleanup after ourselves. Unlink all gthe slave paths.
d59cf2f2 207
26ab719a 208 sub cleanup {
209 my $self = shift @_;
210 foreach my $slave (@{$self->slave_paths}) {
d59cf2f2 211 if(-e $slave) {
212 unlink $slave;
213 }
214 }
26ab719a 215 }
d59cf2f2 216
857d66ca 217 ## --------------------------------------------------------------------- ##
218 ## Subclass for when you are setting the databases via custom export vars
219 ## This is for when you have a replicating database setup that you are
220 ## going to test against. You'll need to define the correct $ENV and have
221 ## two slave databases to test against, as well as a replication system
222 ## that will replicate in less than 1 second.
223 ## --------------------------------------------------------------------- ##
d59cf2f2 224
225 package DBIx::Class::DBI::Replicated::TestReplication::Custom;
857d66ca 226 use base 'DBIx::Class::DBI::Replicated::TestReplication';
d59cf2f2 227
857d66ca 228 ## Return an Array of ArrayRefs where each ArrayRef is suitable to use for
229 ## $storage->connect_info to be used for connecting replicants.
d59cf2f2 230
231 sub generate_replicant_connect_info {
857d66ca 232 return (
233 [$ENV{"DBICTEST_SLAVE0_DSN"}, $ENV{"DBICTEST_SLAVE0_DBUSER"}, $ENV{"DBICTEST_SLAVE0_DBPASS"}, {AutoCommit => 1}],
d59cf2f2 234 [$ENV{"DBICTEST_SLAVE1_DSN"}, $ENV{"DBICTEST_SLAVE1_DBUSER"}, $ENV{"DBICTEST_SLAVE1_DBPASS"}, {AutoCommit => 1}],
857d66ca 235 );
236 }
d59cf2f2 237
238 ## pause a bit to let the replication catch up
239
857d66ca 240 sub replicate {
d59cf2f2 241 sleep 1;
242 }
2bf79155 243}
244
245## ----------------------------------------------------------------------------
246## Create an object and run some tests
247## ----------------------------------------------------------------------------
248
26ab719a 249## Thi first bunch of tests are basic, just make sure all the bits are behaving
2bf79155 250
857d66ca 251my $replicated_class = DBICTest->has_custom_dsn ?
252 'DBIx::Class::DBI::Replicated::TestReplication::Custom' :
253 'DBIx::Class::DBI::Replicated::TestReplication::SQLite';
254
dcdf7b2c 255my $replicated;
256
257for my $method (qw/by_connect_info by_storage_type/) {
a3df35f8 258 undef $replicated;
dcdf7b2c 259 ok $replicated = $replicated_class->new($method)
260 => "Created a replication object $method";
d59cf2f2 261
dcdf7b2c 262 isa_ok $replicated->schema
263 => 'DBIx::Class::Schema';
d59cf2f2 264
dcdf7b2c 265 isa_ok $replicated->schema->storage
266 => 'DBIx::Class::Storage::DBI::Replicated';
267
268 isa_ok $replicated->schema->storage->balancer
269 => 'DBIx::Class::Storage::DBI::Replicated::Balancer::Random'
270 => 'configured balancer_type';
271}
26ab719a 272
f29315ed 273### check that all Storage::DBI methods are handled by ::Replicated
274{
b75235fe 275 my @storage_dbi_methods = Class::MOP::Class
276 ->initialize('DBIx::Class::Storage::DBI')->get_all_method_names;
f29315ed 277
b75235fe 278 my @replicated_methods = DBIx::Class::Storage::DBI::Replicated->meta
279 ->get_all_method_names;
f29315ed 280
b75235fe 281# remove constants and OTHER_CRAP
f29315ed 282 @storage_dbi_methods = grep !/^[A-Z_]+\z/, @storage_dbi_methods;
283
284# remove CAG accessors
285 @storage_dbi_methods = grep !/_accessor\z/, @storage_dbi_methods;
286
287# remove DBIx::Class (the root parent, with CAG and stuff) methods
b75235fe 288 my @root_methods = Class::MOP::Class->initialize('DBIx::Class')
f29315ed 289 ->get_all_method_names;
290 my %count;
b75235fe 291 $count{$_}++ for (@storage_dbi_methods, @root_methods);
f29315ed 292
293 @storage_dbi_methods = grep $count{$_} != 2, @storage_dbi_methods;
294
295# make hashes
296 my %storage_dbi_methods;
297 @storage_dbi_methods{@storage_dbi_methods} = ();
298 my %replicated_methods;
299 @replicated_methods{@replicated_methods} = ();
300
301# remove ::Replicated-specific methods
302 for my $method (@replicated_methods) {
303 delete $replicated_methods{$method}
304 unless exists $storage_dbi_methods{$method};
305 }
e4c5abce 306 @replicated_methods = keys %replicated_methods;
f29315ed 307
308# check that what's left is implemented
309 %count = ();
310 $count{$_}++ for (@storage_dbi_methods, @replicated_methods);
311
312 if ((grep $count{$_} == 2, @storage_dbi_methods) == @storage_dbi_methods) {
313 pass 'all DBIx::Class::Storage::DBI methods implemented';
314 }
315 else {
316 my @unimplemented = grep $count{$_} == 1, @storage_dbi_methods;
317
318 fail 'the following DBIx::Class::Storage::DBI methods are unimplemented: '
319 . "@unimplemented";
320 }
321}
322
26ab719a 323ok $replicated->schema->storage->meta
324 => 'has a meta object';
d59cf2f2 325
26ab719a 326isa_ok $replicated->schema->storage->master
327 => 'DBIx::Class::Storage::DBI';
d59cf2f2 328
26ab719a 329isa_ok $replicated->schema->storage->pool
330 => 'DBIx::Class::Storage::DBI::Replicated::Pool';
d59cf2f2 331
17b05c13 332does_ok $replicated->schema->storage->balancer
d59cf2f2 333 => 'DBIx::Class::Storage::DBI::Replicated::Balancer';
26ab719a 334
335ok my @replicant_connects = $replicated->generate_replicant_connect_info
336 => 'got replication connect information';
337
955a6df6 338ok my @replicated_storages = $replicated->schema->storage->connect_replicants(@replicant_connects)
26ab719a 339 => 'Created some storages suitable for replicants';
6412a592 340
071bbccb 341our %debug;
342$replicated->schema->storage->debug(1);
343$replicated->schema->storage->debugcb(sub {
d59cf2f2 344 my ($op, $info) = @_;
345 ##warn "\n$op, $info\n";
346 %debug = (
347 op => $op,
348 info => $info,
349 dsn => ($info=~m/\[(.+)\]/)[0],
350 storage_type => $info=~m/REPLICANT/ ? 'REPLICANT' : 'MASTER',
351 );
071bbccb 352});
353
6412a592 354ok my @all_storages = $replicated->schema->storage->all_storages
355 => '->all_storages';
356
dcdf7b2c 357is scalar @all_storages,
358 3
6412a592 359 => 'correct number of ->all_storages';
360
dcdf7b2c 361is ((grep $_->isa('DBIx::Class::Storage::DBI'), @all_storages),
362 3
6412a592 363 => '->all_storages are correct type');
b2e4d522 364
dcdf7b2c 365my @all_storage_opts =
366 grep { (reftype($_)||'') eq 'HASH' }
367 map @{ $_->_connect_info }, @all_storages;
368
369is ((grep $_->{master_option}, @all_storage_opts),
370 3
b2e4d522 371 => 'connect_info was merged from master to replicants');
d59cf2f2 372
9901aad7 373my @replicant_names = keys %{ $replicated->schema->storage->replicants };
374
bd5da369 375ok @replicant_names, "found replicant names @replicant_names";
376
9901aad7 377## Silence warning about not supporting the is_replicating method if using the
378## sqlite dbs.
379$replicated->schema->storage->debugobj->silence(1)
380 if first { m{^t/} } @replicant_names;
d59cf2f2 381
cb6ec758 382isa_ok $replicated->schema->storage->balancer->current_replicant
d59cf2f2 383 => 'DBIx::Class::Storage::DBI';
9901aad7 384
385$replicated->schema->storage->debugobj->silence(0);
386
26ab719a 387ok $replicated->schema->storage->pool->has_replicants
d59cf2f2 388 => 'does have replicants';
26ab719a 389
17b05c13 390is $replicated->schema->storage->pool->num_replicants => 2
26ab719a 391 => 'has two replicants';
d59cf2f2 392
de5dc9ef 393does_ok $replicated_storages[0]
26ab719a 394 => 'DBIx::Class::Storage::DBI::Replicated::Replicant';
395
de5dc9ef 396does_ok $replicated_storages[1]
26ab719a 397 => 'DBIx::Class::Storage::DBI::Replicated::Replicant';
d59cf2f2 398
de5dc9ef 399does_ok $replicated->schema->storage->replicants->{$replicant_names[0]}
26ab719a 400 => 'DBIx::Class::Storage::DBI::Replicated::Replicant';
401
de5dc9ef 402does_ok $replicated->schema->storage->replicants->{$replicant_names[1]}
d59cf2f2 403 => 'DBIx::Class::Storage::DBI::Replicated::Replicant';
26ab719a 404
405## Add some info to the database
406
407$replicated
408 ->schema
409 ->populate('Artist', [
410 [ qw/artistid name/ ],
411 [ 4, "Ozric Tentacles"],
412 ]);
071bbccb 413
d59cf2f2 414 is $debug{storage_type}, 'MASTER',
415 "got last query from a master: $debug{dsn}";
416
417 like $debug{info}, qr/INSERT/, 'Last was an insert';
418
26ab719a 419## Make sure all the slaves have the table definitions
420
421$replicated->replicate;
5c1d82d2 422$replicated->schema->storage->replicants->{$replicant_names[0]}->active(1);
423$replicated->schema->storage->replicants->{$replicant_names[1]}->active(1);
9901aad7 424
425## Silence warning about not supporting the is_replicating method if using the
426## sqlite dbs.
427$replicated->schema->storage->debugobj->silence(1)
428 if first { m{^t/} } @replicant_names;
d59cf2f2 429
f15afa13 430$replicated->schema->storage->pool->validate_replicants;
26ab719a 431
9901aad7 432$replicated->schema->storage->debugobj->silence(0);
433
26ab719a 434## Make sure we can read the data.
435
436ok my $artist1 = $replicated->schema->resultset('Artist')->find(4)
437 => 'Created Result';
438
071bbccb 439## We removed testing here since master read weight is on, so we can't tell in
440## advance what storage to expect. We turn master read weight off a bit lower
d59cf2f2 441## is $debug{storage_type}, 'REPLICANT'
442## => "got last query from a replicant: $debug{dsn}, $debug{info}";
071bbccb 443
26ab719a 444isa_ok $artist1
445 => 'DBICTest::Artist';
d59cf2f2 446
26ab719a 447is $artist1->name, 'Ozric Tentacles'
448 => 'Found expected name for first result';
449
ee356d00 450## Check that master_read_weight is honored
451{
87974600 452 no warnings qw/once redefine/;
ee356d00 453
454 local
f404f53c 455 *DBIx::Class::Storage::DBI::Replicated::Balancer::Random::_random_number =
d59cf2f2 456 sub { 999 };
ee356d00 457
458 $replicated->schema->storage->balancer->increment_storage;
459
460 is $replicated->schema->storage->balancer->current_replicant,
461 $replicated->schema->storage->master
462 => 'master_read_weight is honored';
463
464 ## turn it off for the duration of the test
465 $replicated->schema->storage->balancer->master_read_weight(0);
466 $replicated->schema->storage->balancer->increment_storage;
467}
468
26ab719a 469## Add some new rows that only the master will have This is because
470## we overload any type of write operation so that is must hit the master
471## database.
472
473$replicated
474 ->schema
475 ->populate('Artist', [
476 [ qw/artistid name/ ],
477 [ 5, "Doom's Children"],
478 [ 6, "Dead On Arrival"],
479 [ 7, "Watergate"],
480 ]);
481
d59cf2f2 482 is $debug{storage_type}, 'MASTER',
483 "got last query from a master: $debug{dsn}";
484
485 like $debug{info}, qr/INSERT/, 'Last was an insert';
071bbccb 486
26ab719a 487## Make sure all the slaves have the table definitions
488$replicated->replicate;
489
490## Should find some data now
491
492ok my $artist2 = $replicated->schema->resultset('Artist')->find(5)
493 => 'Sync succeed';
071bbccb 494
d59cf2f2 495is $debug{storage_type}, 'REPLICANT'
496 => "got last query from a replicant: $debug{dsn}";
497
26ab719a 498isa_ok $artist2
499 => 'DBICTest::Artist';
d59cf2f2 500
26ab719a 501is $artist2->name, "Doom's Children"
502 => 'Found expected name for first result';
503
504## What happens when we disconnect all the replicants?
505
50336325 506is $replicated->schema->storage->pool->connected_replicants => 2
507 => "both replicants are connected";
d59cf2f2 508
89cf6a70 509$replicated->schema->storage->replicants->{$replicant_names[0]}->disconnect;
510$replicated->schema->storage->replicants->{$replicant_names[1]}->disconnect;
26ab719a 511
50336325 512is $replicated->schema->storage->pool->connected_replicants => 0
513 => "both replicants are now disconnected";
514
515## All these should pass, since the database should automatically reconnect
516
26ab719a 517ok my $artist3 = $replicated->schema->resultset('Artist')->find(6)
518 => 'Still finding stuff.';
071bbccb 519
d59cf2f2 520is $debug{storage_type}, 'REPLICANT'
521 => "got last query from a replicant: $debug{dsn}";
522
26ab719a 523isa_ok $artist3
524 => 'DBICTest::Artist';
d59cf2f2 525
26ab719a 526is $artist3->name, "Dead On Arrival"
527 => 'Found expected name for first result';
2bf79155 528
50336325 529is $replicated->schema->storage->pool->connected_replicants => 1
13b9e828 530 => "At Least One replicant reconnected to handle the job";
d59cf2f2 531
6f6fb437 532## What happens when we try to select something that doesn't exist?
533
534ok ! $replicated->schema->resultset('Artist')->find(666)
535 => 'Correctly failed to find something.';
071bbccb 536
d59cf2f2 537is $debug{storage_type}, 'REPLICANT'
538 => "got last query from a replicant: $debug{dsn}";
539
cb6ec758 540## test the reliable option
541
542TESTRELIABLE: {
d59cf2f2 543
544 $replicated->schema->storage->set_reliable_storage;
545
546 ok $replicated->schema->resultset('Artist')->find(2)
547 => 'Read from master 1';
548
549 is $debug{storage_type}, 'MASTER',
550 "got last query from a master: $debug{dsn}";
551
552 ok $replicated->schema->resultset('Artist')->find(5)
553 => 'Read from master 2';
554
555 is $debug{storage_type}, 'MASTER',
556 "got last query from a master: $debug{dsn}";
557
558 $replicated->schema->storage->set_balanced_storage;
559
560 ok $replicated->schema->resultset('Artist')->find(3)
9c748388 561 => 'Read from replicant';
071bbccb 562
d59cf2f2 563 is $debug{storage_type}, 'REPLICANT',
564 "got last query from a replicant: $debug{dsn}";
cb6ec758 565}
566
9c748388 567## Make sure when reliable goes out of scope, we are using replicants again
cb6ec758 568
569ok $replicated->schema->resultset('Artist')->find(1)
570 => 'back to replicant 1.';
071bbccb 571
d59cf2f2 572 is $debug{storage_type}, 'REPLICANT',
573 "got last query from a replicant: $debug{dsn}";
574
cb6ec758 575ok $replicated->schema->resultset('Artist')->find(2)
576 => 'back to replicant 2.';
2156bbdd 577
d59cf2f2 578 is $debug{storage_type}, 'REPLICANT',
579 "got last query from a replicant: $debug{dsn}";
071bbccb 580
106d5f3b 581## set all the replicants to inactive, and make sure the balancer falls back to
582## the master.
583
89cf6a70 584$replicated->schema->storage->replicants->{$replicant_names[0]}->active(0);
585$replicated->schema->storage->replicants->{$replicant_names[1]}->active(0);
9901aad7 586
f404f53c 587{
588 ## catch the fallback to master warning
589 open my $debugfh, '>', \my $fallback_warning;
590 my $oldfh = $replicated->schema->storage->debugfh;
591 $replicated->schema->storage->debugfh($debugfh);
de5dc9ef 592
f404f53c 593 ok $replicated->schema->resultset('Artist')->find(2)
d59cf2f2 594 => 'Fallback to master';
071bbccb 595
d59cf2f2 596 is $debug{storage_type}, 'MASTER',
597 "got last query from a master: $debug{dsn}";
f404f53c 598
599 like $fallback_warning, qr/falling back to master/
d59cf2f2 600 => 'emits falling back to master warning';
f404f53c 601
602 $replicated->schema->storage->debugfh($oldfh);
603}
9901aad7 604
de5dc9ef 605$replicated->schema->storage->replicants->{$replicant_names[0]}->active(1);
606$replicated->schema->storage->replicants->{$replicant_names[1]}->active(1);
9901aad7 607
608## Silence warning about not supporting the is_replicating method if using the
609## sqlite dbs.
610$replicated->schema->storage->debugobj->silence(1)
611 if first { m{^t/} } @replicant_names;
d59cf2f2 612
f15afa13 613$replicated->schema->storage->pool->validate_replicants;
de5dc9ef 614
9901aad7 615$replicated->schema->storage->debugobj->silence(0);
616
de5dc9ef 617ok $replicated->schema->resultset('Artist')->find(2)
618 => 'Returned to replicates';
071bbccb 619
d59cf2f2 620is $debug{storage_type}, 'REPLICANT',
621 "got last query from a replicant: $debug{dsn}";
622
de5dc9ef 623## Getting slave status tests
624
f797e89e 625SKIP: {
626 ## We skip this tests unless you have a custom replicants, since the default
627 ## sqlite based replication tests don't support these functions.
d59cf2f2 628
629 skip 'Cannot Test Replicant Status on Non Replicating Database', 10
f797e89e 630 unless DBICTest->has_custom_dsn && $ENV{"DBICTEST_SLAVE0_DSN"};
631
632 $replicated->replicate; ## Give the slaves a chance to catchup.
633
d59cf2f2 634 ok $replicated->schema->storage->replicants->{$replicant_names[0]}->is_replicating
635 => 'Replicants are replicating';
636
637 is $replicated->schema->storage->replicants->{$replicant_names[0]}->lag_behind_master, 0
638 => 'Replicant is zero seconds behind master';
639
640 ## Test the validate replicants
641
642 $replicated->schema->storage->pool->validate_replicants;
643
644 is $replicated->schema->storage->pool->active_replicants, 2
645 => 'Still have 2 replicants after validation';
646
647 ## Force the replicants to fail the validate test by required their lag to
648 ## be negative (ie ahead of the master!)
649
7edf5f1c 650 $replicated->schema->storage->pool->maximum_lag(-10);
651 $replicated->schema->storage->pool->validate_replicants;
d59cf2f2 652
7edf5f1c 653 is $replicated->schema->storage->pool->active_replicants, 0
654 => 'No way a replicant be be ahead of the master';
d59cf2f2 655
7edf5f1c 656 ## Let's be fair to the replicants again. Let them lag up to 5
d59cf2f2 657
7edf5f1c 658 $replicated->schema->storage->pool->maximum_lag(5);
659 $replicated->schema->storage->pool->validate_replicants;
d59cf2f2 660
7edf5f1c 661 is $replicated->schema->storage->pool->active_replicants, 2
d59cf2f2 662 => 'Both replicants in good standing again';
663
664 ## Check auto validate
665
666 is $replicated->schema->storage->balancer->auto_validate_every, 100
667 => "Got the expected value for auto validate";
668
669 ## This will make sure we auto validatge everytime
670 $replicated->schema->storage->balancer->auto_validate_every(0);
671
672 ## set all the replicants to inactive, and make sure the balancer falls back to
673 ## the master.
674
675 $replicated->schema->storage->replicants->{$replicant_names[0]}->active(0);
676 $replicated->schema->storage->replicants->{$replicant_names[1]}->active(0);
677
678 ## Ok, now when we go to run a query, autovalidate SHOULD reconnect
679
680 is $replicated->schema->storage->pool->active_replicants => 0
681 => "both replicants turned off";
682
683 ok $replicated->schema->resultset('Artist')->find(5)
684 => 'replicant reactivated';
685
686 is $debug{storage_type}, 'REPLICANT',
687 "got last query from a replicant: $debug{dsn}";
688
689 is $replicated->schema->storage->pool->active_replicants => 2
690 => "both replicants reactivated";
f797e89e 691}
692
c4d3fae2 693## Test the reliably callback
694
695ok my $reliably = sub {
d59cf2f2 696
c4d3fae2 697 ok $replicated->schema->resultset('Artist')->find(5)
071bbccb 698 => 'replicant reactivated';
699
d59cf2f2 700 is $debug{storage_type}, 'MASTER',
701 "got last query from a master: $debug{dsn}";
702
c4d3fae2 703} => 'created coderef properly';
704
705$replicated->schema->storage->execute_reliably($reliably);
706
707## Try something with an error
708
709ok my $unreliably = sub {
d59cf2f2 710
c4d3fae2 711 ok $replicated->schema->resultset('ArtistXX')->find(5)
d59cf2f2 712 => 'replicant reactivated';
713
c4d3fae2 714} => 'created coderef properly';
715
d59cf2f2 716throws_ok {$replicated->schema->storage->execute_reliably($unreliably)}
ed213e85 717 qr/Can't find source for ArtistXX/
c4d3fae2 718 => 'Bad coderef throws proper error';
d59cf2f2 719
ed213e85 720## Make sure replication came back
721
722ok $replicated->schema->resultset('Artist')->find(3)
723 => 'replicant reactivated';
071bbccb 724
725is $debug{storage_type}, 'REPLICANT', "got last query from a replicant: $debug{dsn}";
d59cf2f2 726
c4d3fae2 727## make sure transactions are set to execute_reliably
728
729ok my $transaction = sub {
d59cf2f2 730
731 my $id = shift @_;
732
733 $replicated
734 ->schema
735 ->populate('Artist', [
736 [ qw/artistid name/ ],
737 [ $id, "Children of the Grave"],
738 ]);
739
64cdad22 740 ok my $result = $replicated->schema->resultset('Artist')->find($id)
bd5da369 741 => "Found expected artist for $id";
071bbccb 742
d59cf2f2 743 is $debug{storage_type}, 'MASTER',
744 "got last query from a master: $debug{dsn}";
745
64cdad22 746 ok my $more = $replicated->schema->resultset('Artist')->find(1)
bd5da369 747 => 'Found expected artist again for 1';
071bbccb 748
d59cf2f2 749 is $debug{storage_type}, 'MASTER',
750 "got last query from a master: $debug{dsn}";
751
ed213e85 752 return ($result, $more);
d59cf2f2 753
64cdad22 754} => 'Created a coderef properly';
c4d3fae2 755
ed213e85 756## Test the transaction with multi return
757{
d59cf2f2 758 ok my @return = $replicated->schema->txn_do($transaction, 666)
759 => 'did transaction';
760
761 is $return[0]->id, 666
762 => 'first returned value is correct';
071bbccb 763
d59cf2f2 764 is $debug{storage_type}, 'MASTER',
765 "got last query from a master: $debug{dsn}";
071bbccb 766
d59cf2f2 767 is $return[1]->id, 1
768 => 'second returned value is correct';
769
770 is $debug{storage_type}, 'MASTER',
771 "got last query from a master: $debug{dsn}";
071bbccb 772
ed213e85 773}
774
775## Test that asking for single return works
776{
d59cf2f2 777 ok my @return = $replicated->schema->txn_do($transaction, 777)
778 => 'did transaction';
779
780 is $return[0]->id, 777
781 => 'first returned value is correct';
782
783 is $return[1]->id, 1
784 => 'second returned value is correct';
ed213e85 785}
786
787## Test transaction returning a single value
788
789{
d59cf2f2 790 ok my $result = $replicated->schema->txn_do(sub {
791 ok my $more = $replicated->schema->resultset('Artist')->find(1)
792 => 'found inside a transaction';
793 is $debug{storage_type}, 'MASTER', "got last query from a master: $debug{dsn}";
794 return $more;
795 }) => 'successfully processed transaction';
796
797 is $result->id, 1
798 => 'Got expected single result from transaction';
ed213e85 799}
c4d3fae2 800
801## Make sure replication came back
802
ed213e85 803ok $replicated->schema->resultset('Artist')->find(1)
c4d3fae2 804 => 'replicant reactivated';
071bbccb 805
806is $debug{storage_type}, 'REPLICANT', "got last query from a replicant: $debug{dsn}";
d59cf2f2 807
ed213e85 808## Test Discard changes
809
810{
d59cf2f2 811 ok my $artist = $replicated->schema->resultset('Artist')->find(2)
812 => 'got an artist to test discard changes';
071bbccb 813
d59cf2f2 814 is $debug{storage_type}, 'REPLICANT', "got last query from a replicant: $debug{dsn}";
071bbccb 815
d59cf2f2 816 ok $artist->get_from_storage({force_pool=>'master'})
817 => 'properly discard changes';
071bbccb 818
d59cf2f2 819 is $debug{storage_type}, 'MASTER', "got last query from a master: $debug{dsn}";
071bbccb 820
3a443bb0 821 ok $artist->discard_changes({force_pool=>'master'})
822 => 'properly called discard_changes against master (manual attrs)';
9c169362 823
3a443bb0 824 is $debug{storage_type}, 'MASTER', "got last query from a master: $debug{dsn}";
9c169362 825
3a443bb0 826 ok $artist->discard_changes()
827 => 'properly called discard_changes against master (default attrs)';
9c169362 828
3a443bb0 829 is $debug{storage_type}, 'MASTER', "got last query from a master: $debug{dsn}";
8287e9f8 830
3a443bb0 831 ok $artist->discard_changes({force_pool=>$replicant_names[0]})
832 => 'properly able to override the default attributes';
8287e9f8 833
3a443bb0 834 is $debug{storage_type}, 'REPLICANT', "got last query from a replicant: $debug{dsn}"
ed213e85 835}
64cdad22 836
837## Test some edge cases, like trying to do a transaction inside a transaction, etc
838
839{
840 ok my $result = $replicated->schema->txn_do(sub {
d59cf2f2 841 return $replicated->schema->txn_do(sub {
842 ok my $more = $replicated->schema->resultset('Artist')->find(1)
843 => 'found inside a transaction inside a transaction';
844 is $debug{storage_type}, 'MASTER', "got last query from a master: $debug{dsn}";
845 return $more;
846 });
64cdad22 847 }) => 'successfully processed transaction';
d59cf2f2 848
64cdad22 849 is $result->id, 1
d59cf2f2 850 => 'Got expected single result from transaction';
64cdad22 851}
852
853{
854 ok my $result = $replicated->schema->txn_do(sub {
d59cf2f2 855 return $replicated->schema->storage->execute_reliably(sub {
856 return $replicated->schema->txn_do(sub {
857 return $replicated->schema->storage->execute_reliably(sub {
858 ok my $more = $replicated->schema->resultset('Artist')->find(1)
859 => 'found inside crazy deep transactions and execute_reliably';
860 is $debug{storage_type}, 'MASTER', "got last query from a master: $debug{dsn}";
861 return $more;
862 });
863 });
864 });
64cdad22 865 }) => 'successfully processed transaction';
d59cf2f2 866
64cdad22 867 is $result->id, 1
d59cf2f2 868 => 'Got expected single result from transaction';
869}
2ce6e9a6 870
7e38d850 871## Test the force_pool resultset attribute.
bbafcf26 872
873{
d59cf2f2 874 ok my $artist_rs = $replicated->schema->resultset('Artist')
bbafcf26 875 => 'got artist resultset';
d59cf2f2 876
877 ## Turn on Forced Pool Storage
878 ok my $reliable_artist_rs = $artist_rs->search(undef, {force_pool=>'master'})
7e38d850 879 => 'Created a resultset using force_pool storage';
d59cf2f2 880
881 ok my $artist = $reliable_artist_rs->find(2)
7e38d850 882 => 'got an artist result via force_pool storage';
071bbccb 883
d59cf2f2 884 is $debug{storage_type}, 'MASTER', "got last query from a master: $debug{dsn}";
bbafcf26 885}
886
bd5da369 887## Test the force_pool resultset attribute part two.
888
889{
d59cf2f2 890 ok my $artist_rs = $replicated->schema->resultset('Artist')
bd5da369 891 => 'got artist resultset';
d59cf2f2 892
893 ## Turn on Forced Pool Storage
894 ok my $reliable_artist_rs = $artist_rs->search(undef, {force_pool=>$replicant_names[0]})
bd5da369 895 => 'Created a resultset using force_pool storage';
d59cf2f2 896
897 ok my $artist = $reliable_artist_rs->find(2)
bd5da369 898 => 'got an artist result via force_pool storage';
071bbccb 899
d59cf2f2 900 is $debug{storage_type}, 'REPLICANT', "got last query from a replicant: $debug{dsn}";
bd5da369 901}
0f83441a 902## Delete the old database files
50336325 903$replicated->cleanup;
ee356d00 904
6988233d 905done_testing;
906
ee356d00 907# vim: sw=4 sts=4 :