line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Test::DB::Shared::mysqld; |
2
|
|
|
|
|
|
|
$Test::DB::Shared::mysqld::VERSION = '0.002'; |
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
=head1 NAME |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
Test::DB::Shared::mysqld - Replaces (and decorate) Test::mysqld to share the MySQL instance between your tests |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
=head1 SYNOPSIS |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
If in your test you use L<Test::mysqld>, this acts as a replacement for Test::mysqld: |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
my $mysqld = Test::DB::Shared::mysqld->new( |
13
|
|
|
|
|
|
|
test_namespace => 'myapp', |
14
|
|
|
|
|
|
|
# Then it's plain Test::mysqld config |
15
|
|
|
|
|
|
|
my_cnf => { |
16
|
|
|
|
|
|
|
'skip-networking' => '', # no TCP socket |
17
|
|
|
|
|
|
|
} |
18
|
|
|
|
|
|
|
); |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
# and use like Test::mysqld: |
21
|
|
|
|
|
|
|
my $dbh = DBI->connect( |
22
|
|
|
|
|
|
|
$mysqld->dsn(), undef, '' |
23
|
|
|
|
|
|
|
); |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
And that's it. No special code to write, no restructuring of your tests, and using as |
26
|
|
|
|
|
|
|
a prove plugin is optional. |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
=head1 STATEMENT |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
What you need is a test database, not a test mysqld instance. |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
=head1 HOW TO USE IT |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
See synopsis for the change to your test code. For the rest, you need to use C<prove -j number> |
35
|
|
|
|
|
|
|
to benefit from it. |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
If not all your test use the test db, best results will be obtained by using C<prove -s -j number> |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
=head2 Using it as a prove Plugin |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
To speed things even further, you can use that as a prove plugin, with an optional config file: |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
prove -PTest::DB::Shared::mysqld |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
Or |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
prove -PTest::DB::Shared::mysqld=./testmysqld.json |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
The ./testmysqld.json file can contain the arguments to Test::DB::Shared::mysqld in a json format (see SYNOPSIS). They |
50
|
|
|
|
|
|
|
will be used to build one instance for the whole test suite. |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
If no such file is given, the default configuration is the one specified in the SYNOPSIS, but with a randomly generated test_namespace. |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
Note that using this plugin will result in all your Test::DB::Shared::mysqld instances in your t/ files using the same configuration, |
55
|
|
|
|
|
|
|
regardless of what configuration you give in this or this test. |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
=head1 LIMITATIONS |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
Do NOT use that if your test involves doing anything outside a test database. Tests that manage databases |
60
|
|
|
|
|
|
|
will probably break this. |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
Not all mysqld methods are available. Calls like 'start', 'stop', 'setup', 'read_log' .. are not implemented. |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
=head1 WHAT THIS DOES |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
The first time this is used, it will create a Test::mysqld instance in the current process. Then concurrent processes |
67
|
|
|
|
|
|
|
that use the same module (with the same parameters) will be given a new Database in this already running instance, instead |
68
|
|
|
|
|
|
|
of a new MySQL instance. |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
When this goes out of scope, the test database is destroy, and the last process to destroy the last database will tear |
71
|
|
|
|
|
|
|
down the MySQL instance. |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
=head1 BUGS, DIAGNOSTICS and TROUBLESHOOTING |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
There are probably some. To diagnose them, you can run your test in verbose mode ( prove -v ). If that doesn't help, |
76
|
|
|
|
|
|
|
you can 'use Log::Any::Adapter qw/Stderr/' at the top of your test to get some very verbose tracing. |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
If you SIGKILL your whole test suite, bad things will happen. Running in verbose mode |
79
|
|
|
|
|
|
|
will most probably tell you which files you should clean up on your filesystem to get back to a working state. |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
=head1 METHODS |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
=cut |
84
|
|
|
|
|
|
|
|
85
|
9
|
|
|
9
|
|
359585
|
use Moose; |
|
9
|
|
|
|
|
3487157
|
|
|
9
|
|
|
|
|
65
|
|
86
|
9
|
|
|
9
|
|
63512
|
use Log::Any qw/$log/; |
|
9
|
|
|
|
|
54159
|
|
|
9
|
|
|
|
|
45
|
|
87
|
|
|
|
|
|
|
|
88
|
9
|
|
|
9
|
|
15385
|
use DBI; |
|
9
|
|
|
|
|
26
|
|
|
9
|
|
|
|
|
324
|
|
89
|
|
|
|
|
|
|
|
90
|
9
|
|
|
9
|
|
4382
|
use JSON; |
|
9
|
|
|
|
|
67496
|
|
|
9
|
|
|
|
|
50
|
|
91
|
9
|
|
|
9
|
|
4537
|
use Test::mysqld; |
|
9
|
|
|
|
|
191479
|
|
|
9
|
|
|
|
|
268
|
|
92
|
|
|
|
|
|
|
|
93
|
9
|
|
|
9
|
|
2220
|
use File::Slurp; |
|
9
|
|
|
|
|
17419
|
|
|
9
|
|
|
|
|
4070
|
|
94
|
9
|
|
|
9
|
|
960
|
use File::Spec; |
|
9
|
|
|
|
|
24
|
|
|
9
|
|
|
|
|
155
|
|
95
|
9
|
|
|
9
|
|
3506
|
use File::Flock::Tiny; |
|
9
|
|
|
|
|
6848
|
|
|
9
|
|
|
|
|
257
|
|
96
|
|
|
|
|
|
|
|
97
|
9
|
|
|
9
|
|
48
|
use POSIX qw(SIGTERM WNOHANG); |
|
9
|
|
|
|
|
20
|
|
|
9
|
|
|
|
|
41
|
|
98
|
|
|
|
|
|
|
|
99
|
9
|
|
|
9
|
|
469
|
use Test::More qw//; |
|
9
|
|
|
|
|
18
|
|
|
9
|
|
|
|
|
13210
|
|
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
# Settings |
102
|
|
|
|
|
|
|
has 'test_namespace' => ( is => 'ro', isa => 'Str', default => 'test_db_shared' ); |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
# Public facing stuff |
105
|
|
|
|
|
|
|
has 'dsn' => ( is => 'ro', isa => 'Str', lazy_build => 1 ); |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
# Internal cuisine |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
has '_lock_file' => ( is => 'ro', isa => 'Str', lazy_build => 1 ); |
111
|
|
|
|
|
|
|
has '_mysqld_file' => ( is => 'ro', isa => 'Str', lazy_build => 1 ); |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
sub _build__lock_file{ |
114
|
7
|
|
|
7
|
|
24
|
my ($self) = @_; |
115
|
7
|
|
|
|
|
363
|
return File::Spec->catfile( File::Spec->tmpdir() , $self->_namespace().'.lock' ).''; |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
sub _build__mysqld_file{ |
118
|
7
|
|
|
7
|
|
30
|
my ($self) = @_; |
119
|
7
|
|
|
|
|
421
|
return File::Spec->catfile( File::Spec->tmpdir() , $self->_namespace().'.mysqld' ).''; |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
has '_testmysqld_args' => ( is => 'ro', isa => 'HashRef', required => 1); |
123
|
|
|
|
|
|
|
has '_temp_db_name' => ( is => 'ro', isa => 'Str', lazy_build => 1 ); |
124
|
|
|
|
|
|
|
has '_shared_mysqld' => ( is => 'ro', isa => 'HashRef', lazy_build => 1 ); |
125
|
|
|
|
|
|
|
has '_instance_pid' => ( is => 'ro', isa => 'Int', required => 1); |
126
|
|
|
|
|
|
|
has '_holds_mysqld' => ( is => 'rw', isa => 'Maybe[Test::mysqld]', default => undef); |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
around BUILDARGS => sub { |
129
|
|
|
|
|
|
|
my ($orig, $class, @rest ) = @_; |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
my $hash_args = $class->$orig(@rest); |
132
|
|
|
|
|
|
|
my $test_namespace = delete $hash_args->{test_namespace}; |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
return { |
135
|
|
|
|
|
|
|
_testmysqld_args => $hash_args, |
136
|
|
|
|
|
|
|
_instance_pid => $$, |
137
|
|
|
|
|
|
|
( $test_namespace ? ( test_namespace => $test_namespace ) : () ), |
138
|
|
|
|
|
|
|
( $ENV{TEST_DB_SHARED_NAMESPACE} ? ( test_namespace => $ENV{TEST_DB_SHARED_NAMESPACE} ) : () ), |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
}; |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
=head2 load |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
L<App::Prove> plugin implementation. Do NOT use that yourself. |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
=cut |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
{ |
150
|
|
|
|
|
|
|
my $plugin_instance; |
151
|
|
|
|
|
|
|
sub load{ |
152
|
2
|
|
|
2
|
1
|
1260
|
my ($class, $prove) = @_; |
153
|
2
|
100
|
|
|
|
23
|
my @args = @{$prove->{args} || []}; |
|
2
|
|
|
|
|
105
|
|
154
|
2
|
|
|
|
|
177
|
my $config = { |
155
|
|
|
|
|
|
|
test_namespace => 'plugin'.$$.int( rand(1000) ), |
156
|
|
|
|
|
|
|
my_cnf => { |
157
|
|
|
|
|
|
|
'skip-networking' => '', # no TCP socket |
158
|
|
|
|
|
|
|
} |
159
|
|
|
|
|
|
|
}; |
160
|
2
|
|
|
|
|
7
|
my $config_file = $args[0]; |
161
|
2
|
100
|
|
|
|
25
|
unless( $config_file ){ |
162
|
1
|
|
|
|
|
116
|
Test::More::diag( __PACKAGE__." PID $$ config file is not given. Using default config" ); |
163
|
|
|
|
|
|
|
}else{ |
164
|
1
|
50
|
|
|
|
13
|
if( ! -e $config_file ){ |
165
|
0
|
|
|
|
|
0
|
confess("Cannot find config file $config_file"); |
166
|
|
|
|
|
|
|
}else{ |
167
|
1
|
|
|
|
|
6
|
$config = JSON::decode_json( scalar( File::Slurp::read_file( $config_file, { binmode => ':raw' } ) ) ); |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
} |
170
|
2
|
|
|
|
|
955
|
$plugin_instance = $class->new( $config ); |
171
|
|
|
|
|
|
|
## Just in case. |
172
|
2
|
|
|
|
|
64
|
unlink( $plugin_instance->_mysqld_file() ); |
173
|
2
|
|
|
|
|
67
|
Test::More::diag( __PACKAGE__." PID $$ plugin instance mysqld lives at ".$plugin_instance->dsn() ); |
174
|
0
|
|
|
|
|
0
|
Test::More::diag( __PACKAGE__." PID $$ plugin instance mysqld descriptor is ".$plugin_instance->_mysqld_file() ); |
175
|
|
|
|
|
|
|
# This will inform all the other instances to reuse the namespace (see BUILDARGS). |
176
|
0
|
|
|
|
|
0
|
$ENV{TEST_DB_SHARED_NAMESPACE} = $plugin_instance->test_namespace(); |
177
|
0
|
|
|
|
|
0
|
return 1; |
178
|
|
|
|
|
|
|
} |
179
|
0
|
|
|
0
|
0
|
0
|
sub plugin_instance{ return $plugin_instance; } |
180
|
9
|
|
|
9
|
0
|
222
|
sub tear_down_plugin_instance{ $plugin_instance = undef; } |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
# For the plugin to 'just work' |
183
|
|
|
|
|
|
|
# on unloading this code. |
184
|
|
|
|
|
|
|
sub END{ |
185
|
9
|
|
|
9
|
|
915579
|
__PACKAGE__->tear_down_plugin_instance(); |
186
|
|
|
|
|
|
|
} |
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
sub _namespace{ |
192
|
14
|
|
|
14
|
|
47
|
my ($self) = @_; |
193
|
14
|
|
|
|
|
454
|
return 'tdbs49C7_'.$self->test_namespace(); |
194
|
|
|
|
|
|
|
} |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
# Build a temp DB name according to this pid. |
197
|
|
|
|
|
|
|
# Note it only works because the instance of the DB will run locally. |
198
|
|
|
|
|
|
|
sub _build__temp_db_name{ |
199
|
0
|
|
|
0
|
|
0
|
my ($self) = @_; |
200
|
0
|
|
|
|
|
0
|
return $self->_namespace().( $self + 0 ); |
201
|
|
|
|
|
|
|
} |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
sub _build__shared_mysqld{ |
204
|
14
|
|
|
14
|
|
45
|
my ($self) = @_; |
205
|
|
|
|
|
|
|
# Two cases here. |
206
|
|
|
|
|
|
|
# Either the test mysqld is there and we returned the already built dsn |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
# Or it's not there and we need to build it IN A MUTEX way. |
209
|
|
|
|
|
|
|
# For a start, let's assume it's not there |
210
|
|
|
|
|
|
|
return $self->_monitor(sub{ |
211
|
14
|
|
|
14
|
|
55
|
my $saved_mysqld; |
212
|
14
|
50
|
|
|
|
609
|
if( ! -e $self->_mysqld_file() ){ |
213
|
14
|
|
|
|
|
215
|
Test::More::note( "PID $$ Creating new Test::mysqld instance" ); |
214
|
14
|
|
|
|
|
3817
|
$log->info("PID $$ Creating new Test::mysqld instance"); |
215
|
14
|
50
|
|
|
|
70
|
my $mysqld = Test::mysqld->new( %{$self->_testmysqld_args()} ) or confess |
|
14
|
|
|
|
|
706
|
|
216
|
|
|
|
|
|
|
$Test::mysqld::errstr; |
217
|
0
|
|
|
|
|
0
|
$log->trace("PID $$ Saving all $mysqld public properties"); |
218
|
|
|
|
|
|
|
|
219
|
0
|
|
|
|
|
0
|
$saved_mysqld = {}; |
220
|
0
|
|
|
|
|
0
|
foreach my $property ( 'dsn', 'pid' ){ |
221
|
0
|
|
|
|
|
0
|
$saved_mysqld->{$property} = $mysqld->$property().'' |
222
|
|
|
|
|
|
|
} |
223
|
0
|
|
|
|
|
0
|
$saved_mysqld->{pid_file} = $mysqld->my_cnf()->{'pid-file'}; |
224
|
|
|
|
|
|
|
# DO NOT LET mysql think it can manage its mysqld PID |
225
|
0
|
|
|
|
|
0
|
$mysqld->pid( undef ); |
226
|
|
|
|
|
|
|
|
227
|
0
|
|
|
|
|
0
|
$self->_holds_mysqld( $mysqld ); |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
# Create the pid_registry container. |
230
|
0
|
|
|
|
|
0
|
$log->trace("PID $$ creating pid_registry table in instance"); |
231
|
|
|
|
|
|
|
$self->_with_shared_dbh( $saved_mysqld->{dsn}, |
232
|
|
|
|
|
|
|
sub{ |
233
|
0
|
|
|
|
|
0
|
my ($dbh) = @_; |
234
|
0
|
|
|
|
|
0
|
$dbh->do('CREATE TABLE pid_registry(pid INTEGER NOT NULL, instance BIGINT NOT NULL, PRIMARY KEY(pid, instance))'); |
235
|
0
|
|
|
|
|
0
|
}); |
236
|
0
|
|
|
|
|
0
|
my $json_mysqld = JSON::encode_json( $saved_mysqld ); |
237
|
0
|
|
|
|
|
0
|
$log->trace("PID $$ Saving ".$json_mysqld ); |
238
|
0
|
|
|
|
|
0
|
File::Slurp::write_file( $self->_mysqld_file() , {binmode => ':raw'}, |
239
|
|
|
|
|
|
|
$json_mysqld ); |
240
|
|
|
|
|
|
|
} else { |
241
|
0
|
|
|
|
|
0
|
Test::More::note("PID $$ Reusing Test::mysqld from ".$self->_mysqld_file()); |
242
|
0
|
|
|
|
|
0
|
$log->info("PID $$ file ".$self->_mysqld_file()." is there. Reusing cluster"); |
243
|
0
|
|
|
|
|
0
|
$saved_mysqld = JSON::decode_json( |
244
|
|
|
|
|
|
|
scalar( File::Slurp::read_file( $self->_mysqld_file() , {binmode => ':raw'} ) ) |
245
|
|
|
|
|
|
|
); |
246
|
|
|
|
|
|
|
} |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
$self->_with_shared_dbh( $saved_mysqld->{dsn}, |
249
|
|
|
|
|
|
|
sub{ |
250
|
0
|
|
|
|
|
0
|
my $dbh = shift; |
251
|
0
|
|
|
|
|
0
|
$dbh->do('INSERT INTO pid_registry( pid, instance ) VALUES (?,?)' , {}, |
252
|
|
|
|
|
|
|
$self->_instance_pid(), ( $self + 0 ) |
253
|
|
|
|
|
|
|
); |
254
|
0
|
|
|
|
|
0
|
}); |
255
|
0
|
|
|
|
|
0
|
return $saved_mysqld; |
256
|
14
|
|
|
|
|
294
|
}); |
257
|
|
|
|
|
|
|
} |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
sub _build_dsn{ |
260
|
7
|
|
|
7
|
|
24
|
my ($self) = @_; |
261
|
7
|
50
|
|
|
|
225
|
if( $$ != $self->_instance_pid() ){ |
262
|
0
|
|
|
|
|
0
|
confess("Do not build the dsn in a subprocess of this instance creator"); |
263
|
|
|
|
|
|
|
} |
264
|
|
|
|
|
|
|
|
265
|
7
|
|
|
|
|
237
|
my $dsn = $self->_shared_mysqld()->{dsn}; |
266
|
|
|
|
|
|
|
return $self->_with_shared_dbh( $dsn, sub{ |
267
|
0
|
|
|
0
|
|
0
|
my $dbh = shift; |
268
|
0
|
|
|
|
|
0
|
my $temp_db_name = $self->_temp_db_name(); |
269
|
0
|
|
|
|
|
0
|
$log->info("PID $$ creating temporary database '$temp_db_name' on $dsn"); |
270
|
0
|
|
|
|
|
0
|
$dbh->do('CREATE DATABASE '.$temp_db_name); |
271
|
0
|
|
|
|
|
0
|
$dsn =~ s/dbname=([^;])+/dbname=$temp_db_name/; |
272
|
0
|
|
|
|
|
0
|
$log->info("PID $$ local dsn is '$dsn'"); |
273
|
0
|
|
|
|
|
0
|
return $dsn; |
274
|
0
|
|
|
|
|
0
|
}); |
275
|
|
|
|
|
|
|
} |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
sub _teardown{ |
279
|
0
|
|
|
0
|
|
0
|
my ($self) = @_; |
280
|
0
|
|
|
|
|
0
|
my $dsn = $self->_shared_mysqld()->{dsn}; |
281
|
|
|
|
|
|
|
$self->_with_shared_dbh( $dsn, |
282
|
|
|
|
|
|
|
sub{ |
283
|
0
|
|
|
0
|
|
0
|
my $dbh = shift; |
284
|
0
|
|
|
|
|
0
|
$dbh->do('DELETE FROM pid_registry WHERE pid = ? AND instance = ? ',{}, $self->_instance_pid() , ( $self + 0 ) ); |
285
|
0
|
|
|
|
|
0
|
my ( $count_row ) = $dbh->selectrow_array('SELECT COUNT(*) FROM pid_registry'); |
286
|
0
|
0
|
|
|
|
0
|
if( $count_row ){ |
287
|
0
|
|
|
|
|
0
|
$log->info("PID $$ Some PIDs,Instances are still registered as using this DB. Not tearing down"); |
288
|
0
|
|
|
|
|
0
|
return; |
289
|
|
|
|
|
|
|
} |
290
|
0
|
|
|
|
|
0
|
$log->info("PID $$ no pids anymore in the DB. Tearing down"); |
291
|
0
|
|
|
|
|
0
|
$log->info("PID $$ unlinking ".$self->_mysqld_file()); |
292
|
0
|
|
|
|
|
0
|
unlink $self->_mysqld_file(); |
293
|
0
|
|
|
|
|
0
|
Test::More::note("PID $$ terminating mysqld instance (sending SIGTERM to ".$self->pid().")"); |
294
|
0
|
|
|
|
|
0
|
$log->info("PID $$ terminating mysqld instance (sending SIGTERM to ".$self->pid().")"); |
295
|
0
|
|
|
|
|
0
|
kill SIGTERM, $self->pid(); |
296
|
0
|
|
|
|
|
0
|
}); |
297
|
|
|
|
|
|
|
} |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
sub DEMOLISH{ |
300
|
7
|
|
|
7
|
0
|
31
|
my ($self) = @_; |
301
|
7
|
50
|
|
|
|
218
|
if( $$ != $self->_instance_pid() ){ |
302
|
|
|
|
|
|
|
# Do NOT let subprocesses that forked |
303
|
|
|
|
|
|
|
# after the creation of this to have an impact. |
304
|
0
|
|
|
|
|
0
|
return; |
305
|
|
|
|
|
|
|
} |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
$self->_monitor(sub{ |
308
|
|
|
|
|
|
|
# We always want to drop the local process database. |
309
|
7
|
|
|
7
|
|
362
|
my $dsn = $self->_shared_mysqld()->{dsn}; |
310
|
0
|
|
|
|
|
0
|
$log->info("PID $$ Will drop database on dsn = $dsn"); |
311
|
|
|
|
|
|
|
$self->_with_shared_dbh($dsn, sub{ |
312
|
0
|
|
|
|
|
0
|
my $dbh = shift; |
313
|
0
|
|
|
|
|
0
|
my $temp_db_name = $self->_temp_db_name(); |
314
|
0
|
|
|
|
|
0
|
$log->info("PID $$ dropping temporary database $temp_db_name"); |
315
|
0
|
|
|
|
|
0
|
$dbh->do("DROP DATABASE ".$temp_db_name); |
316
|
0
|
|
|
|
|
0
|
}); |
317
|
0
|
|
|
|
|
0
|
$self->_teardown(); |
318
|
7
|
|
|
|
|
98
|
}); |
319
|
|
|
|
|
|
|
|
320
|
0
|
0
|
|
|
|
0
|
if( my $test_mysqld = $self->_holds_mysqld() ){ |
321
|
|
|
|
|
|
|
# This is the mysqld holder process. Need to wait for it |
322
|
|
|
|
|
|
|
# before exiting |
323
|
0
|
|
|
|
|
0
|
Test::More::note("PID $$ mysqld holder process waiting for mysqld termination"); |
324
|
0
|
|
|
|
|
0
|
$log->info("PID $$ mysqld holder process waiting for mysqld termination"); |
325
|
0
|
|
|
|
|
0
|
while( waitpid( $self->pid() , 0 ) <= 0 ){ |
326
|
0
|
|
|
|
|
0
|
$log->info("PID $$ db pid = ".$self->pid()." not down yet. Waiting 2 seconds"); |
327
|
0
|
|
|
|
|
0
|
sleep(2); |
328
|
|
|
|
|
|
|
} |
329
|
0
|
|
|
|
|
0
|
my $pid_file = $self->_shared_mysqld()->{pid_file}; |
330
|
0
|
|
|
|
|
0
|
$log->trace("PID $$ unlinking mysql pidfile $pid_file. Just in case"); |
331
|
0
|
|
|
|
|
0
|
unlink( $pid_file ); |
332
|
0
|
|
|
|
|
0
|
$log->info("PID $$ Ok, mysqld is gone"); |
333
|
|
|
|
|
|
|
} |
334
|
|
|
|
|
|
|
} |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
=head1 dsn |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
Returns the dsn to connect to the test database. Note that the user is root and the password |
339
|
|
|
|
|
|
|
is the empty string. |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
=cut |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
=head2 pid |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
See L<Test::mysqld> |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
=cut |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
sub pid{ |
350
|
0
|
|
|
0
|
1
|
0
|
my ($self) = @_; |
351
|
0
|
|
|
|
|
0
|
return $self->_shared_mysqld()->{pid}; |
352
|
|
|
|
|
|
|
} |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
my $in_monitor = {}; |
355
|
|
|
|
|
|
|
sub _monitor{ |
356
|
23
|
|
|
23
|
|
86
|
my ($self, $sub) = @_; |
357
|
|
|
|
|
|
|
|
358
|
23
|
100
|
|
|
|
105
|
if( $in_monitor->{$self} ){ |
359
|
8
|
|
|
|
|
129
|
$log->warn("PID $$ Re-entrant monitor. Will execute sub without locking for deadlock protection"); |
360
|
8
|
|
|
|
|
51
|
return $sub->(); |
361
|
|
|
|
|
|
|
} |
362
|
15
|
|
|
|
|
548
|
$log->trace("PID $$ locking file ".$self->_lock_file()); |
363
|
15
|
|
|
|
|
114
|
$in_monitor->{$self} = 1; |
364
|
15
|
|
|
|
|
382
|
my $lock = File::Flock::Tiny->lock( $self->_lock_file() ); |
365
|
15
|
|
|
|
|
114019
|
my $res = eval{ $sub->(); }; |
|
15
|
|
|
|
|
66
|
|
366
|
15
|
|
|
|
|
136903
|
my $err = $@; |
367
|
15
|
|
|
|
|
134
|
delete $in_monitor->{$self}; |
368
|
15
|
100
|
|
|
|
125
|
if( $err ){ |
369
|
14
|
|
|
|
|
1585
|
confess($err); |
370
|
|
|
|
|
|
|
} |
371
|
1
|
|
|
|
|
6
|
return $res; |
372
|
|
|
|
|
|
|
} |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
sub _with_shared_dbh{ |
375
|
0
|
|
|
0
|
|
|
my ($self, $dsn, $code) = @_; |
376
|
0
|
|
|
|
|
|
my $dbh = DBI->connect_cached( $dsn, 'root', '' , { RaiseError => 1 }); |
377
|
0
|
|
|
|
|
|
return $code->($dbh); |
378
|
|
|
|
|
|
|
} |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
__PACKAGE__->meta->make_immutable(); |