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