Commit | Line | Data |
e4dc89b3 |
1 | use strict; |
2 | use warnings; |
3 | use lib qw(t/lib); |
e4dc89b3 |
4 | use Test::More; |
2bf79155 |
5 | use Data::Dump qw/dump/; |
8f7986d6 |
6 | |
86583fa7 |
7 | BEGIN { |
2bf79155 |
8 | eval "use Moose"; |
86583fa7 |
9 | plan $@ |
2bf79155 |
10 | ? ( skip_all => 'needs Moose for testing' ) |
50336325 |
11 | : ( tests => 33 ); |
26ab719a |
12 | } |
13 | |
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::Replicant'; |
17 | use_ok 'DBIx::Class::Storage::DBI::Replicated'; |
0f83441a |
18 | |
19 | ## ---------------------------------------------------------------------------- |
20 | ## Build a class to hold all our required testing data and methods. |
21 | ## ---------------------------------------------------------------------------- |
22 | |
23 | TESTSCHEMACLASS: { |
2bf79155 |
24 | |
25 | package DBIx::Class::DBI::Replicated::TestReplication; |
26 | |
27 | use DBICTest; |
26ab719a |
28 | use File::Copy; |
50336325 |
29 | use Data::Dump qw/dump/; |
26ab719a |
30 | |
2bf79155 |
31 | use base qw/Class::Accessor::Fast/; |
32 | |
26ab719a |
33 | __PACKAGE__->mk_accessors( qw/schema master_path slave_paths/ ); |
2bf79155 |
34 | |
35 | ## Initialize the object |
36 | |
37 | sub new { |
26ab719a |
38 | my $class = shift @_; |
39 | my $self = $class->SUPER::new(@_); |
2bf79155 |
40 | |
41 | $self->schema( $self->init_schema ); |
26ab719a |
42 | $self->master_path("t/var/DBIxClass.db"); |
2bf79155 |
43 | |
44 | return $self; |
45 | } |
46 | |
26ab719a |
47 | ## Get the Schema and set the replication storage type |
2bf79155 |
48 | |
49 | sub init_schema { |
50 | my $class = shift @_; |
51 | my $schema = DBICTest->init_schema(storage_type=>'::DBI::Replicated'); |
52 | return $schema; |
53 | } |
26ab719a |
54 | |
55 | ## Return an Array of ArrayRefs where each ArrayRef is suitable to use for |
56 | ## $storage->connect_info to be used for connecting replicants. |
57 | |
58 | sub generate_replicant_connect_info { |
59 | my $self = shift @_; |
60 | my @dsn = map { |
61 | "dbi:SQLite:${_}"; |
62 | } @{$self->slave_paths}; |
63 | |
64 | return map { [$_,'','',{}] } @dsn; |
65 | } |
66 | |
67 | ## Do a 'good enough' replication by copying the master dbfile over each of |
50336325 |
68 | ## the slave dbfiles. If the master is SQLite we do this, otherwise we |
69 | ## just do a one second pause to let the slaves catch up. |
26ab719a |
70 | |
71 | sub replicate { |
72 | my $self = shift @_; |
73 | foreach my $slave (@{$self->slave_paths}) { |
74 | copy($self->master_path, $slave); |
75 | } |
76 | } |
77 | |
78 | ## Cleanup after ourselves. Unlink all gthe slave paths. |
79 | |
80 | sub cleanup { |
81 | my $self = shift @_; |
82 | foreach my $slave (@{$self->slave_paths}) { |
83 | unlink $slave; |
84 | } |
85 | } |
2bf79155 |
86 | } |
87 | |
88 | ## ---------------------------------------------------------------------------- |
89 | ## Create an object and run some tests |
90 | ## ---------------------------------------------------------------------------- |
91 | |
26ab719a |
92 | ## Thi first bunch of tests are basic, just make sure all the bits are behaving |
2bf79155 |
93 | |
26ab719a |
94 | ok my $replicated = DBIx::Class::DBI::Replicated::TestReplication |
95 | ->new({ |
96 | slave_paths=>[ |
97 | "t/var/DBIxClass_slave1.db", |
98 | "t/var/DBIxClass_slave2.db", |
99 | ], |
100 | }) => 'Created a replication object'; |
2bf79155 |
101 | |
26ab719a |
102 | isa_ok $replicated->schema |
2bf79155 |
103 | => 'DBIx::Class::Schema'; |
104 | |
26ab719a |
105 | isa_ok $replicated->schema->storage |
106 | => 'DBIx::Class::Storage::DBI::Replicated'; |
107 | |
108 | ok $replicated->schema->storage->meta |
109 | => 'has a meta object'; |
110 | |
111 | isa_ok $replicated->schema->storage->master |
112 | => 'DBIx::Class::Storage::DBI'; |
113 | |
114 | isa_ok $replicated->schema->storage->pool |
115 | => 'DBIx::Class::Storage::DBI::Replicated::Pool'; |
116 | |
117 | isa_ok $replicated->schema->storage->balancer |
118 | => 'DBIx::Class::Storage::DBI::Replicated::Balancer'; |
119 | |
120 | ok my @replicant_connects = $replicated->generate_replicant_connect_info |
121 | => 'got replication connect information'; |
122 | |
123 | ok my @replicated_storages = $replicated->schema->storage->create_replicants(@replicant_connects) |
124 | => 'Created some storages suitable for replicants'; |
125 | |
126 | isa_ok $replicated->schema->storage->current_replicant |
127 | => 'DBIx::Class::Storage::DBI'; |
128 | |
129 | ok $replicated->schema->storage->pool->has_replicants |
130 | => 'does have replicants'; |
131 | |
132 | is $replicated->schema->storage->num_replicants => 2 |
133 | => 'has two replicants'; |
134 | |
135 | isa_ok $replicated_storages[0] |
136 | => 'DBIx::Class::Storage::DBI::Replicated::Replicant'; |
137 | |
138 | isa_ok $replicated_storages[1] |
139 | => 'DBIx::Class::Storage::DBI::Replicated::Replicant'; |
140 | |
141 | isa_ok $replicated->schema->storage->replicants->{"t/var/DBIxClass_slave1.db"} |
142 | => 'DBIx::Class::Storage::DBI::Replicated::Replicant'; |
143 | |
144 | isa_ok $replicated->schema->storage->replicants->{"t/var/DBIxClass_slave2.db"} |
145 | => 'DBIx::Class::Storage::DBI::Replicated::Replicant'; |
146 | |
147 | ## Add some info to the database |
148 | |
149 | $replicated |
150 | ->schema |
151 | ->populate('Artist', [ |
152 | [ qw/artistid name/ ], |
153 | [ 4, "Ozric Tentacles"], |
154 | ]); |
155 | |
156 | ## Make sure all the slaves have the table definitions |
157 | |
158 | $replicated->replicate; |
159 | |
160 | ## Make sure we can read the data. |
161 | |
162 | ok my $artist1 = $replicated->schema->resultset('Artist')->find(4) |
163 | => 'Created Result'; |
164 | |
165 | isa_ok $artist1 |
166 | => 'DBICTest::Artist'; |
167 | |
168 | is $artist1->name, 'Ozric Tentacles' |
169 | => 'Found expected name for first result'; |
170 | |
171 | ## Add some new rows that only the master will have This is because |
172 | ## we overload any type of write operation so that is must hit the master |
173 | ## database. |
174 | |
175 | $replicated |
176 | ->schema |
177 | ->populate('Artist', [ |
178 | [ qw/artistid name/ ], |
179 | [ 5, "Doom's Children"], |
180 | [ 6, "Dead On Arrival"], |
181 | [ 7, "Watergate"], |
182 | ]); |
183 | |
184 | ## Alright, the database 'cluster' is not in a consistent state. When we do |
185 | ## a read now we expect bad news |
186 | |
187 | is $replicated->schema->resultset('Artist')->find(5), undef |
188 | => 'read after disconnect fails because it uses a replicant which we have neglected to "replicate" yet'; |
189 | |
190 | ## Make sure all the slaves have the table definitions |
191 | $replicated->replicate; |
192 | |
193 | ## Should find some data now |
194 | |
195 | ok my $artist2 = $replicated->schema->resultset('Artist')->find(5) |
196 | => 'Sync succeed'; |
197 | |
198 | isa_ok $artist2 |
199 | => 'DBICTest::Artist'; |
200 | |
201 | is $artist2->name, "Doom's Children" |
202 | => 'Found expected name for first result'; |
203 | |
204 | ## What happens when we disconnect all the replicants? |
205 | |
50336325 |
206 | is $replicated->schema->storage->pool->connected_replicants => 2 |
207 | => "both replicants are connected"; |
208 | |
26ab719a |
209 | $replicated->schema->storage->replicants->{"t/var/DBIxClass_slave1.db"}->disconnect; |
210 | $replicated->schema->storage->replicants->{"t/var/DBIxClass_slave2.db"}->disconnect; |
211 | |
50336325 |
212 | is $replicated->schema->storage->pool->connected_replicants => 0 |
213 | => "both replicants are now disconnected"; |
214 | |
215 | ## All these should pass, since the database should automatically reconnect |
216 | |
26ab719a |
217 | ok my $artist3 = $replicated->schema->resultset('Artist')->find(6) |
218 | => 'Still finding stuff.'; |
2bf79155 |
219 | |
26ab719a |
220 | isa_ok $artist3 |
221 | => 'DBICTest::Artist'; |
2bf79155 |
222 | |
26ab719a |
223 | is $artist3->name, "Dead On Arrival" |
224 | => 'Found expected name for first result'; |
2bf79155 |
225 | |
50336325 |
226 | is $replicated->schema->storage->pool->connected_replicants => 1 |
227 | => "One replicant reconnected to handle the job"; |
2156bbdd |
228 | |
0f83441a |
229 | ## Delete the old database files |
50336325 |
230 | $replicated->cleanup; |
0f83441a |
231 | |
232 | |
233 | |
234 | |
235 | |
236 | |