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