some documention updates and changed the way we find paths for the sqlite dbfiles...
[dbsrgits/DBIx-Class.git] / t / 93storage_replication.t
1 use strict;
2 use warnings;
3 use lib qw(t/lib);
4 use Test::More;
5 use Test::Exception;
6 use DBICTest;
7 use List::Util 'first';
8 use Scalar::Util 'reftype';
9 use File::Spec;
10 use IO::Handle;
11
12 BEGIN {
13     eval "use DBIx::Class::Storage::DBI::Replicated; use Test::Moose";
14     plan $@
15         ? ( skip_all => "Deps not installed: $@" )
16         : ( tests => 126 );
17 }
18
19 use_ok 'DBIx::Class::Storage::DBI::Replicated::Pool';
20 use_ok 'DBIx::Class::Storage::DBI::Replicated::Balancer';
21 use_ok 'DBIx::Class::Storage::DBI::Replicated::Replicant';
22 use_ok 'DBIx::Class::Storage::DBI::Replicated';
23
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
36 ## ----------------------------------------------------------------------------
37 ## Build a class to hold all our required testing data and methods.
38 ## ----------------------------------------------------------------------------
39
40 TESTSCHEMACLASSES: {
41
42     ## --------------------------------------------------------------------- ##
43     ## Create an object to contain your replicated stuff.
44     ## --------------------------------------------------------------------- ##
45     
46     package DBIx::Class::DBI::Replicated::TestReplication;
47    
48     use DBICTest;
49     use base qw/Class::Accessor::Fast/;
50     
51     __PACKAGE__->mk_accessors( qw/schema/ );
52
53     ## Initialize the object
54     
55         sub new {
56             my ($class, $schema_method) = (shift, shift);
57             my $self = $class->SUPER::new(@_);
58         
59             $self->schema( $self->init_schema($schema_method) );
60             return $self;
61         }
62     
63     ## Get the Schema and set the replication storage type
64     
65     sub init_schema {
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
69         my ($class, $schema_method) = @_;
70
71         my $method = "get_schema_$schema_method";
72         my $schema = $class->$method;
73
74         return $schema;
75     }
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,
85               master_read_weight => 1
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,
102           master_read_weight => 1
103         },
104         deploy_args=>{
105           add_drop_table => 1,
106         },
107       );
108     }
109
110     sub generate_replicant_connect_info {}
111     sub replicate {}
112     sub cleanup {}
113
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     }
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     
146     __PACKAGE__->mk_accessors(qw/master_path slave_paths/);
147     
148     ## Set the master path from DBICTest
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([
156                         File::Spec->catfile(qw/t var DBIxClass_slave1.db/),
157                         File::Spec->catfile(qw/t var DBIxClass_slave2.db/),
158                 ]);
159         
160             return $self;
161         }    
162         
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 {
167         my $self = shift @_;
168         my @dsn = map {
169             "dbi:SQLite:${_}";
170         } @{$self->slave_paths};
171         
172         my @connect_infos = map { [$_,'','',{AutoCommit=>1}] } @dsn;
173
174                 ## Make sure nothing is left over from a failed test
175                 $self->cleanup;
176
177                 ## try a hashref too
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
187     }
188
189     ## Do a 'good enough' replication by copying the master dbfile over each of
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.
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}) {
205                         if(-e $slave) {
206                                 unlink $slave;
207                         }
208         }     
209     }
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     } 
237 }
238
239 ## ----------------------------------------------------------------------------
240 ## Create an object and run some tests
241 ## ----------------------------------------------------------------------------
242
243 ## Thi first bunch of tests are basic, just make sure all the bits are behaving
244
245 my $replicated_class = DBICTest->has_custom_dsn ?
246     'DBIx::Class::DBI::Replicated::TestReplication::Custom' :
247     'DBIx::Class::DBI::Replicated::TestReplication::SQLite';
248
249 my $replicated;
250
251 for 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 }
265
266 ok $replicated->schema->storage->meta
267     => 'has a meta object';
268     
269 isa_ok $replicated->schema->storage->master
270     => 'DBIx::Class::Storage::DBI';
271     
272 isa_ok $replicated->schema->storage->pool
273     => 'DBIx::Class::Storage::DBI::Replicated::Pool';
274     
275 does_ok $replicated->schema->storage->balancer
276     => 'DBIx::Class::Storage::DBI::Replicated::Balancer'; 
277
278 ok my @replicant_connects = $replicated->generate_replicant_connect_info
279     => 'got replication connect information';
280
281 ok my @replicated_storages = $replicated->schema->storage->connect_replicants(@replicant_connects)
282     => 'Created some storages suitable for replicants';
283
284 our %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
297 ok my @all_storages = $replicated->schema->storage->all_storages
298     => '->all_storages';
299
300 is scalar @all_storages,
301     3
302     => 'correct number of ->all_storages';
303
304 is ((grep $_->isa('DBIx::Class::Storage::DBI'), @all_storages),
305     3
306     => '->all_storages are correct type');
307
308 my @all_storage_opts =
309   grep { (reftype($_)||'') eq 'HASH' }
310     map @{ $_->_connect_info }, @all_storages;
311
312 is ((grep $_->{master_option}, @all_storage_opts),
313     3
314     => 'connect_info was merged from master to replicants');
315  
316 my @replicant_names = keys %{ $replicated->schema->storage->replicants };
317
318 ok @replicant_names, "found replicant names @replicant_names";
319
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    
325 isa_ok $replicated->schema->storage->balancer->current_replicant
326     => 'DBIx::Class::Storage::DBI'; 
327
328 $replicated->schema->storage->debugobj->silence(0);
329
330 ok $replicated->schema->storage->pool->has_replicants
331     => 'does have replicants';     
332
333 is $replicated->schema->storage->pool->num_replicants => 2
334     => 'has two replicants';
335        
336 does_ok $replicated_storages[0]
337     => 'DBIx::Class::Storage::DBI::Replicated::Replicant';
338
339 does_ok $replicated_storages[1]
340     => 'DBIx::Class::Storage::DBI::Replicated::Replicant';
341     
342 does_ok $replicated->schema->storage->replicants->{$replicant_names[0]}
343     => 'DBIx::Class::Storage::DBI::Replicated::Replicant';
344
345 does_ok $replicated->schema->storage->replicants->{$replicant_names[1]}
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     ]);
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';
361                 
362 ## Make sure all the slaves have the table definitions
363
364 $replicated->replicate;
365 $replicated->schema->storage->replicants->{$replicant_names[0]}->active(1);
366 $replicated->schema->storage->replicants->{$replicant_names[1]}->active(1);
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  
373 $replicated->schema->storage->pool->validate_replicants;
374
375 $replicated->schema->storage->debugobj->silence(0);
376
377 ## Make sure we can read the data.
378
379 ok my $artist1 = $replicated->schema->resultset('Artist')->find(4)
380     => 'Created Result';
381
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
387 isa_ok $artist1
388     => 'DBICTest::Artist';
389     
390 is $artist1->name, 'Ozric Tentacles'
391     => 'Found expected name for first result';
392
393 ## Check that master_read_weight is honored
394 {
395     no warnings qw/once redefine/;
396
397     local
398     *DBIx::Class::Storage::DBI::Replicated::Balancer::Random::_random_number =
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
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
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
430 ## Make sure all the slaves have the table definitions
431 $replicated->replicate;
432
433 ## Should find some data now
434
435 ok my $artist2 = $replicated->schema->resultset('Artist')->find(5)
436     => 'Sync succeed';
437
438 is $debug{storage_type}, 'REPLICANT' 
439         => "got last query from a replicant: $debug{dsn}";
440             
441 isa_ok $artist2
442     => 'DBICTest::Artist';
443     
444 is $artist2->name, "Doom's Children"
445     => 'Found expected name for first result';
446
447 ## What happens when we disconnect all the replicants?
448
449 is $replicated->schema->storage->pool->connected_replicants => 2
450     => "both replicants are connected";
451     
452 $replicated->schema->storage->replicants->{$replicant_names[0]}->disconnect;
453 $replicated->schema->storage->replicants->{$replicant_names[1]}->disconnect;
454
455 is $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
460 ok my $artist3 = $replicated->schema->resultset('Artist')->find(6)
461     => 'Still finding stuff.';
462
463 is $debug{storage_type}, 'REPLICANT' 
464         => "got last query from a replicant: $debug{dsn}";
465             
466 isa_ok $artist3
467     => 'DBICTest::Artist';
468     
469 is $artist3->name, "Dead On Arrival"
470     => 'Found expected name for first result';
471
472 is $replicated->schema->storage->pool->connected_replicants => 1
473     => "At Least One replicant reconnected to handle the job";
474     
475 ## What happens when we try to select something that doesn't exist?
476
477 ok ! $replicated->schema->resultset('Artist')->find(666)
478     => 'Correctly failed to find something.';
479
480 is $debug{storage_type}, 'REPLICANT' 
481         => "got last query from a replicant: $debug{dsn}";
482                     
483 ## test the reliable option
484
485 TESTRELIABLE: {
486         
487         $replicated->schema->storage->set_reliable_storage;
488         
489         ok $replicated->schema->resultset('Artist')->find(2)
490             => 'Read from master 1';
491
492         is $debug{storage_type}, 'MASTER', 
493                 "got last query from a master: $debug{dsn}";
494                         
495         ok $replicated->schema->resultset('Artist')->find(5)
496             => 'Read from master 2';
497
498         is $debug{storage_type}, 'MASTER', 
499                 "got last query from a master: $debug{dsn}";
500                             
501     $replicated->schema->storage->set_balanced_storage;     
502             
503         ok $replicated->schema->resultset('Artist')->find(3)
504         => 'Read from replicant';
505
506         is $debug{storage_type}, 'REPLICANT', 
507                 "got last query from a replicant: $debug{dsn}";
508 }
509
510 ## Make sure when reliable goes out of scope, we are using replicants again
511
512 ok $replicated->schema->resultset('Artist')->find(1)
513     => 'back to replicant 1.';
514
515         is $debug{storage_type}, 'REPLICANT', 
516                 "got last query from a replicant: $debug{dsn}";
517                     
518 ok $replicated->schema->resultset('Artist')->find(2)
519     => 'back to replicant 2.';
520
521         is $debug{storage_type}, 'REPLICANT', 
522                 "got last query from a replicant: $debug{dsn}";
523
524 ## set all the replicants to inactive, and make sure the balancer falls back to
525 ## the master.
526
527 $replicated->schema->storage->replicants->{$replicant_names[0]}->active(0);
528 $replicated->schema->storage->replicants->{$replicant_names[1]}->active(0);
529
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);
535
536     ok $replicated->schema->resultset('Artist')->find(2)
537                 => 'Fallback to master';
538
539         is $debug{storage_type}, 'MASTER', 
540                 "got last query from a master: $debug{dsn}";
541
542     like $fallback_warning, qr/falling back to master/
543                 => 'emits falling back to master warning';
544
545     $replicated->schema->storage->debugfh($oldfh);
546 }
547
548 $replicated->schema->storage->replicants->{$replicant_names[0]}->active(1);
549 $replicated->schema->storage->replicants->{$replicant_names[1]}->active(1);
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  
556 $replicated->schema->storage->pool->validate_replicants;
557
558 $replicated->schema->storage->debugobj->silence(0);
559
560 ok $replicated->schema->resultset('Artist')->find(2)
561     => 'Returned to replicates';
562
563 is $debug{storage_type}, 'REPLICANT', 
564         "got last query from a replicant: $debug{dsn}";
565     
566 ## Getting slave status tests
567
568 SKIP: {
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     
572     skip 'Cannot Test Replicant Status on Non Replicating Database', 10 
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';
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';    
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';
628
629         is $debug{storage_type}, 'REPLICANT',
630                 "got last query from a replicant: $debug{dsn}";
631             
632         is $replicated->schema->storage->pool->active_replicants => 2
633             => "both replicants reactivated";        
634 }
635
636 ## Test the reliably callback
637
638 ok my $reliably = sub {
639         
640     ok $replicated->schema->resultset('Artist')->find(5)
641         => 'replicant reactivated';
642
643         is $debug{storage_type}, 'MASTER',
644                 "got last query from a master: $debug{dsn}";
645         
646 } => 'created coderef properly';
647
648 $replicated->schema->storage->execute_reliably($reliably);
649
650 ## Try something with an error
651
652 ok my $unreliably = sub {
653     
654     ok $replicated->schema->resultset('ArtistXX')->find(5)
655         => 'replicant reactivated'; 
656     
657 } => 'created coderef properly';
658
659 throws_ok {$replicated->schema->storage->execute_reliably($unreliably)} 
660     qr/Can't find source for ArtistXX/
661     => 'Bad coderef throws proper error';
662     
663 ## Make sure replication came back
664
665 ok $replicated->schema->resultset('Artist')->find(3)
666     => 'replicant reactivated';
667
668 is $debug{storage_type}, 'REPLICANT', "got last query from a replicant: $debug{dsn}";
669     
670 ## make sure transactions are set to execute_reliably
671
672 ok my $transaction = sub {
673         
674         my $id = shift @_;
675         
676         $replicated
677             ->schema
678             ->populate('Artist', [
679                 [ qw/artistid name/ ],
680                 [ $id, "Children of the Grave"],
681             ]);
682             
683     ok my $result = $replicated->schema->resultset('Artist')->find($id)
684         => "Found expected artist for $id";
685
686         is $debug{storage_type}, 'MASTER',
687                 "got last query from a master: $debug{dsn}";
688                                 
689     ok my $more = $replicated->schema->resultset('Artist')->find(1)
690         => 'Found expected artist again for 1';
691
692         is $debug{storage_type}, 'MASTER',
693                 "got last query from a master: $debug{dsn}";
694                                 
695    return ($result, $more);
696    
697 } => 'Created a coderef properly';
698
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';
706
707                 is $debug{storage_type}, 'MASTER',
708                     "got last query from a master: $debug{dsn}";
709                 
710             is $return[1]->id, 1
711                 => 'second returned value is correct';
712
713                 is $debug{storage_type}, 'MASTER',
714                      "got last query from a master: $debug{dsn}";
715
716 }
717
718 ## Test that asking for single return works
719 {
720         ok my @return = $replicated->schema->txn_do($transaction, 777)
721             => 'did transaction';
722             
723             is $return[0]->id, 777
724                 => 'first returned value is correct';
725                 
726             is $return[1]->id, 1
727                 => 'second returned value is correct';
728 }
729
730 ## Test transaction returning a single value
731
732 {
733         ok my $result = $replicated->schema->txn_do(sub {
734                 ok my $more = $replicated->schema->resultset('Artist')->find(1)
735                 => 'found inside a transaction';
736                 is $debug{storage_type}, 'MASTER', "got last query from a master: $debug{dsn}";
737                 return $more;
738         }) => 'successfully processed transaction';
739         
740         is $result->id, 1
741            => 'Got expected single result from transaction';
742 }
743
744 ## Make sure replication came back
745
746 ok $replicated->schema->resultset('Artist')->find(1)
747     => 'replicant reactivated';
748
749 is $debug{storage_type}, 'REPLICANT', "got last query from a replicant: $debug{dsn}";
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';
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'})
760            => 'properly discard changes';
761
762         is $debug{storage_type}, 'MASTER', "got last query from a master: $debug{dsn}";
763
764 }
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';
773                         is $debug{storage_type}, 'MASTER', "got last query from a master: $debug{dsn}";
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)
788                                   => 'found inside crazy deep transactions and execute_reliably';
789                                         is $debug{storage_type}, 'MASTER', "got last query from a master: $debug{dsn}";
790                                 return $more;                           
791                         });
792                 });     
793         });
794     }) => 'successfully processed transaction';
795     
796     is $result->id, 1
797        => 'Got expected single result from transaction';          
798 }     
799
800 ## Test the force_pool resultset attribute.
801
802 {
803         ok my $artist_rs = $replicated->schema->resultset('Artist')
804         => 'got artist resultset';
805            
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';
809            
810     ok my $artist = $reliable_artist_rs->find(2) 
811         => 'got an artist result via force_pool storage';
812
813         is $debug{storage_type}, 'MASTER', "got last query from a master: $debug{dsn}";
814 }
815
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';
828
829         is $debug{storage_type}, 'REPLICANT', "got last query from a replicant: $debug{dsn}";
830 }
831 ## Delete the old database files
832 $replicated->cleanup;
833
834 # vim: sw=4 sts=4 :