| 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(); |