add TODO on constraint check
[dbsrgits/DBIx-Class.git] / t / 93storage_replication.t
CommitLineData
e4dc89b3 1use strict;
2use warnings;
3use lib qw(t/lib);
e4dc89b3 4use Test::More;
8f7986d6 5
86583fa7 6BEGIN {
7 eval "use DBD::Multi";
8 plan $@
9 ? ( skip_all => 'needs DBD::Multi for testing' )
0f83441a 10 : ( tests => 18 );
11}
12
13## ----------------------------------------------------------------------------
14## Build a class to hold all our required testing data and methods.
15## ----------------------------------------------------------------------------
16
17TESTSCHEMACLASS: {
18
19 package DBIx::Class::DBI::Replication::TestReplication;
20
21 use DBI;
22 use DBICTest;
23 use File::Copy;
24
25 ## Create a constructor
26
27 sub new {
28 my $class = shift @_;
29 my %params = @_;
30
31 my $self = bless {
32 db_paths => $params{db_paths},
33 dsns => $class->init_dsns(%params),
34 schema=>$class->init_schema,
35 }, $class;
36
37 $self->connect;
38 return $self;
39 }
40
41 ## get the DSNs. We build this up from the list of file paths
42
43 sub init_dsns {
44 my $class = shift @_;
45 my %params = @_;
46 my $db_paths = $params{db_paths};
47
48 my @dsn = map {
49 "dbi:SQLite:${_}";
50 } @$db_paths;
51
52 return \@dsn;
53 }
54
55 ## get the Schema and set the replication storage type
56
57 sub init_schema {
58 my $class = shift @_;
59 my $schema = DBICTest->init_schema();
60 $schema->storage_type( '::DBI::Replication' );
61
62 return $schema;
63 }
64
65 ## connect the Schema
66
67 sub connect {
68 my $self = shift @_;
69 my ($master, @slaves) = @{$self->{dsns}};
70 my @connections = ([$master, '','', {AutoCommit=>1, PrintError=>0}]);
71 my @slavesob;
72
73 foreach my $slave (@slaves)
74 {
75 my $dbh = shift @{$self->{slaves}}
76 || DBI->connect($slave,"","",{PrintError=>0, PrintWarn=>0});
77
78 push @connections,
79 [$dbh, '','',{priority=>10}];
80
81 push @slavesob,
82 $dbh;
83 }
84
85 ## Keep track of the created slave databases
86 $self->{slaves} = \@slavesob;
87
88 $self
89 ->{schema}
90 ->connect([
91 @connections,
92 {limit_dialect => 'LimitXY'}
93 ]);
94 }
95
96 ## replication
97
98 sub replicate {
99 my $self = shift @_;
100 my ($master, @slaves) = @{$self->{db_paths}};
101
102 foreach my $slave (@slaves) {
103 copy($master, $slave);
104 }
105 }
106
107 ## Cleanup afer ourselves.
108
109 sub cleanup {
110 my $self = shift @_;
111 my ($master, @slaves) = @{$self->{db_paths}};
112
113 foreach my $slave (@slaves) {
114 unlink $slave;
115 }
116 }
117
118 ## Force a reconnection
119
120 sub reconnect {
121 my $self = shift @_;
122 my $schema = $self->connect;
123 $self->{schema} = $schema;
124 return $schema;
125 }
86583fa7 126}
e4dc89b3 127
0f83441a 128## ----------------------------------------------------------------------------
129## Create an object and run some tests
130## ----------------------------------------------------------------------------
131
132my %params = (
133 db_paths => [
134 "t/var/DBIxClass.db",
135 "t/var/DBIxClass_slave1.db",
136 "t/var/DBIxClass_slave2.db",
137 ],
138);
139
140ok my $replicate = DBIx::Class::DBI::Replication::TestReplication->new(%params)
141 => 'Created a replication object';
142
143isa_ok $replicate->{schema}
144 => 'DBIx::Class::Schema';
145
146## Add some info to the database
147
148$replicate
149 ->{schema}
150 ->populate('Artist', [
151 [ qw/artistid name/ ],
152 [ 4, "Ozric Tentacles"],
153 ]);
154
155## Make sure all the slaves have the table definitions
156
157$replicate->replicate;
158
159## Make sure we can read the data.
160
161ok my $artist1 = $replicate->{schema}->resultset('Artist')->find(4)
162 => 'Created Result';
163
164isa_ok $artist1
165 => 'DBICTest::Artist';
166
167is $artist1->name, 'Ozric Tentacles'
168 => 'Found expected name for first result';
169
170## Add some new rows that only the master will have This is because
171## we overload any type of write operation so that is must hit the master
172## database.
173
174use Fcntl qw (:flock);
175
176my $master_path = $replicate->{db_paths}->[0];
177open LOCKFILE, ">>$master_path"
178 or die "Cannot open $master_path";
179flock(LOCKFILE, LOCK_EX);
180
181$replicate
182 ->{schema}
183 ->populate('Artist', [
184 [ qw/artistid name/ ],
185 [ 5, "Doom's Children"],
186 [ 6, "Dead On Arrival"],
187 [ 7, "Watergate"],
188 ]);
189
190## Reconnect the database
191$replicate->reconnect;
192
193## Alright, the database 'cluster' is not in a consistent state. When we do
194## a read now we expect bad news
195
196is $replicate->{schema}->resultset('Artist')->find(5), undef
197 => 'read after disconnect fails because it uses slave 1 which we have neglected to "replicate" yet';
198
199## Make sure all the slaves have the table definitions
200$replicate->replicate;
201
202## Should find some data now
203
204ok my $artist2 = $replicate->{schema}->resultset('Artist')->find(5)
205 => 'Sync succeed';
206
207isa_ok $artist2
208 => 'DBICTest::Artist';
209
210is $artist2->name, "Doom's Children"
211 => 'Found expected name for first result';
212
213## What happens when we delete one of the slaves?
214
215ok my $slave1 = @{$replicate->{slaves}}[0]
216 => 'Got Slave1';
217
218ok $slave1->disconnect
219 => 'disconnected slave1';
220
221$replicate->reconnect;
222
223ok my $artist3 = $replicate->{schema}->resultset('Artist')->find(6)
224 => 'Still finding stuff.';
225
226isa_ok $artist3
227 => 'DBICTest::Artist';
228
229is $artist3->name, "Dead On Arrival"
230 => 'Found expected name for first result';
231
232## Let's delete all the slaves
233
234ok my $slave2 = @{$replicate->{slaves}}[1]
235 => 'Got Slave2';
236
237ok $slave2->disconnect
238 => 'Disconnected slave2';
239
240$replicate->reconnect;
241
242## We expect an error now, since all the slaves are dead
243
244eval {
245 $replicate->{schema}->resultset('Artist')->find(4)->name;
246};
247
248ok $@ => 'Got error when trying to find artistid 4';
249
250## This should also be an error
251
252eval {
253 my $artist4 = $replicate->{schema}->resultset('Artist')->find(7);
254};
255
256ok $@ => 'Got read errors after everything failed';
257
258## Delete the old database files
259$replicate->cleanup;
260
261
262
263
264
265