10 ? ( skip_all => 'needs Moose for testing' )
14 use_ok 'DBIx::Class::Storage::DBI::Replicated::Pool';
15 use_ok 'DBIx::Class::Storage::DBI::Replicated::Balancer';
16 use_ok 'DBIx::Class::Storage::DBI::Replicated::Balancer::Random';
17 use_ok 'DBIx::Class::Storage::DBI::Replicated::Replicant';
18 use_ok 'DBIx::Class::Storage::DBI::Replicated';
20 ## ----------------------------------------------------------------------------
21 ## Build a class to hold all our required testing data and methods.
22 ## ----------------------------------------------------------------------------
26 ## --------------------------------------------------------------------- ##
27 ## Create an object to contain your replicated stuff.
28 ## --------------------------------------------------------------------- ##
30 package DBIx::Class::DBI::Replicated::TestReplication;
33 use base qw/Class::Accessor::Fast/;
35 __PACKAGE__->mk_accessors( qw/schema/ );
37 ## Initialize the object
41 my $self = $class->SUPER::new(@_);
43 $self->schema( $self->init_schema );
47 ## Get the Schema and set the replication storage type
51 my $schema = DBICTest->init_schema(
52 storage_type=>'::DBI::Replicated',
54 balancer_type=>'DBIx::Class::Storage::DBI::Replicated::Balancer::Random',
60 sub generate_replicant_connect_info {}
65 ## --------------------------------------------------------------------- ##
66 ## Subclass for when you are using SQLite for testing, this provides a fake
67 ## replication support.
68 ## --------------------------------------------------------------------- ##
70 package DBIx::Class::DBI::Replicated::TestReplication::SQLite;
74 use base 'DBIx::Class::DBI::Replicated::TestReplication';
76 __PACKAGE__->mk_accessors( qw/master_path slave_paths/ );
78 ## Set the mastep path from DBICTest
82 my $self = $class->SUPER::new(@_);
84 $self->master_path( DBICTest->_sqlite_dbfilename );
86 "t/var/DBIxClass_slave1.db",
87 "t/var/DBIxClass_slave2.db",
93 ## Return an Array of ArrayRefs where each ArrayRef is suitable to use for
94 ## $storage->connect_info to be used for connecting replicants.
96 sub generate_replicant_connect_info {
100 } @{$self->slave_paths};
102 return map { [$_,'','',{AutoCommit=>1}] } @dsn;
105 ## Do a 'good enough' replication by copying the master dbfile over each of
106 ## the slave dbfiles. If the master is SQLite we do this, otherwise we
107 ## just do a one second pause to let the slaves catch up.
111 foreach my $slave (@{$self->slave_paths}) {
112 copy($self->master_path, $slave);
116 ## Cleanup after ourselves. Unlink all gthe slave paths.
120 foreach my $slave (@{$self->slave_paths}) {
125 ## --------------------------------------------------------------------- ##
126 ## Subclass for when you are setting the databases via custom export vars
127 ## This is for when you have a replicating database setup that you are
128 ## going to test against. You'll need to define the correct $ENV and have
129 ## two slave databases to test against, as well as a replication system
130 ## that will replicate in less than 1 second.
131 ## --------------------------------------------------------------------- ##
133 package DBIx::Class::DBI::Replicated::TestReplication::Custom;
134 use base 'DBIx::Class::DBI::Replicated::TestReplication';
136 ## Return an Array of ArrayRefs where each ArrayRef is suitable to use for
137 ## $storage->connect_info to be used for connecting replicants.
139 sub generate_replicant_connect_info {
141 [$ENV{"DBICTEST_SLAVE0_DSN"}, $ENV{"DBICTEST_SLAVE0_DBUSER"}, $ENV{"DBICTEST_SLAVE0_DBPASS"}, {AutoCommit => 1}],
142 [$ENV{"DBICTEST_SLAVE1_DSN"}, $ENV{"DBICTEST_SLAVE1_DBUSER"}, $ENV{"DBICTEST_SLAVE1_DBPASS"}, {AutoCommit => 1}],
146 ## pause a bit to let the replication catch up
153 ## ----------------------------------------------------------------------------
154 ## Create an object and run some tests
155 ## ----------------------------------------------------------------------------
157 ## Thi first bunch of tests are basic, just make sure all the bits are behaving
159 my $replicated_class = DBICTest->has_custom_dsn ?
160 'DBIx::Class::DBI::Replicated::TestReplication::Custom' :
161 'DBIx::Class::DBI::Replicated::TestReplication::SQLite';
163 ok my $replicated = $replicated_class->new
164 => 'Created a replication object';
166 isa_ok $replicated->schema
167 => 'DBIx::Class::Schema';
169 isa_ok $replicated->schema->storage
170 => 'DBIx::Class::Storage::DBI::Replicated';
172 ok $replicated->schema->storage->meta
173 => 'has a meta object';
175 isa_ok $replicated->schema->storage->master
176 => 'DBIx::Class::Storage::DBI';
178 isa_ok $replicated->schema->storage->pool
179 => 'DBIx::Class::Storage::DBI::Replicated::Pool';
181 isa_ok $replicated->schema->storage->balancer
182 => 'DBIx::Class::Storage::DBI::Replicated::Balancer';
184 ok my @replicant_connects = $replicated->generate_replicant_connect_info
185 => 'got replication connect information';
187 ok my @replicated_storages = $replicated->schema->storage->connect_replicants(@replicant_connects)
188 => 'Created some storages suitable for replicants';
190 isa_ok $replicated->schema->storage->balancer->current_replicant
191 => 'DBIx::Class::Storage::DBI';
193 ok $replicated->schema->storage->pool->has_replicants
194 => 'does have replicants';
196 is $replicated->schema->storage->num_replicants => 2
197 => 'has two replicants';
199 isa_ok $replicated_storages[0]
200 => 'DBIx::Class::Storage::DBI::Replicated::Replicant';
202 isa_ok $replicated_storages[1]
203 => 'DBIx::Class::Storage::DBI::Replicated::Replicant';
205 isa_ok $replicated->schema->storage->replicants->{"t/var/DBIxClass_slave1.db"}
206 => 'DBIx::Class::Storage::DBI::Replicated::Replicant';
208 isa_ok $replicated->schema->storage->replicants->{"t/var/DBIxClass_slave2.db"}
209 => 'DBIx::Class::Storage::DBI::Replicated::Replicant';
211 ## Add some info to the database
215 ->populate('Artist', [
216 [ qw/artistid name/ ],
217 [ 4, "Ozric Tentacles"],
220 ## Make sure all the slaves have the table definitions
222 $replicated->replicate;
224 ## Make sure we can read the data.
226 ok my $artist1 = $replicated->schema->resultset('Artist')->find(4)
230 => 'DBICTest::Artist';
232 is $artist1->name, 'Ozric Tentacles'
233 => 'Found expected name for first result';
235 ## Add some new rows that only the master will have This is because
236 ## we overload any type of write operation so that is must hit the master
241 ->populate('Artist', [
242 [ qw/artistid name/ ],
243 [ 5, "Doom's Children"],
244 [ 6, "Dead On Arrival"],
248 ## Alright, the database 'cluster' is not in a consistent state. When we do
249 ## a read now we expect bad news
251 is $replicated->schema->resultset('Artist')->find(5), undef
252 => 'read after disconnect fails because it uses a replicant which we have neglected to "replicate" yet';
254 ## Make sure all the slaves have the table definitions
255 $replicated->replicate;
257 ## Should find some data now
259 ok my $artist2 = $replicated->schema->resultset('Artist')->find(5)
263 => 'DBICTest::Artist';
265 is $artist2->name, "Doom's Children"
266 => 'Found expected name for first result';
268 ## What happens when we disconnect all the replicants?
270 is $replicated->schema->storage->pool->connected_replicants => 2
271 => "both replicants are connected";
273 $replicated->schema->storage->replicants->{"t/var/DBIxClass_slave1.db"}->disconnect;
274 $replicated->schema->storage->replicants->{"t/var/DBIxClass_slave2.db"}->disconnect;
276 is $replicated->schema->storage->pool->connected_replicants => 0
277 => "both replicants are now disconnected";
279 ## All these should pass, since the database should automatically reconnect
281 ok my $artist3 = $replicated->schema->resultset('Artist')->find(6)
282 => 'Still finding stuff.';
285 => 'DBICTest::Artist';
287 is $artist3->name, "Dead On Arrival"
288 => 'Found expected name for first result';
290 is $replicated->schema->storage->pool->connected_replicants => 1
291 => "One replicant reconnected to handle the job";
293 ## What happens when we try to select something that doesn't exist?
295 ok ! $replicated->schema->resultset('Artist')->find(666)
296 => 'Correctly failed to find something.';
298 ## test the reliable option
302 $replicated->schema->storage->set_reliable_storage;
304 ok $replicated->schema->resultset('Artist')->find(2)
305 => 'Read from master 1';
307 ok $replicated->schema->resultset('Artist')->find(5)
308 => 'Read from master 2';
310 $replicated->schema->storage->set_balanced_storage;
312 ok $replicated->schema->resultset('Artist')->find(3)
313 => 'Read from replicant';
316 ## Make sure when reliable goes out of scope, we are using replicants again
318 ok $replicated->schema->resultset('Artist')->find(1)
319 => 'back to replicant 1.';
321 ok $replicated->schema->resultset('Artist')->find(2)
322 => 'back to replicant 2.';
325 ## Delete the old database files
326 $replicated->cleanup;