line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package File::Rsync::Mirror::Recentfile; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# use warnings; |
4
|
8
|
|
|
8
|
|
84049
|
use strict; |
|
8
|
|
|
|
|
34
|
|
|
8
|
|
|
|
|
744
|
|
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
=encoding utf-8 |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
=head1 NAME |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
File::Rsync::Mirror::Recentfile - mirroring via rsync made efficient |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
=cut |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
my $HAVE = {}; |
15
|
|
|
|
|
|
|
for my $package ( |
16
|
|
|
|
|
|
|
"Data::Serializer", |
17
|
|
|
|
|
|
|
"File::Rsync" |
18
|
|
|
|
|
|
|
) { |
19
|
|
|
|
|
|
|
$HAVE->{$package} = eval qq{ require $package; }; |
20
|
|
|
|
|
|
|
} |
21
|
8
|
|
|
8
|
|
58
|
use Config; |
|
8
|
|
|
|
|
15
|
|
|
8
|
|
|
|
|
307
|
|
22
|
8
|
|
|
8
|
|
43
|
use File::Basename qw(basename dirname fileparse); |
|
8
|
|
|
|
|
11
|
|
|
8
|
|
|
|
|
657
|
|
23
|
8
|
|
|
8
|
|
1144
|
use File::Copy qw(cp); |
|
8
|
|
|
|
|
9272
|
|
|
8
|
|
|
|
|
423
|
|
24
|
8
|
|
|
8
|
|
51
|
use File::Path qw(mkpath); |
|
8
|
|
|
|
|
17
|
|
|
8
|
|
|
|
|
399
|
|
25
|
8
|
|
|
8
|
|
3444
|
use File::Rsync::Mirror::Recentfile::FakeBigFloat qw(:all); |
|
8
|
|
|
|
|
18
|
|
|
8
|
|
|
|
|
1423
|
|
26
|
8
|
|
|
8
|
|
6289
|
use File::Temp; |
|
8
|
|
|
|
|
151333
|
|
|
8
|
|
|
|
|
674
|
|
27
|
8
|
|
|
8
|
|
71
|
use List::Util qw(first max min); |
|
8
|
|
|
|
|
21
|
|
|
8
|
|
|
|
|
944
|
|
28
|
8
|
|
|
8
|
|
60
|
use Scalar::Util qw(blessed reftype); |
|
8
|
|
|
|
|
21
|
|
|
8
|
|
|
|
|
388
|
|
29
|
8
|
|
|
8
|
|
5048
|
use Storable; |
|
8
|
|
|
|
|
26499
|
|
|
8
|
|
|
|
|
457
|
|
30
|
8
|
|
|
8
|
|
4329
|
use Time::HiRes qw(); |
|
8
|
|
|
|
|
11095
|
|
|
8
|
|
|
|
|
215
|
|
31
|
8
|
|
|
8
|
|
3727
|
use YAML::Syck; |
|
8
|
|
|
|
|
15070
|
|
|
8
|
|
|
|
|
500
|
|
32
|
|
|
|
|
|
|
|
33
|
8
|
|
|
8
|
|
69
|
use version; our $VERSION = qv('0.0.9'); |
|
8
|
|
|
|
|
15
|
|
|
8
|
|
|
|
|
48
|
|
34
|
|
|
|
|
|
|
|
35
|
8
|
|
|
8
|
|
969
|
use constant MAX_INT => ~0>>1; # anything better? |
|
8
|
|
|
|
|
23
|
|
|
8
|
|
|
|
|
577
|
|
36
|
8
|
|
|
8
|
|
58
|
use constant DEFAULT_PROTOCOL => 1; |
|
8
|
|
|
|
|
56
|
|
|
8
|
|
|
|
|
6877
|
|
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
# cf. interval_secs |
39
|
|
|
|
|
|
|
my %seconds; |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
# maybe subclass if this mapping is bad? |
42
|
|
|
|
|
|
|
my %serializers; |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
=head1 SYNOPSIS |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
Writer (of a single file): |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
use File::Rsync::Mirror::Recentfile; |
49
|
|
|
|
|
|
|
my $fr = File::Rsync::Mirror::Recentfile->new |
50
|
|
|
|
|
|
|
( |
51
|
|
|
|
|
|
|
interval => q(6h), |
52
|
|
|
|
|
|
|
filenameroot => "RECENT", |
53
|
|
|
|
|
|
|
comment => "These 'RECENT' files are part of a test of a new CPAN mirroring concept. Please ignore them for now.", |
54
|
|
|
|
|
|
|
localroot => "/home/ftp/pub/PAUSE/authors/", |
55
|
|
|
|
|
|
|
aggregator => [qw(1d 1W 1M 1Q 1Y Z)], |
56
|
|
|
|
|
|
|
); |
57
|
|
|
|
|
|
|
$rf->update("/home/ftp/pub/PAUSE/authors/id/A/AN/ANDK/CPAN-1.92_63.tar.gz","new"); |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
Reader/mirrorer: |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
my $rf = File::Rsync::Mirror::Recentfile->new |
62
|
|
|
|
|
|
|
( |
63
|
|
|
|
|
|
|
filenameroot => "RECENT", |
64
|
|
|
|
|
|
|
interval => q(6h), |
65
|
|
|
|
|
|
|
localroot => "/home/ftp/pub/PAUSE/authors", |
66
|
|
|
|
|
|
|
remote_dir => "", |
67
|
|
|
|
|
|
|
remote_host => "pause.perl.org", |
68
|
|
|
|
|
|
|
remote_module => "authors", |
69
|
|
|
|
|
|
|
rsync_options => { |
70
|
|
|
|
|
|
|
compress => 1, |
71
|
|
|
|
|
|
|
'rsync-path' => '/usr/bin/rsync', |
72
|
|
|
|
|
|
|
links => 1, |
73
|
|
|
|
|
|
|
times => 1, |
74
|
|
|
|
|
|
|
'omit-dir-times' => 1, |
75
|
|
|
|
|
|
|
checksum => 1, |
76
|
|
|
|
|
|
|
}, |
77
|
|
|
|
|
|
|
verbose => 1, |
78
|
|
|
|
|
|
|
); |
79
|
|
|
|
|
|
|
$rf->mirror; |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
Aggregator (usually the writer): |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
my $rf = File::Rsync::Mirror::Recentfile->new_from_file ( $file ); |
84
|
|
|
|
|
|
|
$rf->aggregate; |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
=head1 DESCRIPTION |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
Lower level than F:R:M:Recent, handles one recentfile. Whereas a tree |
89
|
|
|
|
|
|
|
is always composed of several recentfiles, controlled by the |
90
|
|
|
|
|
|
|
F:R:M:Recent object. The Recentfile object has to do the bookkeeping |
91
|
|
|
|
|
|
|
for a single timeslice. |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
=head1 EXPORT |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
No exports. |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
=head1 CONSTRUCTORS / DESTRUCTOR |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
=head2 my $obj = CLASS->new(%hash) |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
Constructor. On every argument pair the key is a method name and the |
102
|
|
|
|
|
|
|
value is an argument to that method name. |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
If a recentfile for this resource already exists, metadata that are |
105
|
|
|
|
|
|
|
not defined by the constructor will be fetched from there as soon as |
106
|
|
|
|
|
|
|
it is being read by recent_events(). |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
=cut |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
sub new { |
111
|
542
|
|
|
542
|
1
|
550396
|
my($class, @args) = @_; |
112
|
542
|
|
|
|
|
2796
|
my $self = bless {}, $class; |
113
|
542
|
|
|
|
|
3482
|
while (@args) { |
114
|
1700
|
|
|
|
|
8160
|
my($method,$arg) = splice @args, 0, 2; |
115
|
1700
|
|
|
|
|
9780
|
$self->$method($arg); |
116
|
|
|
|
|
|
|
} |
117
|
542
|
50
|
|
|
|
5515
|
unless (defined $self->protocol) { |
118
|
542
|
|
|
|
|
4212
|
$self->protocol(DEFAULT_PROTOCOL); |
119
|
|
|
|
|
|
|
} |
120
|
542
|
100
|
|
|
|
4083
|
unless (defined $self->filenameroot) { |
121
|
517
|
|
|
|
|
3751
|
$self->filenameroot("RECENT"); |
122
|
|
|
|
|
|
|
} |
123
|
542
|
100
|
|
|
|
3682
|
unless (defined $self->serializer_suffix) { |
124
|
522
|
|
|
|
|
4506
|
$self->serializer_suffix(".yaml"); |
125
|
|
|
|
|
|
|
} |
126
|
542
|
|
|
|
|
4234
|
return $self; |
127
|
|
|
|
|
|
|
} |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
=head2 my $obj = CLASS->new_from_file($file) |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
Constructor. $file is a I. |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
=cut |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
sub new_from_file { |
136
|
1109
|
|
|
1109
|
1
|
455595
|
my($class, $file) = @_; |
137
|
1109
|
|
|
|
|
3711
|
my $self = bless {}, $class; |
138
|
1109
|
|
|
|
|
4848
|
$self->_rfile($file); |
139
|
|
|
|
|
|
|
#?# $self->lock; |
140
|
1109
|
50
|
|
|
|
6191
|
my $serialized = do { open my $fh, $file or die "Could not open '$file': $!"; |
|
1109
|
|
|
|
|
54394
|
|
141
|
1109
|
|
|
|
|
8690
|
local $/; |
142
|
1109
|
|
|
|
|
58756
|
<$fh>; |
143
|
|
|
|
|
|
|
}; |
144
|
|
|
|
|
|
|
# XXX: we can skip this step when the metadata are sufficient, but |
145
|
|
|
|
|
|
|
# we cannot parse the file without some magic stuff about |
146
|
|
|
|
|
|
|
# serialized formats |
147
|
1109
|
|
|
|
|
20607
|
while (-l $file) { |
148
|
31
|
|
|
|
|
1507
|
my($name,$path) = fileparse $file; |
149
|
31
|
|
|
|
|
451
|
my $symlink = readlink $file; |
150
|
31
|
50
|
|
|
|
173
|
if ($symlink =~ m|/|) { |
151
|
0
|
|
|
|
|
0
|
die "FIXME: filenames containing '/' not supported, got $symlink"; |
152
|
|
|
|
|
|
|
} |
153
|
31
|
|
|
|
|
1014
|
$file = File::Spec->catfile ( $path, $symlink ); |
154
|
|
|
|
|
|
|
} |
155
|
1109
|
|
|
|
|
113646
|
my($name,$path,$suffix) = fileparse $file, keys %serializers; |
156
|
1109
|
|
|
|
|
8407
|
$self->serializer_suffix($suffix); |
157
|
1109
|
|
|
|
|
9037
|
$self->localroot($path); |
158
|
1109
|
50
|
|
|
|
7067
|
die "Could not determine file format from suffix" unless $suffix; |
159
|
1109
|
|
|
|
|
2158
|
my $deserialized; |
160
|
1109
|
50
|
|
|
|
3770
|
if ($suffix eq ".yaml") { |
|
|
0
|
|
|
|
|
|
161
|
1109
|
|
|
|
|
7810
|
require YAML::Syck; |
162
|
1109
|
|
|
|
|
5581
|
$deserialized = YAML::Syck::LoadFile($file); |
163
|
|
|
|
|
|
|
} elsif ($HAVE->{"Data::Serializer"}) { |
164
|
|
|
|
|
|
|
my $serializer = Data::Serializer->new |
165
|
0
|
|
|
|
|
0
|
( serializer => $serializers{$suffix} ); |
166
|
0
|
|
|
|
|
0
|
$deserialized = $serializer->raw_deserialize($serialized); |
167
|
|
|
|
|
|
|
} else { |
168
|
0
|
|
|
|
|
0
|
die "Data::Serializer not installed, cannot proceed with suffix '$suffix'"; |
169
|
|
|
|
|
|
|
} |
170
|
1109
|
|
|
|
|
916142
|
while (my($k,$v) = each %{$deserialized->{meta}}) { |
|
13119
|
|
|
|
|
68223
|
|
171
|
12010
|
100
|
|
|
|
24665
|
next if $k ne lc $k; # "Producers" |
172
|
10901
|
|
|
|
|
27808
|
$self->$k($v); |
173
|
|
|
|
|
|
|
} |
174
|
1109
|
50
|
|
|
|
3334
|
unless (defined $self->protocol) { |
175
|
0
|
|
|
|
|
0
|
$self->protocol(DEFAULT_PROTOCOL); |
176
|
|
|
|
|
|
|
} |
177
|
1109
|
|
|
|
|
33306
|
return $self; |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
=head2 DESTROY |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
A simple unlock. |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
=cut |
185
|
|
|
|
|
|
|
sub DESTROY { |
186
|
5198
|
|
|
5198
|
|
100779517
|
my $self = shift; |
187
|
5198
|
|
|
|
|
16346
|
$self->unlock; |
188
|
5198
|
100
|
|
|
|
34110
|
unless ($self->_current_tempfile_fh) { |
189
|
5194
|
100
|
|
|
|
23896
|
if (my $tempfile = $self->_current_tempfile) { |
190
|
119
|
100
|
|
|
|
16929
|
if (-e $tempfile) { |
191
|
|
|
|
|
|
|
# unlink $tempfile; # may fail in global destruction |
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
} |
194
|
|
|
|
|
|
|
} |
195
|
|
|
|
|
|
|
} |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
=head1 ACCESSORS |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
=cut |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
my @accessors; |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
BEGIN { |
204
|
8
|
|
|
8
|
|
75
|
@accessors = ( |
205
|
|
|
|
|
|
|
"_current_tempfile", |
206
|
|
|
|
|
|
|
"_current_tempfile_fh", |
207
|
|
|
|
|
|
|
"_delayed_operations", |
208
|
|
|
|
|
|
|
"_done", |
209
|
|
|
|
|
|
|
"_interval", |
210
|
|
|
|
|
|
|
"_is_locked", |
211
|
|
|
|
|
|
|
"_localroot", |
212
|
|
|
|
|
|
|
"_merged", |
213
|
|
|
|
|
|
|
"_pathdb", |
214
|
|
|
|
|
|
|
"_remember_last_uptodate_call", |
215
|
|
|
|
|
|
|
"_remote_dir", |
216
|
|
|
|
|
|
|
"_remoteroot", |
217
|
|
|
|
|
|
|
"_requires_fsck", |
218
|
|
|
|
|
|
|
"_rfile", |
219
|
|
|
|
|
|
|
"_rsync", |
220
|
|
|
|
|
|
|
"__verified_tempdir", |
221
|
|
|
|
|
|
|
"_seeded", |
222
|
|
|
|
|
|
|
"_uptodateness_ever_reached", |
223
|
|
|
|
|
|
|
"_use_tempfile", |
224
|
|
|
|
|
|
|
); |
225
|
|
|
|
|
|
|
|
226
|
8
|
|
|
|
|
602
|
my @pod_lines = |
227
|
8
|
|
|
|
|
33
|
split /\n/, <<'=cut'; push @accessors, grep {s/^=item\s+//} @pod_lines; } |
|
1152
|
|
|
|
|
2287
|
|
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
=over 4 |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
=item aggregator |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
A list of interval specs that tell the aggregator which Is |
234
|
|
|
|
|
|
|
are to be produced. |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
=item canonize |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
The name of a method to canonize the path before rsyncing. Only |
239
|
|
|
|
|
|
|
supported value is C. Defaults to that. |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
=item comment |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
A comment about this tree and setup. |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
=item dirtymark |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
A timestamp. The dirtymark is updated whenever an out of band change |
248
|
|
|
|
|
|
|
on the origin server is performed that violates the protocol. Say, |
249
|
|
|
|
|
|
|
they add or remove files in the middle somewhere. Slaves must react |
250
|
|
|
|
|
|
|
with a devaluation of their C structure which then leads to a |
251
|
|
|
|
|
|
|
full re-sync of all files. Implementation note: dirtymark may increase |
252
|
|
|
|
|
|
|
or decrease. |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
=item filenameroot |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
The (prefix of the) filename we use for this I. Defaults to |
257
|
|
|
|
|
|
|
C. The string must not contain a directory separator. |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
=item have_mirrored |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
Timestamp remembering when we mirrored this recentfile the last time. |
262
|
|
|
|
|
|
|
Only relevant for slaves. |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
=item ignore_link_stat_errors |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
If set to true, rsync errors are ignored that complain about link stat |
267
|
|
|
|
|
|
|
errors. These seem to happen only when there are files missing at the |
268
|
|
|
|
|
|
|
origin. In race conditions this can always happen, so it defaults to |
269
|
|
|
|
|
|
|
true. |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
=item is_slave |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
If set to true, this object will fetch a new recentfile from remote |
274
|
|
|
|
|
|
|
when the timespan between the last mirror (see have_mirrored) and now |
275
|
|
|
|
|
|
|
is too large (see C). |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
=item keep_delete_objects_forever |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
The default for delete events is that they are passed through the |
280
|
|
|
|
|
|
|
collection of recentfile objects until they reach the Z file. There |
281
|
|
|
|
|
|
|
they get dropped so that the associated file object ceases to exist at |
282
|
|
|
|
|
|
|
all. By setting C the delete objects are |
283
|
|
|
|
|
|
|
kept forever. This makes the Z file larger but has the advantage that |
284
|
|
|
|
|
|
|
slaves that have interrupted mirroring for a long time still can clean |
285
|
|
|
|
|
|
|
up their copy. |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
=item locktimeout |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
After how many seconds shall we die if we cannot lock a I? |
290
|
|
|
|
|
|
|
Defaults to 600 seconds. |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
=item loopinterval |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
When mirror_loop is called, this accessor can specify how much time |
295
|
|
|
|
|
|
|
every loop shall at least take. If the work of a loop is done before |
296
|
|
|
|
|
|
|
that time has gone, sleeps for the rest of the time. Defaults to |
297
|
|
|
|
|
|
|
arbitrary 42 seconds. |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
=item max_files_per_connection |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
Maximum number of files that are transferred on a single rsync call. |
302
|
|
|
|
|
|
|
Setting it higher means higher performance at the price of holding |
303
|
|
|
|
|
|
|
connections longer and potentially disturbing other users in the pool. |
304
|
|
|
|
|
|
|
Defaults to the arbitrary value 42. |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
=item max_rsync_errors |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
When rsync operations encounter that many errors without any resetting |
309
|
|
|
|
|
|
|
success in between, then we die. Defaults to unlimited. A value of |
310
|
|
|
|
|
|
|
-1 means we run forever ignoring all rsync errors. |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
=item minmax |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
Hashref remembering when we read the recent_events from this file the |
315
|
|
|
|
|
|
|
last time and what the timespan was. |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
=item protocol |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
When the RECENT file format changes, we increment the protocol. We try |
320
|
|
|
|
|
|
|
to support older protocols in later releases. |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
=item remote_host |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
The host we are mirroring from. Leave empty for the local filesystem. |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
=item remote_module |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
Rsync servers have so called modules to separate directory trees from |
329
|
|
|
|
|
|
|
each other. Put here the name of the module under which we are |
330
|
|
|
|
|
|
|
mirroring. Leave empty for local filesystem. |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
=item rsync_options |
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
Things like compress, links, times or checksums. Passed in to the |
335
|
|
|
|
|
|
|
File::Rsync object used to run the mirror. |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
=item serializer_suffix |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
Mostly untested accessor. The only well tested format for |
340
|
|
|
|
|
|
|
Is at the moment is YAML. It is used with YAML::Syck via |
341
|
|
|
|
|
|
|
Data::Serializer. But in principle other formats are supported as |
342
|
|
|
|
|
|
|
well. See section SERIALIZERS below. |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
=item sleep_per_connection |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
Sleep that many seconds (floating point OK) after every chunk of rsyncing |
347
|
|
|
|
|
|
|
has finished. Defaults to arbitrary 0.42. |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
=item tempdir |
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
Directory to write temporary files to. Must allow rename operations |
352
|
|
|
|
|
|
|
into the tree which usually means it must live on the same partition |
353
|
|
|
|
|
|
|
as the target directory. Defaults to C<< $self->localroot >>. |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
=item ttl |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
Time to live. Number of seconds after which this recentfile must be |
358
|
|
|
|
|
|
|
fetched again from the origin server. Only relevant for slaves. |
359
|
|
|
|
|
|
|
Defaults to arbitrary 24.2 seconds. |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
=item verbose |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
Boolean to turn on a bit verbosity. |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
=item verboselog |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
Path to the logfile to write verbose progress information to. This is |
368
|
|
|
|
|
|
|
a primitive stop gap solution to get simple verbose logging working. |
369
|
|
|
|
|
|
|
Switching to Log4perl or similar is probably the way to go. |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
=back |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
=cut |
374
|
|
|
|
|
|
|
|
375
|
8
|
|
|
8
|
|
3674
|
use accessors @accessors; |
|
8
|
|
|
|
|
7540
|
|
|
8
|
|
|
|
|
44
|
|
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
=head1 METHODS |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
=head2 (void) $obj->aggregate( %options ) |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
Takes all intervals that are collected in the accessor called |
382
|
|
|
|
|
|
|
aggregator. Sorts them by actual length of the interval. |
383
|
|
|
|
|
|
|
Removes those that are shorter than our own interval. Then merges this |
384
|
|
|
|
|
|
|
object into the next larger object. The merging continues upwards |
385
|
|
|
|
|
|
|
as long as the next I is old enough to warrant a merge. |
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
If a merge is warranted is decided according to the interval of the |
388
|
|
|
|
|
|
|
previous interval so that larger files are not so often updated as |
389
|
|
|
|
|
|
|
smaller ones. If $options{force} is true, all files get updated. |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
Here is an example to illustrate the behaviour. Given aggregators |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
1h 1d 1W 1M 1Q 1Y Z |
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
then |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
1h updates 1d on every call to aggregate() |
398
|
|
|
|
|
|
|
1d updates 1W earliest after 1h |
399
|
|
|
|
|
|
|
1W updates 1M earliest after 1d |
400
|
|
|
|
|
|
|
1M updates 1Q earliest after 1W |
401
|
|
|
|
|
|
|
1Q updates 1Y earliest after 1M |
402
|
|
|
|
|
|
|
1Y updates Z earliest after 1Q |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
Note that all but the smallest recentfile get updated at an arbitrary |
405
|
|
|
|
|
|
|
rate and as such are quite useless on their own. |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
=cut |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
sub aggregate { |
410
|
354
|
|
|
354
|
1
|
18088338
|
my($self, %option) = @_; |
411
|
354
|
|
|
|
|
1162
|
my %seen_interval; |
412
|
2932
|
|
|
|
|
5235
|
my @aggs = sort { $a->{secs} <=> $b->{secs} } |
413
|
1870
|
50
|
|
|
|
8093
|
grep { !$seen_interval{$_->{interval}}++ && $_->{secs} >= $self->interval_secs } |
414
|
1870
|
|
|
|
|
5970
|
map { { interval => $_, secs => $self->interval_secs($_)} } |
415
|
354
|
50
|
|
|
|
1401
|
$self->interval, @{$self->aggregator || []}; |
|
354
|
|
|
|
|
1480
|
|
416
|
354
|
|
|
|
|
1330
|
$self->update; |
417
|
354
|
|
|
|
|
10770
|
$aggs[0]{object} = $self; |
418
|
354
|
|
|
|
|
1574
|
AGGREGATOR: for my $i (0..$#aggs-1) { |
419
|
986
|
|
|
|
|
2055
|
my $this = $aggs[$i]{object}; |
420
|
986
|
|
|
|
|
2878
|
my $next = $this->_sparse_clone; |
421
|
986
|
|
|
|
|
4016
|
$next->interval($aggs[$i+1]{interval}); |
422
|
986
|
|
|
|
|
1633
|
my $want_merge = 0; |
423
|
986
|
100
|
100
|
|
|
4632
|
if ($option{force} || $i == 0) { |
424
|
606
|
|
|
|
|
991
|
$want_merge = 1; |
425
|
|
|
|
|
|
|
} else { |
426
|
380
|
|
|
|
|
1325
|
my $next_rfile = $next->rfile; |
427
|
380
|
100
|
|
|
|
8055
|
if (-e $next_rfile) { |
428
|
320
|
|
|
|
|
1650
|
my $prev = $aggs[$i-1]{object}; |
429
|
320
|
|
|
|
|
2305
|
local $^T = time; |
430
|
320
|
|
|
|
|
4310
|
my $next_age = 86400 * -M $next_rfile; |
431
|
320
|
100
|
|
|
|
1530
|
if ($next_age > $prev->interval_secs) { |
432
|
55
|
|
|
|
|
195
|
$want_merge = 1; |
433
|
|
|
|
|
|
|
} |
434
|
|
|
|
|
|
|
} else { |
435
|
60
|
|
|
|
|
175
|
$want_merge = 1; |
436
|
|
|
|
|
|
|
} |
437
|
|
|
|
|
|
|
} |
438
|
986
|
100
|
|
|
|
2407
|
if ($want_merge) { |
439
|
721
|
|
|
|
|
2959
|
$next->merge($this); |
440
|
721
|
|
|
|
|
20279
|
$aggs[$i+1]{object} = $next; |
441
|
|
|
|
|
|
|
} else { |
442
|
265
|
|
|
|
|
1515
|
last AGGREGATOR; |
443
|
|
|
|
|
|
|
} |
444
|
|
|
|
|
|
|
} |
445
|
|
|
|
|
|
|
} |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
# collect file size and mtime for all files of this aggregate |
448
|
|
|
|
|
|
|
sub _debug_aggregate { |
449
|
30
|
|
|
30
|
|
25530
|
my($self) = @_; |
450
|
270
|
|
|
|
|
430
|
my @aggs = sort { $a->{secs} <=> $b->{secs} } |
451
|
180
|
|
|
|
|
685
|
map { { interval => $_, secs => $self->interval_secs($_)} } |
452
|
30
|
50
|
|
|
|
120
|
$self->interval, @{$self->aggregator || []}; |
|
30
|
|
|
|
|
95
|
|
453
|
30
|
|
|
|
|
85
|
my $report = []; |
454
|
30
|
|
|
|
|
115
|
for my $i (0..$#aggs) { |
455
|
180
|
|
|
|
|
10945
|
my $this = Storable::dclone $self; |
456
|
180
|
|
|
|
|
705
|
$this->interval($aggs[$i]{interval}); |
457
|
180
|
|
|
|
|
345
|
my $rfile = $this->rfile; |
458
|
180
|
|
|
|
|
3195
|
my @stat = stat $rfile; |
459
|
180
|
|
|
|
|
1410
|
push @$report, {rfile => $rfile, size => $stat[7], mtime => $stat[9]}; |
460
|
|
|
|
|
|
|
} |
461
|
30
|
|
|
|
|
425
|
$report; |
462
|
|
|
|
|
|
|
} |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
# (void) $self->_assert_symlink() |
465
|
|
|
|
|
|
|
sub _assert_symlink { |
466
|
1646
|
|
|
1646
|
|
3809
|
my($self) = @_; |
467
|
1646
|
|
|
|
|
4023
|
my $recentrecentfile = File::Spec->catfile |
468
|
|
|
|
|
|
|
( |
469
|
|
|
|
|
|
|
$self->localroot, |
470
|
|
|
|
|
|
|
sprintf |
471
|
|
|
|
|
|
|
( |
472
|
|
|
|
|
|
|
"%s.recent", |
473
|
|
|
|
|
|
|
$self->filenameroot |
474
|
|
|
|
|
|
|
) |
475
|
|
|
|
|
|
|
); |
476
|
1646
|
50
|
|
|
|
57766
|
if ($Config{d_symlink} eq "define") { |
477
|
1646
|
|
|
|
|
3413
|
my $howto_create_symlink; # 0=no need; 1=straight symlink; 2=rename symlink |
478
|
1646
|
100
|
|
|
|
31890
|
if (-l $recentrecentfile) { |
479
|
1625
|
|
|
|
|
18584
|
my $found_symlink = readlink $recentrecentfile; |
480
|
1625
|
100
|
|
|
|
6553
|
if ($found_symlink eq $self->rfilename) { |
481
|
1610
|
|
|
|
|
3675
|
return; |
482
|
|
|
|
|
|
|
} else { |
483
|
15
|
|
|
|
|
35
|
$howto_create_symlink = 2; |
484
|
|
|
|
|
|
|
} |
485
|
|
|
|
|
|
|
} else { |
486
|
21
|
|
|
|
|
63
|
$howto_create_symlink = 1; |
487
|
|
|
|
|
|
|
} |
488
|
36
|
100
|
|
|
|
100
|
if (1 == $howto_create_symlink) { |
489
|
21
|
50
|
|
|
|
54
|
symlink $self->rfilename, $recentrecentfile or die "Could not create symlink '$recentrecentfile': $!" |
490
|
|
|
|
|
|
|
} else { |
491
|
15
|
|
|
|
|
270
|
unlink "$recentrecentfile.$$"; # may fail |
492
|
15
|
50
|
|
|
|
60
|
symlink $self->rfilename, "$recentrecentfile.$$" or die "Could not create symlink '$recentrecentfile.$$': $!"; |
493
|
15
|
50
|
|
|
|
620
|
rename "$recentrecentfile.$$", $recentrecentfile or die "Could not rename '$recentrecentfile.$$' to $recentrecentfile: $!"; |
494
|
|
|
|
|
|
|
} |
495
|
|
|
|
|
|
|
} else { |
496
|
0
|
|
|
|
|
0
|
warn "Warning: symlinks not supported on this system, doing a copy instead\n"; |
497
|
0
|
|
|
|
|
0
|
unlink "$recentrecentfile.$$"; # may fail |
498
|
0
|
0
|
|
|
|
0
|
cp $self->rfilename, "$recentrecentfile.$$" or die "Could not copy to '$recentrecentfile.$$': $!"; |
499
|
0
|
0
|
|
|
|
0
|
rename "$recentrecentfile.$$", $recentrecentfile or die "Could not rename '$recentrecentfile.$$' to $recentrecentfile: $!"; |
500
|
|
|
|
|
|
|
} |
501
|
|
|
|
|
|
|
} |
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
=head2 $hashref = $obj->delayed_operations |
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
A hash of hashes containing unlink and rmdir operations which had to |
506
|
|
|
|
|
|
|
wait until the recentfile got unhidden in order to not confuse |
507
|
|
|
|
|
|
|
downstream mirrors (in case we have some). |
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
=cut |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
sub delayed_operations { |
512
|
41
|
|
|
41
|
1
|
219
|
my($self) = @_; |
513
|
41
|
|
|
|
|
558
|
my $x = $self->_delayed_operations; |
514
|
41
|
100
|
|
|
|
599
|
unless (defined $x) { |
515
|
15
|
|
|
|
|
360
|
$x = { |
516
|
|
|
|
|
|
|
unlink => {}, |
517
|
|
|
|
|
|
|
rmdir => {}, |
518
|
|
|
|
|
|
|
}; |
519
|
15
|
|
|
|
|
181
|
$self->_delayed_operations ($x); |
520
|
|
|
|
|
|
|
} |
521
|
41
|
|
|
|
|
688
|
return $x; |
522
|
|
|
|
|
|
|
} |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
=head2 $done = $obj->done |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
C<$done> is a reference to a L |
527
|
|
|
|
|
|
|
object that keeps track of rsync activities. Only needed and used when |
528
|
|
|
|
|
|
|
we are a mirroring slave. |
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
=cut |
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
sub done { |
533
|
119
|
|
|
119
|
1
|
1110
|
my($self) = @_; |
534
|
119
|
|
|
|
|
2112
|
require File::Rsync::Mirror::Recentfile::Done; |
535
|
119
|
|
|
|
|
1700
|
my $done = $self->_done; |
536
|
119
|
100
|
|
|
|
3006
|
if (!$done) { |
|
|
100
|
|
|
|
|
|
537
|
15
|
|
|
|
|
691
|
$done = File::Rsync::Mirror::Recentfile::Done->new(); |
538
|
15
|
|
|
|
|
199
|
$done->_rfinterval ($self->interval); |
539
|
15
|
|
|
|
|
408
|
$self->_done ( $done ); |
540
|
|
|
|
|
|
|
} elsif (!blessed $done) { |
541
|
|
|
|
|
|
|
# when the serializer does not support blessed objects |
542
|
10
|
|
|
|
|
281
|
bless $done, 'File::Rsync::Mirror::Recentfile::Done'; |
543
|
10
|
|
|
|
|
99
|
$self->_done ( $done ); |
544
|
|
|
|
|
|
|
} |
545
|
119
|
|
|
|
|
2118
|
return $done; |
546
|
|
|
|
|
|
|
} |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
=head2 $tempfilename = $obj->get_remote_recentfile_as_tempfile () |
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
Stores the remote I locally as a tempfile. The caller is |
551
|
|
|
|
|
|
|
responsible to remove the file after use. |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
Note: if you're intending to act as an rsync server for other slaves, |
554
|
|
|
|
|
|
|
then you must prefer this method to fetch that file with |
555
|
|
|
|
|
|
|
get_remotefile(). Otherwise downstream mirrors would expect you to |
556
|
|
|
|
|
|
|
already have mirrored all the files that are in the I |
557
|
|
|
|
|
|
|
before you have them mirrored. |
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
=cut |
560
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
sub get_remote_recentfile_as_tempfile { |
562
|
74
|
|
|
74
|
1
|
280
|
my($self) = @_; |
563
|
74
|
|
|
|
|
559
|
mkpath $self->localroot; |
564
|
74
|
|
|
|
|
8329
|
my $fh; |
565
|
|
|
|
|
|
|
my $trfilename; |
566
|
74
|
100
|
|
|
|
470
|
if ( $self->_use_tempfile() ) { |
567
|
43
|
100
|
|
|
|
660
|
if ($self->ttl_reached) { |
568
|
10
|
|
|
|
|
127
|
$fh = $self->_current_tempfile_fh; |
569
|
10
|
|
|
|
|
172
|
$trfilename = $self->rfilename; |
570
|
|
|
|
|
|
|
} else { |
571
|
33
|
|
|
|
|
216
|
return $self->_current_tempfile; |
572
|
|
|
|
|
|
|
} |
573
|
|
|
|
|
|
|
} else { |
574
|
31
|
|
|
|
|
402
|
$trfilename = $self->rfilename; |
575
|
|
|
|
|
|
|
} |
576
|
|
|
|
|
|
|
|
577
|
41
|
|
|
|
|
245
|
my $dst; |
578
|
41
|
50
|
|
|
|
300
|
if ($fh) { |
579
|
0
|
|
|
|
|
0
|
$dst = $self->_current_tempfile; |
580
|
|
|
|
|
|
|
} else { |
581
|
41
|
|
|
|
|
290
|
$fh = $self->_get_remote_rat_provide_tempfile_object ($trfilename); |
582
|
41
|
|
|
|
|
305
|
$dst = $fh->filename; |
583
|
41
|
|
|
|
|
610
|
$self->_current_tempfile ($dst); |
584
|
41
|
|
|
|
|
353
|
my $rfile = eval { $self->rfile; }; # may fail (RECENT.recent has no rfile) |
|
41
|
|
|
|
|
221
|
|
585
|
41
|
100
|
66
|
|
|
1369
|
if (defined $rfile && -e $rfile) { |
586
|
|
|
|
|
|
|
# saving on bandwidth. Might need to be configurable |
587
|
|
|
|
|
|
|
# $self->bandwidth_is_cheap? |
588
|
27
|
50
|
|
|
|
626
|
cp $rfile, $dst or die "Could not copy '$rfile' to '$dst': $!" |
589
|
|
|
|
|
|
|
} |
590
|
|
|
|
|
|
|
} |
591
|
41
|
|
|
|
|
18615
|
my $src = join ("/", |
592
|
|
|
|
|
|
|
$self->remoteroot, |
593
|
|
|
|
|
|
|
$trfilename, |
594
|
|
|
|
|
|
|
); |
595
|
41
|
50
|
|
|
|
496
|
if ($self->verbose) { |
596
|
0
|
0
|
|
|
|
0
|
my $doing = -e $dst ? "Sync" : "Get"; |
597
|
0
|
|
|
|
|
0
|
my $display_dst = join "/", "...", basename(dirname($dst)), basename($dst); |
598
|
0
|
|
|
|
|
0
|
my $LFH = $self->_logfilehandle; |
599
|
0
|
|
|
|
|
0
|
printf $LFH |
600
|
|
|
|
|
|
|
( |
601
|
|
|
|
|
|
|
"%-4s %d (1/1/%s) temp %s ... ", |
602
|
|
|
|
|
|
|
$doing, |
603
|
|
|
|
|
|
|
time, |
604
|
|
|
|
|
|
|
$self->interval, |
605
|
|
|
|
|
|
|
$display_dst, |
606
|
|
|
|
|
|
|
); |
607
|
|
|
|
|
|
|
} |
608
|
41
|
|
|
|
|
713
|
my $gaveup = 0; |
609
|
41
|
|
|
|
|
113
|
my $retried = 0; |
610
|
41
|
|
|
|
|
1148
|
local($ENV{LANG}) = "C"; |
611
|
41
|
|
|
|
|
332
|
while (!$self->rsync->exec( |
612
|
|
|
|
|
|
|
src => $src, |
613
|
|
|
|
|
|
|
dst => $dst, |
614
|
|
|
|
|
|
|
)) { |
615
|
0
|
|
|
|
|
0
|
$self->register_rsync_error ($self->rsync->err); |
616
|
0
|
0
|
|
|
|
0
|
if (++$retried >= 3) { |
617
|
0
|
|
|
|
|
0
|
warn "XXX giving up"; |
618
|
0
|
|
|
|
|
0
|
$gaveup = 1; |
619
|
0
|
|
|
|
|
0
|
last; |
620
|
|
|
|
|
|
|
} |
621
|
|
|
|
|
|
|
} |
622
|
41
|
50
|
|
|
|
2154324
|
if ($gaveup) { |
623
|
0
|
|
|
|
|
0
|
my $LFH = $self->_logfilehandle; |
624
|
0
|
|
|
|
|
0
|
printf $LFH "Warning: gave up mirroring %s, will try again later", $self->interval; |
625
|
|
|
|
|
|
|
} else { |
626
|
41
|
|
|
|
|
1984
|
$self->_refresh_internals ($dst); |
627
|
41
|
|
|
|
|
848
|
$self->have_mirrored (Time::HiRes::time); |
628
|
41
|
|
|
|
|
922
|
$self->un_register_rsync_error (); |
629
|
|
|
|
|
|
|
} |
630
|
41
|
|
|
|
|
672
|
$self->unseed; |
631
|
41
|
50
|
|
|
|
530
|
if ($self->verbose) { |
632
|
0
|
|
|
|
|
0
|
my $LFH = $self->_logfilehandle; |
633
|
0
|
|
|
|
|
0
|
print $LFH "DONE\n"; |
634
|
|
|
|
|
|
|
} |
635
|
41
|
|
|
|
|
408
|
my $mode = 0644; |
636
|
41
|
50
|
|
|
|
1835
|
chmod $mode, $dst or die "Could not chmod $mode '$dst': $!"; |
637
|
41
|
|
|
|
|
2030
|
return $dst; |
638
|
|
|
|
|
|
|
} |
639
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
sub _verified_tempdir { |
641
|
41
|
|
|
41
|
|
151
|
my($self) = @_; |
642
|
41
|
|
|
|
|
253
|
my $tempdir = $self->__verified_tempdir(); |
643
|
41
|
100
|
|
|
|
566
|
return $tempdir if defined $tempdir; |
644
|
20
|
50
|
|
|
|
143
|
unless ($tempdir = $self->tempdir) { |
645
|
20
|
|
|
|
|
182
|
$tempdir = $self->localroot; |
646
|
|
|
|
|
|
|
} |
647
|
20
|
50
|
|
|
|
483
|
unless (-d $tempdir) { |
648
|
0
|
|
|
|
|
0
|
mkpath $tempdir; |
649
|
|
|
|
|
|
|
} |
650
|
20
|
|
|
|
|
139
|
$self->__verified_tempdir($tempdir); |
651
|
20
|
|
|
|
|
184
|
return $tempdir; |
652
|
|
|
|
|
|
|
} |
653
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
sub _get_remote_rat_provide_tempfile_object { |
655
|
41
|
|
|
41
|
|
523
|
my($self, $trfilename) = @_; |
656
|
41
|
|
|
|
|
214
|
my $_verified_tempdir = $self->_verified_tempdir; |
657
|
41
|
|
|
|
|
558
|
my $fh = File::Temp->new |
658
|
|
|
|
|
|
|
(TEMPLATE => sprintf(".FRMRecent-%s-XXXX", |
659
|
|
|
|
|
|
|
$trfilename, |
660
|
|
|
|
|
|
|
), |
661
|
|
|
|
|
|
|
DIR => $_verified_tempdir, |
662
|
|
|
|
|
|
|
SUFFIX => $self->serializer_suffix, |
663
|
|
|
|
|
|
|
UNLINK => $self->_use_tempfile, |
664
|
|
|
|
|
|
|
); |
665
|
41
|
|
|
|
|
35810
|
my $mode = 0644; |
666
|
41
|
|
|
|
|
211
|
my $dst = $fh->filename; |
667
|
41
|
50
|
|
|
|
1215
|
chmod $mode, $dst or die "Could not chmod $mode '$dst': $!"; |
668
|
41
|
100
|
|
|
|
456
|
if ($self->_use_tempfile) { |
669
|
10
|
|
|
|
|
175
|
$self->_current_tempfile_fh ($fh); # delay self destruction |
670
|
|
|
|
|
|
|
} |
671
|
41
|
|
|
|
|
665
|
return $fh; |
672
|
|
|
|
|
|
|
} |
673
|
|
|
|
|
|
|
|
674
|
|
|
|
|
|
|
sub _logfilehandle { |
675
|
0
|
|
|
0
|
|
0
|
my($self) = @_; |
676
|
0
|
|
|
|
|
0
|
my $fh; |
677
|
0
|
0
|
|
|
|
0
|
if (my $vl = $self->verboselog) { |
678
|
0
|
0
|
|
|
|
0
|
open $fh, ">>", $vl or die "Could not open >> '$vl': $!"; |
679
|
|
|
|
|
|
|
} else { |
680
|
0
|
|
|
|
|
0
|
$fh = \*STDERR; |
681
|
|
|
|
|
|
|
} |
682
|
0
|
|
|
|
|
0
|
return $fh; |
683
|
|
|
|
|
|
|
} |
684
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
=head2 $localpath = $obj->get_remotefile ( $relative_path ) |
686
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
Rsyncs one single remote file to local filesystem. |
688
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
Note: no locking is done on this file. Any number of processes may |
690
|
|
|
|
|
|
|
mirror this object. |
691
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
Note II: do not use for recentfiles. If you are a cascading |
693
|
|
|
|
|
|
|
slave/server combination, it would confuse other slaves. They would |
694
|
|
|
|
|
|
|
expect the contents of these recentfiles to be available. Use |
695
|
|
|
|
|
|
|
get_remote_recentfile_as_tempfile() instead. |
696
|
|
|
|
|
|
|
|
697
|
|
|
|
|
|
|
=cut |
698
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
sub get_remotefile { |
700
|
0
|
|
|
0
|
1
|
0
|
my($self, $path) = @_; |
701
|
0
|
|
|
|
|
0
|
my $dst = File::Spec->catfile($self->localroot, $path); |
702
|
0
|
|
|
|
|
0
|
mkpath dirname $dst; |
703
|
0
|
0
|
|
|
|
0
|
if ($self->verbose) { |
704
|
0
|
0
|
|
|
|
0
|
my $doing = -e $dst ? "Sync" : "Get"; |
705
|
0
|
|
|
|
|
0
|
my $LFH = $self->_logfilehandle; |
706
|
0
|
|
|
|
|
0
|
printf $LFH |
707
|
|
|
|
|
|
|
( |
708
|
|
|
|
|
|
|
"%-4s %d (1/1/%s) %s ... ", |
709
|
|
|
|
|
|
|
$doing, |
710
|
|
|
|
|
|
|
time, |
711
|
|
|
|
|
|
|
$self->interval, |
712
|
|
|
|
|
|
|
$path, |
713
|
|
|
|
|
|
|
); |
714
|
|
|
|
|
|
|
} |
715
|
0
|
|
|
|
|
0
|
local($ENV{LANG}) = "C"; |
716
|
0
|
0
|
|
|
|
0
|
my $remoteroot = $self->remoteroot or die "Alert: missing remoteroot. Cannot continue"; |
717
|
0
|
|
|
|
|
0
|
while (!$self->rsync->exec( |
718
|
|
|
|
|
|
|
src => join("/", |
719
|
|
|
|
|
|
|
$remoteroot, |
720
|
|
|
|
|
|
|
$path), |
721
|
|
|
|
|
|
|
dst => $dst, |
722
|
|
|
|
|
|
|
)) { |
723
|
0
|
|
|
|
|
0
|
$self->register_rsync_error ($self->rsync->err); |
724
|
|
|
|
|
|
|
} |
725
|
0
|
|
|
|
|
0
|
$self->un_register_rsync_error (); |
726
|
0
|
0
|
|
|
|
0
|
if ($self->verbose) { |
727
|
0
|
|
|
|
|
0
|
my $LFH = $self->_logfilehandle; |
728
|
0
|
|
|
|
|
0
|
print $LFH "DONE\n"; |
729
|
|
|
|
|
|
|
} |
730
|
0
|
|
|
|
|
0
|
return $dst; |
731
|
|
|
|
|
|
|
} |
732
|
|
|
|
|
|
|
|
733
|
|
|
|
|
|
|
=head2 $obj->interval ( $interval_spec ) |
734
|
|
|
|
|
|
|
|
735
|
|
|
|
|
|
|
Get/set accessor. $interval_spec is a string and described below in |
736
|
|
|
|
|
|
|
the section INTERVAL SPEC. |
737
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
=cut |
739
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
sub interval { |
741
|
72309
|
|
|
72309
|
1
|
134256
|
my ($self, $interval) = @_; |
742
|
72309
|
100
|
|
|
|
129521
|
if (@_ >= 2) { |
743
|
5060
|
|
|
|
|
15375
|
$self->_interval($interval); |
744
|
5060
|
|
|
|
|
26166
|
$self->_rfile(undef); |
745
|
|
|
|
|
|
|
} |
746
|
72309
|
|
|
|
|
147635
|
$interval = $self->_interval; |
747
|
72309
|
100
|
|
|
|
313546
|
unless (defined $interval) { |
748
|
|
|
|
|
|
|
# do not ask the $self too much, it recurses! |
749
|
1
|
|
|
|
|
7
|
require Carp; |
750
|
1
|
|
|
|
|
215
|
Carp::confess("Alert: interval undefined for '".$self."'. Cannot continue."); |
751
|
|
|
|
|
|
|
} |
752
|
72308
|
|
|
|
|
197955
|
return $interval; |
753
|
|
|
|
|
|
|
} |
754
|
|
|
|
|
|
|
|
755
|
|
|
|
|
|
|
=head2 $secs = $obj->interval_secs ( $interval_spec ) |
756
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
$interval_spec is described below in the section INTERVAL SPEC. If |
758
|
|
|
|
|
|
|
empty defaults to the inherent interval for this object. |
759
|
|
|
|
|
|
|
|
760
|
|
|
|
|
|
|
=cut |
761
|
|
|
|
|
|
|
|
762
|
|
|
|
|
|
|
sub interval_secs { |
763
|
26087
|
|
|
26087
|
1
|
190506
|
my ($self, $interval) = @_; |
764
|
26087
|
|
66
|
|
|
61435
|
$interval ||= $self->interval; |
765
|
26086
|
50
|
|
|
|
45665
|
unless (defined $interval) { |
766
|
0
|
|
|
|
|
0
|
die "interval_secs() called without argument on an object without a declared one"; |
767
|
|
|
|
|
|
|
} |
768
|
26086
|
100
|
|
|
|
131525
|
my ($n,$t) = $interval =~ /^(\d*)([smhdWMQYZ]$)/ or |
769
|
|
|
|
|
|
|
die "Could not determine seconds from interval[$interval]"; |
770
|
26085
|
100
|
33
|
|
|
124634
|
if ($interval eq "Z") { |
|
|
50
|
|
|
|
|
|
771
|
961
|
|
|
|
|
3478
|
return MAX_INT; |
772
|
|
|
|
|
|
|
} elsif (exists $seconds{$t} and $n =~ /^\d+$/) { |
773
|
25124
|
|
|
|
|
89044
|
return $seconds{$t}*$n; |
774
|
|
|
|
|
|
|
} else { |
775
|
0
|
|
|
|
|
0
|
die "Invalid interval specification: n[$n]t[$t]"; |
776
|
|
|
|
|
|
|
} |
777
|
|
|
|
|
|
|
} |
778
|
|
|
|
|
|
|
|
779
|
|
|
|
|
|
|
=head2 $obj->localroot ( $localroot ) |
780
|
|
|
|
|
|
|
|
781
|
|
|
|
|
|
|
Get/set accessor. The local root of the tree. Guaranteed without |
782
|
|
|
|
|
|
|
trailing slash. |
783
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
=cut |
785
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
sub localroot { |
787
|
11543
|
|
|
11543
|
1
|
24458
|
my ($self, $localroot) = @_; |
788
|
11543
|
100
|
|
|
|
27971
|
if (@_ >= 2) { |
789
|
1663
|
|
|
|
|
9010
|
$localroot =~ s|/$||; |
790
|
1663
|
|
|
|
|
6535
|
$self->_localroot($localroot); |
791
|
1663
|
|
|
|
|
9780
|
$self->_rfile(undef); |
792
|
|
|
|
|
|
|
} |
793
|
11543
|
|
|
|
|
29154
|
$localroot = $self->_localroot; |
794
|
|
|
|
|
|
|
} |
795
|
|
|
|
|
|
|
|
796
|
|
|
|
|
|
|
=head2 $ret = $obj->local_path($path_found_in_recentfile) |
797
|
|
|
|
|
|
|
|
798
|
|
|
|
|
|
|
Combines the path to our local mirror and the path of an object found |
799
|
|
|
|
|
|
|
in this I. In other words: the target of a mirror operation. |
800
|
|
|
|
|
|
|
|
801
|
|
|
|
|
|
|
Implementation note: We split on slashes and then use |
802
|
|
|
|
|
|
|
File::Spec::catfile to adjust to the local operating system. |
803
|
|
|
|
|
|
|
|
804
|
|
|
|
|
|
|
=cut |
805
|
|
|
|
|
|
|
|
806
|
|
|
|
|
|
|
sub local_path { |
807
|
1486
|
|
|
1486
|
1
|
4181
|
my($self,$path) = @_; |
808
|
1486
|
50
|
|
|
|
2970
|
unless (defined $path) { |
809
|
|
|
|
|
|
|
# seems like a degenerated case |
810
|
0
|
|
|
|
|
0
|
return $self->localroot; |
811
|
|
|
|
|
|
|
} |
812
|
1486
|
|
|
|
|
4418
|
my @p = split m|/|, $path; |
813
|
1486
|
|
|
|
|
3609
|
File::Spec->catfile($self->localroot,@p); |
814
|
|
|
|
|
|
|
} |
815
|
|
|
|
|
|
|
|
816
|
|
|
|
|
|
|
=head2 (void) $obj->lock |
817
|
|
|
|
|
|
|
|
818
|
|
|
|
|
|
|
Locking is implemented with an C on a locking directory |
819
|
|
|
|
|
|
|
(C<.lock> appended to $rfile). |
820
|
|
|
|
|
|
|
|
821
|
|
|
|
|
|
|
=cut |
822
|
|
|
|
|
|
|
|
823
|
|
|
|
|
|
|
sub lock { |
824
|
3088
|
|
|
3088
|
1
|
6129
|
my ($self) = @_; |
825
|
|
|
|
|
|
|
# not using flock because it locks on filehandles instead of |
826
|
|
|
|
|
|
|
# old school ressources. |
827
|
3088
|
50
|
|
|
|
8364
|
my $locked = $self->_is_locked and return; |
828
|
3088
|
|
|
|
|
18430
|
my $rfile = $self->rfile; |
829
|
|
|
|
|
|
|
# XXX need a way to allow breaking the lock |
830
|
3088
|
|
|
|
|
6614
|
my $start = time; |
831
|
3088
|
|
50
|
|
|
9015
|
my $locktimeout = $self->locktimeout || 600; |
832
|
3088
|
|
|
|
|
18997
|
my %have_warned; |
833
|
3088
|
|
|
|
|
8440
|
my $lockdir = "$rfile.lock"; |
834
|
3088
|
|
|
|
|
6292
|
my $procfile = "$lockdir/process"; |
835
|
3088
|
|
|
|
|
194679
|
GETLOCK: while (not mkdir $lockdir) { |
836
|
0
|
0
|
|
|
|
0
|
if (open my $fh, "<", $procfile) { |
837
|
0
|
|
|
|
|
0
|
chomp(my $process = <$fh>); |
838
|
0
|
0
|
|
|
|
0
|
if (0) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
839
|
0
|
|
|
|
|
0
|
} elsif ($process !~ /^\d+$/) { |
840
|
0
|
0
|
|
|
|
0
|
warn "Warning: unknown process holds a lock in '$lockdir', waiting..." unless $have_warned{unknown}++; |
841
|
|
|
|
|
|
|
} elsif ($$ == $process) { |
842
|
0
|
|
|
|
|
0
|
last GETLOCK; |
843
|
|
|
|
|
|
|
} elsif (kill 0, $process) { |
844
|
0
|
0
|
|
|
|
0
|
warn "Warning: process $process holds a lock in '$lockdir', waiting..." unless $have_warned{$process}++; |
845
|
|
|
|
|
|
|
} else { |
846
|
0
|
|
|
|
|
0
|
warn "Warning: breaking lock held by process $process"; |
847
|
0
|
|
|
|
|
0
|
sleep 1; |
848
|
0
|
|
|
|
|
0
|
last GETLOCK; |
849
|
|
|
|
|
|
|
} |
850
|
|
|
|
|
|
|
} else { |
851
|
0
|
0
|
|
|
|
0
|
warn "Warning: unknown process holds a lock in '$lockdir', waiting..." unless $have_warned{unknown}++; |
852
|
|
|
|
|
|
|
} |
853
|
0
|
|
|
|
|
0
|
Time::HiRes::sleep 0.01; |
854
|
0
|
0
|
|
|
|
0
|
if (time - $start > $locktimeout) { |
855
|
0
|
|
|
|
|
0
|
die "Could not acquire lockdirectory '$rfile.lock': $!"; |
856
|
|
|
|
|
|
|
} |
857
|
|
|
|
|
|
|
} # GETLOCK |
858
|
3088
|
50
|
|
|
|
209398
|
open my $fh, ">", $procfile or die "Could not open >$procfile\: $!"; |
859
|
3088
|
|
|
|
|
46212
|
print $fh $$, "\n"; |
860
|
3088
|
50
|
|
|
|
91918
|
close $fh or die "Could not close: $!"; |
861
|
3088
|
|
|
|
|
20517
|
$self->_is_locked (1); |
862
|
|
|
|
|
|
|
} |
863
|
|
|
|
|
|
|
|
864
|
|
|
|
|
|
|
=head2 (void) $obj->merge ($other) |
865
|
|
|
|
|
|
|
|
866
|
|
|
|
|
|
|
Bulk update of this object with another one. It's used to merge a |
867
|
|
|
|
|
|
|
smaller and younger $other object into the current one. If this file |
868
|
|
|
|
|
|
|
is a C file, then we normally do not merge in objects of type |
869
|
|
|
|
|
|
|
C; this can be overridden by setting |
870
|
|
|
|
|
|
|
keep_delete_objects_forever. But if we encounter an object of type |
871
|
|
|
|
|
|
|
delete we delete the corresponding C object if we have it. |
872
|
|
|
|
|
|
|
|
873
|
|
|
|
|
|
|
If there is nothing to be merged, nothing is done. |
874
|
|
|
|
|
|
|
|
875
|
|
|
|
|
|
|
=cut |
876
|
|
|
|
|
|
|
|
877
|
|
|
|
|
|
|
sub merge { |
878
|
721
|
|
|
721
|
1
|
1989
|
my($self, $other) = @_; |
879
|
721
|
|
|
|
|
2358
|
$self->_merge_sanitycheck ( $other ); |
880
|
721
|
|
|
|
|
2182
|
$other->lock; |
881
|
721
|
|
50
|
|
|
7043
|
my $other_recent = $other->recent_events || []; |
882
|
721
|
|
|
|
|
2764
|
$self->lock; |
883
|
721
|
|
|
|
|
8808
|
$self->_merge_locked ( $other, $other_recent ); |
884
|
721
|
|
|
|
|
10105
|
$self->unlock; |
885
|
721
|
|
|
|
|
5059
|
$other->unlock; |
886
|
|
|
|
|
|
|
} |
887
|
|
|
|
|
|
|
|
888
|
|
|
|
|
|
|
sub _merge_locked { |
889
|
721
|
|
|
721
|
|
2256
|
my($self, $other, $other_recent) = @_; |
890
|
721
|
|
50
|
|
|
1989
|
my $my_recent = $self->recent_events || []; |
891
|
|
|
|
|
|
|
|
892
|
|
|
|
|
|
|
# calculate the target time span |
893
|
721
|
100
|
|
|
|
2629
|
my $myepoch = $my_recent->[0] ? $my_recent->[0]{epoch} : undef; |
894
|
721
|
50
|
|
|
|
1898
|
my $epoch = $other_recent->[0] ? $other_recent->[0]{epoch} : $myepoch; |
895
|
721
|
|
|
|
|
1176
|
my $oldest_allowed = 0; |
896
|
721
|
|
|
|
|
1015
|
my $something_done; |
897
|
721
|
100
|
|
|
|
1841
|
unless ($my_recent->[0]) { |
898
|
|
|
|
|
|
|
# obstetrics |
899
|
75
|
|
|
|
|
110
|
$something_done = 1; |
900
|
|
|
|
|
|
|
} |
901
|
721
|
50
|
|
|
|
1663
|
if ($epoch) { |
902
|
721
|
100
|
50
|
|
|
1793
|
if (($other->dirtymark||0) ne ($self->dirtymark||0)) { |
|
|
100
|
100
|
|
|
|
|
903
|
233
|
|
|
|
|
2933
|
$oldest_allowed = 0; |
904
|
233
|
|
|
|
|
306
|
$something_done = 1; |
905
|
|
|
|
|
|
|
} elsif (my $merged = $self->merged) { |
906
|
446
|
|
|
|
|
1147
|
my $secs = $self->interval_secs(); |
907
|
446
|
|
50
|
|
|
4041
|
$oldest_allowed = min($epoch - $secs, $merged->{epoch}||0); |
908
|
446
|
50
|
33
|
|
|
2596
|
if (@$other_recent and |
909
|
|
|
|
|
|
|
_bigfloatlt($other_recent->[-1]{epoch}, $oldest_allowed) |
910
|
|
|
|
|
|
|
) { |
911
|
0
|
|
|
|
|
0
|
$oldest_allowed = $other_recent->[-1]{epoch}; |
912
|
|
|
|
|
|
|
} |
913
|
|
|
|
|
|
|
} |
914
|
721
|
|
100
|
|
|
3587
|
while (@$my_recent && _bigfloatlt($my_recent->[-1]{epoch}, $oldest_allowed)) { |
915
|
1356
|
|
|
|
|
2061
|
pop @$my_recent; |
916
|
1356
|
|
|
|
|
3730
|
$something_done = 1; |
917
|
|
|
|
|
|
|
} |
918
|
|
|
|
|
|
|
} |
919
|
|
|
|
|
|
|
|
920
|
721
|
|
|
|
|
1238
|
my %have_path; |
921
|
721
|
|
|
|
|
1318
|
my $other_recent_filtered = []; |
922
|
721
|
|
|
|
|
1538
|
for my $oev (@$other_recent) { |
923
|
24436
|
|
50
|
|
|
48347
|
my $oevepoch = $oev->{epoch} || 0; |
924
|
24436
|
50
|
|
|
|
41121
|
next if _bigfloatlt($oevepoch, $oldest_allowed); |
925
|
24436
|
|
|
|
|
41801
|
my $path = $oev->{path}; |
926
|
24436
|
50
|
|
|
|
62604
|
next if $have_path{$path}++; |
927
|
24436
|
100
|
100
|
|
|
38942
|
if ( $self->interval eq "Z" |
|
|
|
66
|
|
|
|
|
928
|
|
|
|
|
|
|
and $oev->{type} eq "delete" |
929
|
|
|
|
|
|
|
and ! $self->keep_delete_objects_forever |
930
|
|
|
|
|
|
|
) { |
931
|
|
|
|
|
|
|
# do nothing |
932
|
|
|
|
|
|
|
} else { |
933
|
24409
|
100
|
100
|
|
|
54146
|
if (!$myepoch || _bigfloatgt($oevepoch, $myepoch)) { |
934
|
4725
|
|
|
|
|
5489
|
$something_done = 1; |
935
|
|
|
|
|
|
|
} |
936
|
24409
|
|
|
|
|
103839
|
push @$other_recent_filtered, { epoch => $oev->{epoch}, path => $path, type => $oev->{type} }; |
937
|
|
|
|
|
|
|
} |
938
|
|
|
|
|
|
|
} |
939
|
721
|
100
|
|
|
|
3097
|
if ($something_done) { |
940
|
679
|
|
|
|
|
2501
|
$self->_merge_something_done ($other_recent_filtered, $my_recent, $other_recent, $other, \%have_path, $epoch); |
941
|
|
|
|
|
|
|
} |
942
|
|
|
|
|
|
|
} |
943
|
|
|
|
|
|
|
|
944
|
|
|
|
|
|
|
sub _merge_something_done { |
945
|
679
|
|
|
679
|
|
2241
|
my($self, $other_recent_filtered, $my_recent, $other_recent, $other, $have_path, $epoch) = @_; |
946
|
679
|
|
|
|
|
1100
|
my $recent = []; |
947
|
679
|
|
|
|
|
1302
|
my $epoch_conflict = 0; |
948
|
679
|
|
|
|
|
980
|
my $last_epoch; |
949
|
679
|
|
100
|
|
|
1843
|
ZIP: while (@$other_recent_filtered || @$my_recent) { |
950
|
55206
|
|
|
|
|
57839
|
my $event; |
951
|
55206
|
100
|
100
|
|
|
142940
|
if (!@$my_recent || |
|
|
|
100
|
|
|
|
|
952
|
|
|
|
|
|
|
@$other_recent_filtered && _bigfloatge($other_recent_filtered->[0]{epoch},$my_recent->[0]{epoch})) { |
953
|
23355
|
|
|
|
|
31361
|
$event = shift @$other_recent_filtered; |
954
|
|
|
|
|
|
|
} else { |
955
|
31851
|
|
|
|
|
39347
|
$event = shift @$my_recent; |
956
|
31851
|
100
|
|
|
|
110062
|
next ZIP if $have_path->{$event->{path}}++; |
957
|
|
|
|
|
|
|
} |
958
|
36654
|
100
|
100
|
|
|
101177
|
$epoch_conflict=1 if defined $last_epoch && $event->{epoch} eq $last_epoch; |
959
|
36654
|
|
|
|
|
45168
|
$last_epoch = $event->{epoch}; |
960
|
36654
|
|
|
|
|
82499
|
push @$recent, $event; |
961
|
|
|
|
|
|
|
} |
962
|
679
|
100
|
|
|
|
1474
|
if ($epoch_conflict) { |
963
|
10
|
|
|
|
|
15
|
my %have_epoch; |
964
|
10
|
|
|
|
|
45
|
for (my $i = $#$recent;$i>=0;$i--) { |
965
|
270
|
|
|
|
|
340
|
my $epoch = $recent->[$i]{epoch}; |
966
|
270
|
100
|
|
|
|
820
|
if ($have_epoch{$epoch}++) { |
967
|
10
|
|
|
|
|
35
|
while ($have_epoch{$epoch}) { |
968
|
10
|
|
|
|
|
40
|
$epoch = _increase_a_bit($epoch); |
969
|
|
|
|
|
|
|
} |
970
|
10
|
|
|
|
|
30
|
$recent->[$i]{epoch} = $epoch; |
971
|
10
|
|
|
|
|
35
|
$have_epoch{$epoch}++; |
972
|
|
|
|
|
|
|
} |
973
|
|
|
|
|
|
|
} |
974
|
|
|
|
|
|
|
} |
975
|
679
|
100
|
100
|
|
|
2176
|
if (!$self->dirtymark || $other->dirtymark ne $self->dirtymark) { |
976
|
233
|
|
|
|
|
3089
|
$self->dirtymark ( $other->dirtymark ); |
977
|
|
|
|
|
|
|
} |
978
|
679
|
|
|
|
|
8515
|
$self->write_recent($recent); |
979
|
|
|
|
|
|
|
$other->merged({ |
980
|
|
|
|
|
|
|
time => Time::HiRes::time, # not used anywhere |
981
|
|
|
|
|
|
|
epoch => $recent->[0]{epoch}, |
982
|
679
|
|
|
|
|
5530
|
into_interval => $self->interval, # not used anywhere |
983
|
|
|
|
|
|
|
}); |
984
|
679
|
|
|
|
|
1600
|
$other->write_recent($other_recent); |
985
|
|
|
|
|
|
|
} |
986
|
|
|
|
|
|
|
|
987
|
|
|
|
|
|
|
sub _merge_sanitycheck { |
988
|
721
|
|
|
721
|
|
1416
|
my($self, $other) = @_; |
989
|
721
|
50
|
|
|
|
1753
|
if ($self->interval_secs <= $other->interval_secs) { |
990
|
0
|
|
|
|
|
0
|
require Carp; |
991
|
0
|
|
|
|
|
0
|
Carp::confess |
992
|
|
|
|
|
|
|
(sprintf |
993
|
|
|
|
|
|
|
( |
994
|
|
|
|
|
|
|
"Alert: illegal merge operation of a bigger interval[%d] into a smaller[%d]", |
995
|
|
|
|
|
|
|
$self->interval_secs, |
996
|
|
|
|
|
|
|
$other->interval_secs, |
997
|
|
|
|
|
|
|
)); |
998
|
|
|
|
|
|
|
} |
999
|
|
|
|
|
|
|
} |
1000
|
|
|
|
|
|
|
|
1001
|
|
|
|
|
|
|
=head2 merged |
1002
|
|
|
|
|
|
|
|
1003
|
|
|
|
|
|
|
Hashref denoting when this recentfile has been merged into some other |
1004
|
|
|
|
|
|
|
at which epoch. |
1005
|
|
|
|
|
|
|
|
1006
|
|
|
|
|
|
|
=cut |
1007
|
|
|
|
|
|
|
|
1008
|
|
|
|
|
|
|
sub merged { |
1009
|
14503
|
|
|
14503
|
1
|
31049
|
my($self, $set) = @_; |
1010
|
14503
|
100
|
|
|
|
27133
|
if (defined $set) { |
1011
|
4542
|
|
|
|
|
10103
|
$self->_merged ($set); |
1012
|
|
|
|
|
|
|
} |
1013
|
14503
|
|
|
|
|
40140
|
my $merged = $self->_merged; |
1014
|
14503
|
|
|
|
|
50754
|
my $into; |
1015
|
14503
|
100
|
100
|
|
|
60935
|
if ($merged and $into = $merged->{into_interval} and defined $self->_interval) { |
|
|
|
100
|
|
|
|
|
1016
|
|
|
|
|
|
|
# sanity checks |
1017
|
9421
|
50
|
|
|
|
53544
|
if ($into eq $self->interval) { |
|
|
50
|
|
|
|
|
|
1018
|
0
|
|
|
|
|
0
|
require Carp; |
1019
|
0
|
|
|
|
|
0
|
Carp::cluck(sprintf |
1020
|
|
|
|
|
|
|
( |
1021
|
|
|
|
|
|
|
"Warning: into_interval[%s] same as own interval[%s]. Danger ahead.", |
1022
|
|
|
|
|
|
|
$into, |
1023
|
|
|
|
|
|
|
$self->interval, |
1024
|
|
|
|
|
|
|
)); |
1025
|
|
|
|
|
|
|
} elsif ($self->interval_secs($into) < $self->interval_secs) { |
1026
|
0
|
|
|
|
|
0
|
require Carp; |
1027
|
0
|
|
|
|
|
0
|
Carp::cluck(sprintf |
1028
|
|
|
|
|
|
|
( |
1029
|
|
|
|
|
|
|
"Warning: into_interval_secs[%s] smaller than own interval_secs[%s] on interval[%s]. Danger ahead.", |
1030
|
|
|
|
|
|
|
$self->interval_secs($into), |
1031
|
|
|
|
|
|
|
$self->interval_secs, |
1032
|
|
|
|
|
|
|
$self->interval, |
1033
|
|
|
|
|
|
|
)); |
1034
|
|
|
|
|
|
|
} |
1035
|
|
|
|
|
|
|
} |
1036
|
14503
|
|
|
|
|
37582
|
$merged; |
1037
|
|
|
|
|
|
|
} |
1038
|
|
|
|
|
|
|
|
1039
|
|
|
|
|
|
|
=head2 $hashref = $obj->meta_data |
1040
|
|
|
|
|
|
|
|
1041
|
|
|
|
|
|
|
Returns the hashref of metadata that the server has to add to the |
1042
|
|
|
|
|
|
|
I. |
1043
|
|
|
|
|
|
|
|
1044
|
|
|
|
|
|
|
=cut |
1045
|
|
|
|
|
|
|
|
1046
|
|
|
|
|
|
|
sub meta_data { |
1047
|
2712
|
|
|
2712
|
1
|
4954
|
my($self) = @_; |
1048
|
2712
|
|
|
|
|
5256
|
my $ret = $self->{meta}; |
1049
|
2712
|
|
|
|
|
6033
|
for my $m ( |
1050
|
|
|
|
|
|
|
"aggregator", |
1051
|
|
|
|
|
|
|
"canonize", |
1052
|
|
|
|
|
|
|
"comment", |
1053
|
|
|
|
|
|
|
"dirtymark", |
1054
|
|
|
|
|
|
|
"filenameroot", |
1055
|
|
|
|
|
|
|
"interval", |
1056
|
|
|
|
|
|
|
"merged", |
1057
|
|
|
|
|
|
|
"minmax", |
1058
|
|
|
|
|
|
|
"protocol", |
1059
|
|
|
|
|
|
|
"serializer_suffix", |
1060
|
|
|
|
|
|
|
) { |
1061
|
27120
|
|
|
|
|
55872
|
my $v = $self->$m; |
1062
|
27120
|
100
|
|
|
|
88919
|
if (defined $v) { |
1063
|
23322
|
|
|
|
|
51022
|
$ret->{$m} = $v; |
1064
|
|
|
|
|
|
|
} |
1065
|
|
|
|
|
|
|
} |
1066
|
|
|
|
|
|
|
# XXX need to reset the Producer if I am a writer, keep it when I |
1067
|
|
|
|
|
|
|
# am a reader |
1068
|
|
|
|
|
|
|
$ret->{Producers} ||= { |
1069
|
2712
|
|
50
|
|
|
41294
|
__PACKAGE__, "$VERSION", # stringified it looks better |
1070
|
|
|
|
|
|
|
'$0', $0, |
1071
|
|
|
|
|
|
|
'time', Time::HiRes::time, |
1072
|
|
|
|
|
|
|
}; |
1073
|
2712
|
|
66
|
|
|
7573
|
$ret->{dirtymark} ||= Time::HiRes::time; |
1074
|
2712
|
|
|
|
|
8426
|
return $ret; |
1075
|
|
|
|
|
|
|
} |
1076
|
|
|
|
|
|
|
|
1077
|
|
|
|
|
|
|
=head2 $success = $obj->mirror ( %options ) |
1078
|
|
|
|
|
|
|
|
1079
|
|
|
|
|
|
|
Mirrors the files in this I as reported by |
1080
|
|
|
|
|
|
|
C. Options named C, C, C are passed |
1081
|
|
|
|
|
|
|
through to the C call. The boolean option C, |
1082
|
|
|
|
|
|
|
if true, causes C to only rsync C |
1083
|
|
|
|
|
|
|
and keep track of the rsynced files so that future calls will rsync |
1084
|
|
|
|
|
|
|
different files until all files are brought to sync. |
1085
|
|
|
|
|
|
|
|
1086
|
|
|
|
|
|
|
=cut |
1087
|
|
|
|
|
|
|
|
1088
|
|
|
|
|
|
|
sub mirror { |
1089
|
32
|
|
|
32
|
1
|
6293
|
my($self, %options) = @_; |
1090
|
32
|
|
|
|
|
536
|
my $trecentfile = $self->get_remote_recentfile_as_tempfile(); |
1091
|
32
|
|
|
|
|
4908
|
$self->_use_tempfile (1); |
1092
|
|
|
|
|
|
|
# skip-deletes is inadequat for passthrough within mirror. We |
1093
|
|
|
|
|
|
|
# would never reach uptodateness when a delete were on a |
1094
|
|
|
|
|
|
|
# borderline |
1095
|
32
|
|
|
|
|
334
|
my %passthrough = map { ($_ => $options{$_}) } qw(before after max); |
|
96
|
|
|
|
|
1346
|
|
1096
|
32
|
|
|
|
|
655
|
my ($recent_events) = $self->recent_events(%passthrough); |
1097
|
32
|
|
|
|
|
116
|
my(@error, @dlcollector); # download-collector: array containing paths we need |
1098
|
32
|
|
|
|
|
222
|
my $first_item = 0; |
1099
|
32
|
|
|
|
|
106
|
my $last_item = $#$recent_events; |
1100
|
32
|
|
|
|
|
322
|
my $done = $self->done; |
1101
|
32
|
|
|
|
|
174
|
my $pathdb = $self->_pathdb; |
1102
|
32
|
|
|
|
|
537
|
ITEM: for my $i ($first_item..$last_item) { |
1103
|
2732
|
|
|
|
|
4177
|
my $status = +{}; |
1104
|
2732
|
|
|
|
|
7590
|
$self->_mirror_item |
1105
|
|
|
|
|
|
|
( |
1106
|
|
|
|
|
|
|
$i, |
1107
|
|
|
|
|
|
|
$recent_events, |
1108
|
|
|
|
|
|
|
$last_item, |
1109
|
|
|
|
|
|
|
$done, |
1110
|
|
|
|
|
|
|
$pathdb, |
1111
|
|
|
|
|
|
|
\@dlcollector, |
1112
|
|
|
|
|
|
|
\%options, |
1113
|
|
|
|
|
|
|
$status, |
1114
|
|
|
|
|
|
|
\@error, |
1115
|
|
|
|
|
|
|
); |
1116
|
2732
|
100
|
|
|
|
6829
|
last if $i == $last_item; |
1117
|
2706
|
100
|
|
|
|
6552
|
if ($status->{mustreturn}){ |
1118
|
6
|
100
|
66
|
|
|
222
|
if ($self->_current_tempfile && ! $self->_current_tempfile_fh) { |
1119
|
|
|
|
|
|
|
# looks like a bug somewhere else |
1120
|
5
|
|
|
|
|
470
|
my $t = $self->_current_tempfile; |
1121
|
5
|
50
|
|
|
|
939
|
unlink $t or die "Could not unlink '$t': $!"; |
1122
|
5
|
|
|
|
|
88
|
$self->_current_tempfile(undef); |
1123
|
5
|
|
|
|
|
153
|
$self->_use_tempfile(0); |
1124
|
|
|
|
|
|
|
} |
1125
|
6
|
|
|
|
|
5897
|
return; |
1126
|
|
|
|
|
|
|
} |
1127
|
|
|
|
|
|
|
} |
1128
|
26
|
100
|
|
|
|
200
|
if (@dlcollector) { |
1129
|
17
|
|
|
|
|
105
|
my $success = eval { $self->_mirror_dlcollector (\@dlcollector,$pathdb,$recent_events);}; |
|
17
|
|
|
|
|
335
|
|
1130
|
17
|
50
|
33
|
|
|
434
|
if (!$success || $@) { |
1131
|
0
|
|
|
|
|
0
|
warn "Warning: Unknown error while mirroring: $@"; |
1132
|
0
|
|
|
|
|
0
|
push @error, $@; |
1133
|
0
|
|
|
|
|
0
|
sleep 1; |
1134
|
|
|
|
|
|
|
} |
1135
|
|
|
|
|
|
|
} |
1136
|
26
|
50
|
|
|
|
361
|
if ($self->verbose) { |
1137
|
0
|
|
|
|
|
0
|
my $LFH = $self->_logfilehandle; |
1138
|
0
|
|
|
|
|
0
|
print $LFH "DONE\n"; |
1139
|
|
|
|
|
|
|
} |
1140
|
|
|
|
|
|
|
# once we've gone to the end we consider ourselves free of obligations |
1141
|
26
|
|
|
|
|
508
|
$self->unseed; |
1142
|
26
|
|
|
|
|
272
|
$self->_mirror_unhide_tempfile ($trecentfile); |
1143
|
26
|
|
|
|
|
543
|
$self->_mirror_perform_delayed_ops(\%options); |
1144
|
26
|
|
|
|
|
9805
|
return !@error; |
1145
|
|
|
|
|
|
|
} |
1146
|
|
|
|
|
|
|
|
1147
|
|
|
|
|
|
|
sub _mirror_item { |
1148
|
2732
|
|
|
2732
|
|
5463
|
my($self, |
1149
|
|
|
|
|
|
|
$i, |
1150
|
|
|
|
|
|
|
$recent_events, |
1151
|
|
|
|
|
|
|
$last_item, |
1152
|
|
|
|
|
|
|
$done, |
1153
|
|
|
|
|
|
|
$pathdb, |
1154
|
|
|
|
|
|
|
$dlcollector, |
1155
|
|
|
|
|
|
|
$options, |
1156
|
|
|
|
|
|
|
$status, |
1157
|
|
|
|
|
|
|
$error, |
1158
|
|
|
|
|
|
|
) = @_; |
1159
|
2732
|
|
|
|
|
3943
|
my $recent_event = $recent_events->[$i]; |
1160
|
2732
|
100
|
|
|
|
7012
|
return if $done->covered ( $recent_event->{epoch} ); |
1161
|
1486
|
100
|
|
|
|
3437
|
if ($pathdb) { |
1162
|
826
|
|
|
|
|
2462
|
my $rec = $pathdb->{$recent_event->{path}}; |
1163
|
826
|
50
|
66
|
|
|
2745
|
if ($rec && $rec->{recentepoch}) { |
1164
|
271
|
50
|
|
|
|
1076
|
if (_bigfloatgt |
1165
|
|
|
|
|
|
|
( $rec->{recentepoch}, $recent_event->{epoch} )){ |
1166
|
0
|
|
|
|
|
0
|
$done->register ($recent_events, [$i]); |
1167
|
0
|
|
|
|
|
0
|
return; |
1168
|
|
|
|
|
|
|
} |
1169
|
|
|
|
|
|
|
} |
1170
|
|
|
|
|
|
|
} |
1171
|
1486
|
|
|
|
|
3449
|
my $dst = $self->local_path($recent_event->{path}); |
1172
|
1486
|
100
|
|
|
|
22012
|
if ($recent_event->{type} eq "new"){ |
|
|
50
|
|
|
|
|
|
1173
|
1462
|
|
|
|
|
3539
|
$self->_mirror_item_new |
1174
|
|
|
|
|
|
|
( |
1175
|
|
|
|
|
|
|
$dst, |
1176
|
|
|
|
|
|
|
$i, |
1177
|
|
|
|
|
|
|
$last_item, |
1178
|
|
|
|
|
|
|
$recent_events, |
1179
|
|
|
|
|
|
|
$recent_event, |
1180
|
|
|
|
|
|
|
$dlcollector, |
1181
|
|
|
|
|
|
|
$pathdb, |
1182
|
|
|
|
|
|
|
$status, |
1183
|
|
|
|
|
|
|
$error, |
1184
|
|
|
|
|
|
|
$options, |
1185
|
|
|
|
|
|
|
); |
1186
|
|
|
|
|
|
|
} elsif ($recent_event->{type} eq "delete") { |
1187
|
24
|
|
|
|
|
160
|
my $activity; |
1188
|
24
|
50
|
|
|
|
202
|
if ($options->{'skip-deletes'}) { |
1189
|
0
|
|
|
|
|
0
|
$activity = "skipped"; |
1190
|
|
|
|
|
|
|
} else { |
1191
|
24
|
|
|
|
|
1434
|
my @lstat = lstat $dst; |
1192
|
24
|
100
|
33
|
|
|
980
|
if (! -e _) { |
|
|
50
|
|
|
|
|
|
1193
|
9
|
|
|
|
|
120
|
$activity = "not_found"; |
1194
|
|
|
|
|
|
|
} elsif (-l _ or not -d _) { |
1195
|
15
|
|
|
|
|
235
|
$self->delayed_operations->{unlink}{$dst}++; |
1196
|
15
|
|
|
|
|
185
|
$activity = "deleted"; |
1197
|
|
|
|
|
|
|
} else { |
1198
|
0
|
|
|
|
|
0
|
$self->delayed_operations->{rmdir}{$dst}++; |
1199
|
0
|
|
|
|
|
0
|
$activity = "deleted"; |
1200
|
|
|
|
|
|
|
} |
1201
|
|
|
|
|
|
|
} |
1202
|
24
|
|
|
|
|
432
|
$done->register ($recent_events, [$i]); |
1203
|
24
|
100
|
|
|
|
219
|
if ($pathdb) { |
1204
|
9
|
|
|
|
|
116
|
$self->_mirror_register_path($pathdb,[$recent_event],$activity); |
1205
|
|
|
|
|
|
|
} |
1206
|
|
|
|
|
|
|
} else { |
1207
|
0
|
|
|
|
|
0
|
warn "Warning: invalid upload type '$recent_event->{type}'"; |
1208
|
|
|
|
|
|
|
} |
1209
|
|
|
|
|
|
|
} |
1210
|
|
|
|
|
|
|
|
1211
|
|
|
|
|
|
|
sub _mirror_item_new { |
1212
|
1462
|
|
|
1462
|
|
3262
|
my($self, |
1213
|
|
|
|
|
|
|
$dst, |
1214
|
|
|
|
|
|
|
$i, |
1215
|
|
|
|
|
|
|
$last_item, |
1216
|
|
|
|
|
|
|
$recent_events, |
1217
|
|
|
|
|
|
|
$recent_event, |
1218
|
|
|
|
|
|
|
$dlcollector, |
1219
|
|
|
|
|
|
|
$pathdb, |
1220
|
|
|
|
|
|
|
$status, |
1221
|
|
|
|
|
|
|
$error, |
1222
|
|
|
|
|
|
|
$options, |
1223
|
|
|
|
|
|
|
) = @_; |
1224
|
1462
|
50
|
|
|
|
3146
|
if ($self->verbose) { |
1225
|
0
|
0
|
|
|
|
0
|
my $doing = -e $dst ? "Sync" : "Get"; |
1226
|
0
|
|
|
|
|
0
|
my $LFH = $self->_logfilehandle; |
1227
|
|
|
|
|
|
|
printf $LFH |
1228
|
|
|
|
|
|
|
( |
1229
|
|
|
|
|
|
|
"%-4s %d (%d/%d/%s) %s ... ", |
1230
|
|
|
|
|
|
|
$doing, |
1231
|
|
|
|
|
|
|
time, |
1232
|
|
|
|
|
|
|
1+$i, |
1233
|
|
|
|
|
|
|
1+$last_item, |
1234
|
|
|
|
|
|
|
$self->interval, |
1235
|
|
|
|
|
|
|
$recent_event->{path}, |
1236
|
0
|
|
|
|
|
0
|
); |
1237
|
|
|
|
|
|
|
} |
1238
|
1462
|
|
50
|
|
|
7254
|
my $max_files_per_connection = $self->max_files_per_connection || 42; |
1239
|
1462
|
|
|
|
|
5749
|
my $success; |
1240
|
1462
|
50
|
|
|
|
2718
|
if ($self->verbose) { |
1241
|
0
|
|
|
|
|
0
|
my $LFH = $self->_logfilehandle; |
1242
|
0
|
|
|
|
|
0
|
print $LFH "\n"; |
1243
|
|
|
|
|
|
|
} |
1244
|
1462
|
|
|
|
|
9457
|
push @$dlcollector, { rev => $recent_event, i => $i }; |
1245
|
1462
|
100
|
|
|
|
3827
|
if (@$dlcollector >= $max_files_per_connection) { |
1246
|
11
|
|
|
|
|
35
|
$success = eval {$self->_mirror_dlcollector ($dlcollector,$pathdb,$recent_events);}; |
|
11
|
|
|
|
|
136
|
|
1247
|
11
|
|
|
|
|
161
|
my $sleep = $self->sleep_per_connection; |
1248
|
11
|
50
|
|
|
|
201
|
$sleep = 0.42 unless defined $sleep; |
1249
|
11
|
|
|
|
|
4622636
|
Time::HiRes::sleep $sleep; |
1250
|
11
|
100
|
|
|
|
676
|
if ($options->{piecemeal}) { |
1251
|
6
|
|
|
|
|
123
|
$status->{mustreturn} = 1; |
1252
|
6
|
|
|
|
|
201
|
return; |
1253
|
|
|
|
|
|
|
} |
1254
|
|
|
|
|
|
|
} else { |
1255
|
1451
|
|
|
|
|
3184
|
return; |
1256
|
|
|
|
|
|
|
} |
1257
|
5
|
50
|
33
|
|
|
425
|
if (!$success || $@) { |
1258
|
0
|
|
|
|
|
0
|
warn "Warning: Error while mirroring: $@"; |
1259
|
0
|
|
|
|
|
0
|
push @$error, $@; |
1260
|
0
|
|
|
|
|
0
|
sleep 1; |
1261
|
|
|
|
|
|
|
} |
1262
|
5
|
50
|
|
|
|
220
|
if ($self->verbose) { |
1263
|
0
|
|
|
|
|
0
|
my $LFH = $self->_logfilehandle; |
1264
|
0
|
|
|
|
|
0
|
print $LFH "DONE\n"; |
1265
|
|
|
|
|
|
|
} |
1266
|
|
|
|
|
|
|
} |
1267
|
|
|
|
|
|
|
|
1268
|
|
|
|
|
|
|
sub _mirror_dlcollector { |
1269
|
28
|
|
|
28
|
|
141
|
my($self,$xcoll,$pathdb,$recent_events) = @_; |
1270
|
28
|
|
|
|
|
123
|
my $success = $self->mirror_path([map {$_->{rev}{path}} @$xcoll]); |
|
1462
|
|
|
|
|
3148
|
|
1271
|
28
|
100
|
|
|
|
9448
|
if ($pathdb) { |
1272
|
18
|
|
|
|
|
410
|
$self->_mirror_register_path($pathdb,[map {$_->{rev}} @$xcoll],"rsync"); |
|
817
|
|
|
|
|
4237
|
|
1273
|
|
|
|
|
|
|
} |
1274
|
28
|
|
|
|
|
940
|
$self->done->register($recent_events, [map {$_->{i}} @$xcoll]); |
|
1462
|
|
|
|
|
4735
|
|
1275
|
28
|
|
|
|
|
4475
|
@$xcoll = (); |
1276
|
28
|
|
|
|
|
319
|
return $success; |
1277
|
|
|
|
|
|
|
} |
1278
|
|
|
|
|
|
|
|
1279
|
|
|
|
|
|
|
sub _mirror_register_path { |
1280
|
27
|
|
|
27
|
|
387
|
my($self,$pathdb,$coll,$activity) = @_; |
1281
|
27
|
|
|
|
|
255
|
my $time = time; |
1282
|
27
|
|
|
|
|
309
|
for my $item (@$coll) { |
1283
|
|
|
|
|
|
|
$pathdb->{$item->{path}} = |
1284
|
|
|
|
|
|
|
{ |
1285
|
|
|
|
|
|
|
recentepoch => $item->{epoch}, |
1286
|
826
|
|
|
|
|
16869
|
($activity."_on") => $time, |
1287
|
|
|
|
|
|
|
}; |
1288
|
|
|
|
|
|
|
} |
1289
|
|
|
|
|
|
|
} |
1290
|
|
|
|
|
|
|
|
1291
|
|
|
|
|
|
|
sub _mirror_unhide_tempfile { |
1292
|
26
|
|
|
26
|
|
172
|
my($self, $trecentfile) = @_; |
1293
|
26
|
|
|
|
|
220
|
my $rfile = $self->rfile; |
1294
|
26
|
50
|
|
|
|
2430
|
if (rename $trecentfile, $rfile) { |
1295
|
|
|
|
|
|
|
# warn "DEBUG: renamed '$trecentfile' to '$rfile'"; |
1296
|
|
|
|
|
|
|
} else { |
1297
|
0
|
|
|
|
|
0
|
require Carp; |
1298
|
0
|
|
|
|
|
0
|
Carp::confess("Could not rename '$trecentfile' to '$rfile': $!"); |
1299
|
|
|
|
|
|
|
} |
1300
|
26
|
|
|
|
|
439
|
$self->_use_tempfile (0); |
1301
|
26
|
100
|
|
|
|
347
|
if (my $ctfh = $self->_current_tempfile_fh) { |
1302
|
10
|
|
|
|
|
412
|
$ctfh->unlink_on_destroy (0); |
1303
|
10
|
|
|
|
|
370
|
$self->_current_tempfile_fh (undef); |
1304
|
|
|
|
|
|
|
} |
1305
|
|
|
|
|
|
|
} |
1306
|
|
|
|
|
|
|
|
1307
|
|
|
|
|
|
|
sub _mirror_perform_delayed_ops { |
1308
|
26
|
|
|
26
|
|
1523
|
my($self,$options) = @_; |
1309
|
26
|
|
|
|
|
318
|
my $delayed = $self->delayed_operations; |
1310
|
26
|
|
|
|
|
78
|
for my $dst (keys %{$delayed->{unlink}}) { |
|
26
|
|
|
|
|
457
|
|
1311
|
30
|
100
|
|
|
|
1280
|
unless (unlink $dst) { |
1312
|
15
|
|
|
|
|
120
|
require Carp; |
1313
|
15
|
50
|
|
|
|
85
|
Carp::cluck ( "Warning: Error while unlinking '$dst': $!" ) if $options->{verbose}; |
1314
|
|
|
|
|
|
|
} |
1315
|
30
|
50
|
|
|
|
155
|
if ($self->verbose) { |
1316
|
0
|
|
|
|
|
0
|
my $doing = "Del"; |
1317
|
0
|
|
|
|
|
0
|
my $LFH = $self->_logfilehandle; |
1318
|
0
|
|
|
|
|
0
|
printf $LFH |
1319
|
|
|
|
|
|
|
( |
1320
|
|
|
|
|
|
|
"%-4s %d (%s) %s DONE\n", |
1321
|
|
|
|
|
|
|
$doing, |
1322
|
|
|
|
|
|
|
time, |
1323
|
|
|
|
|
|
|
$self->interval, |
1324
|
|
|
|
|
|
|
$dst, |
1325
|
|
|
|
|
|
|
); |
1326
|
0
|
|
|
|
|
0
|
delete $delayed->{unlink}{$dst}; |
1327
|
|
|
|
|
|
|
} |
1328
|
|
|
|
|
|
|
} |
1329
|
26
|
|
|
|
|
238
|
for my $dst (sort {length($b) <=> length($a)} keys %{$delayed->{rmdir}}) { |
|
0
|
|
|
|
|
0
|
|
|
26
|
|
|
|
|
245
|
|
1330
|
0
|
0
|
|
|
|
0
|
unless (rmdir $dst) { |
1331
|
0
|
|
|
|
|
0
|
require Carp; |
1332
|
0
|
0
|
|
|
|
0
|
Carp::cluck ( "Warning: Error on rmdir '$dst': $!" ) if $options->{verbose}; |
1333
|
|
|
|
|
|
|
} |
1334
|
0
|
0
|
|
|
|
0
|
if ($self->verbose) { |
1335
|
0
|
|
|
|
|
0
|
my $doing = "Del"; |
1336
|
0
|
|
|
|
|
0
|
my $LFH = $self->_logfilehandle; |
1337
|
0
|
|
|
|
|
0
|
printf $LFH |
1338
|
|
|
|
|
|
|
( |
1339
|
|
|
|
|
|
|
"%-4s %d (%s) %s DONE\n", |
1340
|
|
|
|
|
|
|
$doing, |
1341
|
|
|
|
|
|
|
time, |
1342
|
|
|
|
|
|
|
$self->interval, |
1343
|
|
|
|
|
|
|
$dst, |
1344
|
|
|
|
|
|
|
); |
1345
|
0
|
|
|
|
|
0
|
delete $delayed->{rmdir}{$dst}; |
1346
|
|
|
|
|
|
|
} |
1347
|
|
|
|
|
|
|
} |
1348
|
|
|
|
|
|
|
} |
1349
|
|
|
|
|
|
|
|
1350
|
|
|
|
|
|
|
=head2 $success = $obj->mirror_path ( $arrref | $path ) |
1351
|
|
|
|
|
|
|
|
1352
|
|
|
|
|
|
|
If the argument is a scalar it is treated as a path. The remote path |
1353
|
|
|
|
|
|
|
is mirrored into the local copy. $path is the path found in the |
1354
|
|
|
|
|
|
|
I, i.e. it is relative to the root directory of the |
1355
|
|
|
|
|
|
|
mirror. |
1356
|
|
|
|
|
|
|
|
1357
|
|
|
|
|
|
|
If the argument is an array reference then all elements are treated as |
1358
|
|
|
|
|
|
|
a path below the current tree and all are rsynced with a single |
1359
|
|
|
|
|
|
|
command (and a single connection). |
1360
|
|
|
|
|
|
|
|
1361
|
|
|
|
|
|
|
=cut |
1362
|
|
|
|
|
|
|
|
1363
|
|
|
|
|
|
|
sub mirror_path { |
1364
|
28
|
|
|
28
|
1
|
136
|
my($self,$path) = @_; |
1365
|
|
|
|
|
|
|
# XXX simplify the two branches such that $path is treated as |
1366
|
|
|
|
|
|
|
# [$path] maybe even demand the argument as an arrayref to |
1367
|
|
|
|
|
|
|
# simplify docs and code. (rsync-over-recentfile-2.pl uses the |
1368
|
|
|
|
|
|
|
# interface) |
1369
|
28
|
50
|
33
|
|
|
543
|
if (ref $path and ref $path eq "ARRAY") { |
1370
|
28
|
|
|
|
|
132
|
my $dst = $self->localroot; |
1371
|
28
|
|
|
|
|
4412
|
mkpath dirname $dst; |
1372
|
28
|
|
|
|
|
353
|
my($fh) = File::Temp->new(TEMPLATE => sprintf(".%s-XXXX", |
1373
|
|
|
|
|
|
|
lc $self->filenameroot, |
1374
|
|
|
|
|
|
|
), |
1375
|
|
|
|
|
|
|
TMPDIR => 1, |
1376
|
|
|
|
|
|
|
UNLINK => 0, |
1377
|
|
|
|
|
|
|
); |
1378
|
28
|
|
|
|
|
23978
|
for my $p (@$path) { |
1379
|
1462
|
|
|
|
|
3726
|
print $fh $p, "\n"; |
1380
|
|
|
|
|
|
|
} |
1381
|
28
|
|
|
|
|
1535
|
$fh->flush; |
1382
|
28
|
|
|
|
|
197
|
$fh->unlink_on_destroy(1); |
1383
|
28
|
|
|
|
|
418
|
my $gaveup = 0; |
1384
|
28
|
|
|
|
|
63
|
my $retried = 0; |
1385
|
28
|
|
|
|
|
495
|
local($ENV{LANG}) = "C"; |
1386
|
28
|
|
|
|
|
229
|
while (!$self->rsync->exec |
1387
|
|
|
|
|
|
|
( |
1388
|
|
|
|
|
|
|
src => join("/", |
1389
|
|
|
|
|
|
|
$self->remoteroot, |
1390
|
|
|
|
|
|
|
), |
1391
|
|
|
|
|
|
|
dst => $dst, |
1392
|
|
|
|
|
|
|
'files-from' => $fh->filename, |
1393
|
|
|
|
|
|
|
)) { |
1394
|
0
|
|
|
|
|
0
|
my(@err) = $self->rsync->err; |
1395
|
0
|
0
|
0
|
|
|
0
|
if ($self->_my_ignore_link_stat_errors && "@err" =~ m{^ rsync: \s link_stat }x ) { |
1396
|
0
|
0
|
|
|
|
0
|
if ($self->verbose) { |
1397
|
0
|
|
|
|
|
0
|
my $LFH = $self->_logfilehandle; |
1398
|
0
|
|
|
|
|
0
|
print $LFH "Info: ignoring link_stat error '@err'"; |
1399
|
|
|
|
|
|
|
} |
1400
|
0
|
|
|
|
|
0
|
return 1; |
1401
|
|
|
|
|
|
|
} |
1402
|
0
|
|
|
|
|
0
|
$self->register_rsync_error (@err); |
1403
|
0
|
0
|
|
|
|
0
|
if (++$retried >= 3) { |
1404
|
0
|
|
|
|
|
0
|
my $batchsize = @$path; |
1405
|
0
|
|
|
|
|
0
|
warn "The number of rsync retries now reached 3 within a batch of size $batchsize. Error was '@err'. Giving up now, will retry later, "; |
1406
|
0
|
|
|
|
|
0
|
$gaveup = 1; |
1407
|
0
|
|
|
|
|
0
|
last; |
1408
|
|
|
|
|
|
|
} |
1409
|
0
|
|
|
|
|
0
|
sleep 1; |
1410
|
|
|
|
|
|
|
} |
1411
|
28
|
50
|
|
|
|
1797644
|
unless ($gaveup) { |
1412
|
28
|
|
|
|
|
1237
|
$self->un_register_rsync_error (); |
1413
|
|
|
|
|
|
|
} |
1414
|
|
|
|
|
|
|
} else { |
1415
|
0
|
|
|
|
|
0
|
my $dst = $self->local_path($path); |
1416
|
0
|
|
|
|
|
0
|
mkpath dirname $dst; |
1417
|
0
|
|
|
|
|
0
|
local($ENV{LANG}) = "C"; |
1418
|
0
|
|
|
|
|
0
|
while (!$self->rsync->exec |
1419
|
|
|
|
|
|
|
( |
1420
|
|
|
|
|
|
|
src => join("/", |
1421
|
|
|
|
|
|
|
$self->remoteroot, |
1422
|
|
|
|
|
|
|
$path |
1423
|
|
|
|
|
|
|
), |
1424
|
|
|
|
|
|
|
dst => $dst, |
1425
|
|
|
|
|
|
|
)) { |
1426
|
0
|
|
|
|
|
0
|
my(@err) = $self->rsync->err; |
1427
|
0
|
0
|
0
|
|
|
0
|
if ($self->_my_ignore_link_stat_errors && "@err" =~ m{^ rsync: \s link_stat }x ) { |
1428
|
0
|
0
|
|
|
|
0
|
if ($self->verbose) { |
1429
|
0
|
|
|
|
|
0
|
my $LFH = $self->_logfilehandle; |
1430
|
0
|
|
|
|
|
0
|
print $LFH "Info: ignoring link_stat error '@err'"; |
1431
|
|
|
|
|
|
|
} |
1432
|
0
|
|
|
|
|
0
|
return 1; |
1433
|
|
|
|
|
|
|
} |
1434
|
0
|
|
|
|
|
0
|
$self->register_rsync_error (@err); |
1435
|
|
|
|
|
|
|
} |
1436
|
0
|
|
|
|
|
0
|
$self->un_register_rsync_error (); |
1437
|
|
|
|
|
|
|
} |
1438
|
28
|
|
|
|
|
26014
|
return 1; |
1439
|
|
|
|
|
|
|
} |
1440
|
|
|
|
|
|
|
|
1441
|
|
|
|
|
|
|
sub _my_ignore_link_stat_errors { |
1442
|
0
|
|
|
0
|
|
0
|
my($self) = @_; |
1443
|
0
|
|
|
|
|
0
|
my $x = $self->ignore_link_stat_errors; |
1444
|
0
|
0
|
|
|
|
0
|
$x = 1 unless defined $x; |
1445
|
0
|
|
|
|
|
0
|
return $x; |
1446
|
|
|
|
|
|
|
} |
1447
|
|
|
|
|
|
|
|
1448
|
|
|
|
|
|
|
sub _my_current_rfile { |
1449
|
6619
|
|
|
6619
|
|
13489
|
my($self) = @_; |
1450
|
6619
|
|
|
|
|
8866
|
my $rfile; |
1451
|
6619
|
100
|
|
|
|
13883
|
if ($self->_use_tempfile) { |
1452
|
33
|
|
|
|
|
274
|
$rfile = $self->_current_tempfile; |
1453
|
|
|
|
|
|
|
} |
1454
|
6619
|
100
|
66
|
|
|
36214
|
unless ($rfile && -s $rfile) { |
1455
|
6586
|
|
|
|
|
13293
|
$rfile = $self->rfile; |
1456
|
|
|
|
|
|
|
} |
1457
|
6619
|
|
|
|
|
17825
|
return $rfile; |
1458
|
|
|
|
|
|
|
} |
1459
|
|
|
|
|
|
|
|
1460
|
|
|
|
|
|
|
=head2 $path = $obj->naive_path_normalize ($path) |
1461
|
|
|
|
|
|
|
|
1462
|
|
|
|
|
|
|
Takes an absolute unix style path as argument and canonicalizes it to |
1463
|
|
|
|
|
|
|
a shorter path if possible, removing things like double slashes or |
1464
|
|
|
|
|
|
|
C and removes references to C<../> directories to get a shorter |
1465
|
|
|
|
|
|
|
unambiguos path. This is used to make the code easier that determines |
1466
|
|
|
|
|
|
|
if a file passed to C is indeed below our C. |
1467
|
|
|
|
|
|
|
|
1468
|
|
|
|
|
|
|
=cut |
1469
|
|
|
|
|
|
|
|
1470
|
|
|
|
|
|
|
sub naive_path_normalize { |
1471
|
1292
|
|
|
1292
|
1
|
2836
|
my($self,$path) = @_; |
1472
|
1292
|
|
|
|
|
12090
|
$path =~ s|/+|/|g; |
1473
|
1292
|
|
|
|
|
5114
|
1 while $path =~ s|/[^/]+/\.\./|/|; |
1474
|
1292
|
|
|
|
|
2335
|
$path =~ s|/$||; |
1475
|
1292
|
|
|
|
|
2932
|
$path; |
1476
|
|
|
|
|
|
|
} |
1477
|
|
|
|
|
|
|
|
1478
|
|
|
|
|
|
|
=head2 $ret = $obj->read_recent_1 ( $data ) |
1479
|
|
|
|
|
|
|
|
1480
|
|
|
|
|
|
|
Delegate of C on protocol 1 |
1481
|
|
|
|
|
|
|
|
1482
|
|
|
|
|
|
|
=cut |
1483
|
|
|
|
|
|
|
|
1484
|
|
|
|
|
|
|
sub read_recent_1 { |
1485
|
6487
|
|
|
6487
|
1
|
12276
|
my($self, $data) = @_; |
1486
|
6487
|
|
|
|
|
12089
|
return $data->{recent}; |
1487
|
|
|
|
|
|
|
} |
1488
|
|
|
|
|
|
|
|
1489
|
|
|
|
|
|
|
=head2 $array_ref = $obj->recent_events ( %options ) |
1490
|
|
|
|
|
|
|
|
1491
|
|
|
|
|
|
|
Note: the code relies on the resource being written atomically. We |
1492
|
|
|
|
|
|
|
cannot lock because we may have no write access. If the caller has |
1493
|
|
|
|
|
|
|
write access (eg. aggregate() or update()), it has to care for any |
1494
|
|
|
|
|
|
|
necessary locking and it MUST write atomically. |
1495
|
|
|
|
|
|
|
|
1496
|
|
|
|
|
|
|
If C<$options{after}> is specified, only file events after this |
1497
|
|
|
|
|
|
|
timestamp are returned. |
1498
|
|
|
|
|
|
|
|
1499
|
|
|
|
|
|
|
If C<$options{before}> is specified, only file events before this |
1500
|
|
|
|
|
|
|
timestamp are returned. |
1501
|
|
|
|
|
|
|
|
1502
|
|
|
|
|
|
|
If C<$options{max}> is specified only a maximum of this many most |
1503
|
|
|
|
|
|
|
recent events is returned. |
1504
|
|
|
|
|
|
|
|
1505
|
|
|
|
|
|
|
If C<$options{'skip-deletes'}> is specified, no files-to-be-deleted |
1506
|
|
|
|
|
|
|
will be returned. |
1507
|
|
|
|
|
|
|
|
1508
|
|
|
|
|
|
|
If C<$options{contains}> is specified the value must be a hash |
1509
|
|
|
|
|
|
|
reference containing a query. The query may contain the keys C, |
1510
|
|
|
|
|
|
|
C, and C. Each represents a condition that must be met. If |
1511
|
|
|
|
|
|
|
there is more than one such key, the conditions are ANDed. |
1512
|
|
|
|
|
|
|
|
1513
|
|
|
|
|
|
|
If C<$options{info}> is specified, it must be a hashref. This hashref |
1514
|
|
|
|
|
|
|
will be filled with metadata about the unfiltered recent_events of |
1515
|
|
|
|
|
|
|
this object, in key C there is the first item, in key C |
1516
|
|
|
|
|
|
|
is the last. |
1517
|
|
|
|
|
|
|
|
1518
|
|
|
|
|
|
|
=cut |
1519
|
|
|
|
|
|
|
|
1520
|
|
|
|
|
|
|
sub recent_events { |
1521
|
6597
|
|
|
6597
|
1
|
161572
|
my ($self, %options) = @_; |
1522
|
6597
|
|
|
|
|
14268
|
my $info = $options{info}; |
1523
|
6597
|
100
|
|
|
|
18360
|
if ($self->is_slave) { |
1524
|
|
|
|
|
|
|
# XXX seems dubious, might produce tempfiles without removing them? |
1525
|
37
|
|
|
|
|
588
|
$self->get_remote_recentfile_as_tempfile; |
1526
|
|
|
|
|
|
|
} |
1527
|
6597
|
50
|
|
|
|
36504
|
my $rfile_or_tempfile = $self->_my_current_rfile or return []; |
1528
|
6597
|
100
|
|
|
|
124287
|
-e $rfile_or_tempfile or return []; |
1529
|
6487
|
|
|
|
|
30938
|
my $suffix = $self->serializer_suffix; |
1530
|
6487
|
|
|
|
|
40904
|
my ($data) = eval { |
1531
|
6487
|
|
|
|
|
17788
|
$self->_try_deserialize |
1532
|
|
|
|
|
|
|
( |
1533
|
|
|
|
|
|
|
$suffix, |
1534
|
|
|
|
|
|
|
$rfile_or_tempfile, |
1535
|
|
|
|
|
|
|
); |
1536
|
|
|
|
|
|
|
}; |
1537
|
6487
|
|
|
|
|
5224750
|
my $err = $@; |
1538
|
6487
|
50
|
33
|
|
|
38015
|
if ($err or !$data) { |
1539
|
0
|
|
|
|
|
0
|
return []; |
1540
|
|
|
|
|
|
|
} |
1541
|
6487
|
|
|
|
|
11520
|
my $re; |
1542
|
6487
|
50
|
|
|
|
26907
|
if (reftype $data eq 'ARRAY') { # protocol 0 |
1543
|
0
|
|
|
|
|
0
|
$re = $data; |
1544
|
|
|
|
|
|
|
} else { |
1545
|
6487
|
|
|
|
|
19685
|
$re = $self->_recent_events_protocol_x |
1546
|
|
|
|
|
|
|
( |
1547
|
|
|
|
|
|
|
$data, |
1548
|
|
|
|
|
|
|
$rfile_or_tempfile, |
1549
|
|
|
|
|
|
|
); |
1550
|
|
|
|
|
|
|
} |
1551
|
6487
|
100
|
|
|
|
13666
|
return $re unless grep {defined $options{$_}} qw(after before contains max skip-deletes); |
|
32435
|
|
|
|
|
92811
|
|
1552
|
2005
|
|
|
|
|
6410
|
$self->_recent_events_handle_options ($re, \%options); |
1553
|
|
|
|
|
|
|
} |
1554
|
|
|
|
|
|
|
|
1555
|
|
|
|
|
|
|
# File::Rsync::Mirror::Recentfile::_recent_events_handle_options |
1556
|
|
|
|
|
|
|
sub _recent_events_handle_options { |
1557
|
2005
|
|
|
2005
|
|
4115
|
my($self, $re, $options) = @_; |
1558
|
2005
|
|
|
|
|
3515
|
my $last_item = $#$re; |
1559
|
2005
|
|
|
|
|
4115
|
my $info = $options->{info}; |
1560
|
2005
|
100
|
|
|
|
4205
|
if ($info) { |
1561
|
2000
|
|
|
|
|
5150
|
$info->{first} = $re->[0]; |
1562
|
2000
|
|
|
|
|
3915
|
$info->{last} = $re->[-1]; |
1563
|
|
|
|
|
|
|
} |
1564
|
2005
|
100
|
|
|
|
3950
|
if (defined $options->{after}) { |
1565
|
5
|
50
|
|
|
|
170
|
if ($re->[0]{epoch} > $options->{after}) { |
1566
|
5
|
50
|
|
|
|
225
|
if ( |
1567
|
|
|
|
|
|
|
my $f = first |
1568
|
125
|
|
|
125
|
|
340
|
{$re->[$_]{epoch} <= $options->{after}} |
1569
|
|
|
|
|
|
|
0..$#$re |
1570
|
|
|
|
|
|
|
) { |
1571
|
5
|
|
|
|
|
70
|
$last_item = $f-1; |
1572
|
|
|
|
|
|
|
} |
1573
|
|
|
|
|
|
|
} else { |
1574
|
0
|
|
|
|
|
0
|
$last_item = -1; |
1575
|
|
|
|
|
|
|
} |
1576
|
|
|
|
|
|
|
} |
1577
|
2005
|
|
|
|
|
2880
|
my $first_item = 0; |
1578
|
2005
|
100
|
|
|
|
4155
|
if (defined $options->{before}) { |
1579
|
2000
|
100
|
|
|
|
10000
|
if ($re->[0]{epoch} > $options->{before}) { |
1580
|
1855
|
100
|
|
|
|
19285
|
if ( |
1581
|
|
|
|
|
|
|
my $f = first |
1582
|
144815
|
|
|
144815
|
|
263095
|
{$re->[$_]{epoch} < $options->{before}} |
1583
|
|
|
|
|
|
|
0..$last_item |
1584
|
|
|
|
|
|
|
) { |
1585
|
570
|
|
|
|
|
1760
|
$first_item = $f; |
1586
|
|
|
|
|
|
|
} |
1587
|
|
|
|
|
|
|
} else { |
1588
|
145
|
|
|
|
|
605
|
$first_item = 0; |
1589
|
|
|
|
|
|
|
} |
1590
|
|
|
|
|
|
|
} |
1591
|
2005
|
50
|
66
|
|
|
19040
|
if (0 != $first_item || -1 != $last_item) { |
1592
|
2005
|
|
|
|
|
14370
|
@$re = splice @$re, $first_item, 1+$last_item-$first_item; |
1593
|
|
|
|
|
|
|
} |
1594
|
2005
|
50
|
|
|
|
5855
|
if ($options->{'skip-deletes'}) { |
1595
|
0
|
|
|
|
|
0
|
@$re = grep { $_->{type} ne "delete" } @$re; |
|
0
|
|
|
|
|
0
|
|
1596
|
|
|
|
|
|
|
} |
1597
|
2005
|
50
|
|
|
|
4795
|
if (my $contopt = $options->{contains}) { |
1598
|
0
|
|
|
|
|
0
|
my $seen_allowed = 0; |
1599
|
0
|
|
|
|
|
0
|
for my $allow (qw(epoch path type)) { |
1600
|
0
|
0
|
|
|
|
0
|
if (exists $contopt->{$allow}) { |
1601
|
0
|
|
|
|
|
0
|
$seen_allowed++; |
1602
|
0
|
|
|
|
|
0
|
my $v = $contopt->{$allow}; |
1603
|
0
|
|
|
|
|
0
|
@$re = grep { $_->{$allow} eq $v } @$re; |
|
0
|
|
|
|
|
0
|
|
1604
|
|
|
|
|
|
|
} |
1605
|
|
|
|
|
|
|
} |
1606
|
0
|
0
|
|
|
|
0
|
if (keys %$contopt > $seen_allowed) { |
1607
|
0
|
|
|
|
|
0
|
require Carp; |
1608
|
0
|
|
|
|
|
0
|
Carp::confess |
1609
|
|
|
|
|
|
|
(sprintf "unknown query: %s", join ", ", %$contopt); |
1610
|
|
|
|
|
|
|
} |
1611
|
|
|
|
|
|
|
} |
1612
|
2005
|
50
|
33
|
|
|
5360
|
if ($options->{max} && @$re > $options->{max}) { |
1613
|
0
|
|
|
|
|
0
|
@$re = splice @$re, 0, $options->{max}; |
1614
|
|
|
|
|
|
|
} |
1615
|
2005
|
|
|
|
|
15480
|
$re; |
1616
|
|
|
|
|
|
|
} |
1617
|
|
|
|
|
|
|
|
1618
|
|
|
|
|
|
|
sub _recent_events_protocol_x { |
1619
|
6487
|
|
|
6487
|
|
13978
|
my($self, |
1620
|
|
|
|
|
|
|
$data, |
1621
|
|
|
|
|
|
|
$rfile_or_tempfile, |
1622
|
|
|
|
|
|
|
) = @_; |
1623
|
6487
|
|
|
|
|
36229
|
my $meth = sprintf "read_recent_%d", $data->{meta}{protocol}; |
1624
|
|
|
|
|
|
|
# we may be reading meta for the first time |
1625
|
6487
|
|
|
|
|
10935
|
while (my($k,$v) = each %{$data->{meta}}) { |
|
68455
|
|
|
|
|
336998
|
|
1626
|
61968
|
100
|
|
|
|
115161
|
if ($k ne lc $k){ # "Producers" |
1627
|
6487
|
|
|
|
|
19241
|
$self->{ORIG}{$k} = $v; |
1628
|
6487
|
|
|
|
|
14719
|
next; |
1629
|
|
|
|
|
|
|
} |
1630
|
55481
|
100
|
|
|
|
125690
|
next if defined $self->$k; |
1631
|
10460
|
|
|
|
|
40664
|
$self->$k($v); |
1632
|
|
|
|
|
|
|
} |
1633
|
6487
|
|
|
|
|
18428
|
my $re = $self->$meth ($data); |
1634
|
6487
|
|
|
|
|
8568
|
my $minmax; |
1635
|
6487
|
50
|
|
|
|
132351
|
if (my @stat = stat $rfile_or_tempfile) { |
1636
|
6487
|
|
|
|
|
31574
|
$minmax = { mtime => $stat[9] }; |
1637
|
|
|
|
|
|
|
} else { |
1638
|
|
|
|
|
|
|
# defensive because ABH encountered: |
1639
|
|
|
|
|
|
|
|
1640
|
|
|
|
|
|
|
#### Sync 1239828608 (1/1/Z) temp .../authors/.FRMRecent-RECENT-Z.yaml- |
1641
|
|
|
|
|
|
|
#### Ydr_.yaml ... DONE |
1642
|
|
|
|
|
|
|
#### Cannot stat '/mirrors/CPAN/authors/.FRMRecent-RECENT-Z.yaml- |
1643
|
|
|
|
|
|
|
#### Ydr_.yaml': No such file or directory at /usr/lib/perl5/site_perl/ |
1644
|
|
|
|
|
|
|
#### 5.8.8/File/Rsync/Mirror/Recentfile.pm line 1558. |
1645
|
|
|
|
|
|
|
#### unlink0: /mirrors/CPAN/authors/.FRMRecent-RECENT-Z.yaml-Ydr_.yaml is |
1646
|
|
|
|
|
|
|
#### gone already at cpan-pause.pl line 0 |
1647
|
|
|
|
|
|
|
|
1648
|
0
|
|
|
|
|
0
|
my $LFH = $self->_logfilehandle; |
1649
|
0
|
|
|
|
|
0
|
print $LFH "Warning (maybe harmless): Cannot stat '$rfile_or_tempfile': $!" |
1650
|
|
|
|
|
|
|
} |
1651
|
6487
|
50
|
|
|
|
16944
|
if (@$re) { |
1652
|
6487
|
|
|
|
|
15482
|
$minmax->{min} = $re->[-1]{epoch}; |
1653
|
6487
|
|
|
|
|
15694
|
$minmax->{max} = $re->[0]{epoch}; |
1654
|
|
|
|
|
|
|
} |
1655
|
6487
|
|
|
|
|
22772
|
$self->minmax ( $minmax ); |
1656
|
6487
|
|
|
|
|
40323
|
return $re; |
1657
|
|
|
|
|
|
|
} |
1658
|
|
|
|
|
|
|
|
1659
|
|
|
|
|
|
|
sub _try_deserialize { |
1660
|
6487
|
|
|
6487
|
|
17229
|
my($self, |
1661
|
|
|
|
|
|
|
$suffix, |
1662
|
|
|
|
|
|
|
$rfile_or_tempfile, |
1663
|
|
|
|
|
|
|
) = @_; |
1664
|
6487
|
50
|
|
|
|
17230
|
if ($suffix eq ".yaml") { |
|
|
0
|
|
|
|
|
|
1665
|
6487
|
|
|
|
|
41436
|
require YAML::Syck; |
1666
|
6487
|
|
|
|
|
22806
|
YAML::Syck::LoadFile($rfile_or_tempfile); |
1667
|
|
|
|
|
|
|
} elsif ($HAVE->{"Data::Serializer"}) { |
1668
|
|
|
|
|
|
|
my $serializer = Data::Serializer->new |
1669
|
0
|
|
|
|
|
0
|
( serializer => $serializers{$suffix} ); |
1670
|
|
|
|
|
|
|
my $serialized = do |
1671
|
0
|
|
|
|
|
0
|
{ |
1672
|
0
|
0
|
|
|
|
0
|
open my $fh, $rfile_or_tempfile or die "Could not open: $!"; |
1673
|
0
|
|
|
|
|
0
|
local $/; |
1674
|
0
|
|
|
|
|
0
|
<$fh>; |
1675
|
|
|
|
|
|
|
}; |
1676
|
0
|
|
|
|
|
0
|
$serializer->raw_deserialize($serialized); |
1677
|
|
|
|
|
|
|
} else { |
1678
|
0
|
|
|
|
|
0
|
die "Data::Serializer not installed, cannot proceed with suffix '$suffix'"; |
1679
|
|
|
|
|
|
|
} |
1680
|
|
|
|
|
|
|
} |
1681
|
|
|
|
|
|
|
|
1682
|
|
|
|
|
|
|
sub _refresh_internals { |
1683
|
41
|
|
|
41
|
|
1308
|
my($self, $dst) = @_; |
1684
|
41
|
|
|
|
|
1015
|
my $class = ref $self; |
1685
|
41
|
|
|
|
|
2858
|
my $rfpeek = $class->new_from_file ($dst); |
1686
|
41
|
|
|
|
|
536
|
for my $acc (qw( |
1687
|
|
|
|
|
|
|
_merged |
1688
|
|
|
|
|
|
|
minmax |
1689
|
|
|
|
|
|
|
)) { |
1690
|
82
|
|
|
|
|
1372
|
$self->$acc ( $rfpeek->$acc ); |
1691
|
|
|
|
|
|
|
} |
1692
|
41
|
|
|
|
|
727
|
my $old_dirtymark = $self->dirtymark; |
1693
|
41
|
|
|
|
|
687
|
my $new_dirtymark = $rfpeek->dirtymark; |
1694
|
41
|
100
|
66
|
|
|
1656
|
if ($old_dirtymark && $new_dirtymark && $new_dirtymark ne $old_dirtymark) { |
|
|
|
100
|
|
|
|
|
1695
|
5
|
|
|
|
|
120
|
$self->done->reset; |
1696
|
5
|
|
|
|
|
73
|
$self->dirtymark ( $new_dirtymark ); |
1697
|
5
|
|
|
|
|
125
|
$self->_uptodateness_ever_reached(0); |
1698
|
5
|
|
|
|
|
151
|
$self->seed; |
1699
|
|
|
|
|
|
|
} |
1700
|
|
|
|
|
|
|
} |
1701
|
|
|
|
|
|
|
|
1702
|
|
|
|
|
|
|
=head2 $ret = $obj->rfilename |
1703
|
|
|
|
|
|
|
|
1704
|
|
|
|
|
|
|
Just the basename of our I, composed from C, |
1705
|
|
|
|
|
|
|
a dash, C, and C. E.g. C |
1706
|
|
|
|
|
|
|
|
1707
|
|
|
|
|
|
|
=cut |
1708
|
|
|
|
|
|
|
|
1709
|
|
|
|
|
|
|
sub rfilename { |
1710
|
6682
|
|
|
6682
|
1
|
31023
|
my($self) = @_; |
1711
|
6682
|
|
|
|
|
15744
|
my $file = sprintf("%s-%s%s", |
1712
|
|
|
|
|
|
|
$self->filenameroot, |
1713
|
|
|
|
|
|
|
$self->interval, |
1714
|
|
|
|
|
|
|
$self->serializer_suffix, |
1715
|
|
|
|
|
|
|
); |
1716
|
6682
|
|
|
|
|
107592
|
return $file; |
1717
|
|
|
|
|
|
|
} |
1718
|
|
|
|
|
|
|
|
1719
|
|
|
|
|
|
|
=head2 $str = $self->remote_dir |
1720
|
|
|
|
|
|
|
|
1721
|
|
|
|
|
|
|
The directory we are mirroring from. |
1722
|
|
|
|
|
|
|
|
1723
|
|
|
|
|
|
|
=cut |
1724
|
|
|
|
|
|
|
|
1725
|
|
|
|
|
|
|
sub remote_dir { |
1726
|
15
|
|
|
15
|
1
|
90
|
my($self, $set) = @_; |
1727
|
15
|
100
|
|
|
|
40
|
if (defined $set) { |
1728
|
5
|
|
|
|
|
130
|
$self->_remote_dir ($set); |
1729
|
|
|
|
|
|
|
} |
1730
|
15
|
|
|
|
|
55
|
my $x = $self->_remote_dir; |
1731
|
15
|
|
|
|
|
80
|
$self->is_slave (1); |
1732
|
15
|
|
|
|
|
80
|
return $x; |
1733
|
|
|
|
|
|
|
} |
1734
|
|
|
|
|
|
|
|
1735
|
|
|
|
|
|
|
=head2 $str = $obj->remoteroot |
1736
|
|
|
|
|
|
|
|
1737
|
|
|
|
|
|
|
=head2 (void) $obj->remoteroot ( $set ) |
1738
|
|
|
|
|
|
|
|
1739
|
|
|
|
|
|
|
Get/Set the composed prefix needed when rsyncing from a remote module. |
1740
|
|
|
|
|
|
|
If remote_host, remote_module, and remote_dir are set, it is composed |
1741
|
|
|
|
|
|
|
from these. |
1742
|
|
|
|
|
|
|
|
1743
|
|
|
|
|
|
|
=cut |
1744
|
|
|
|
|
|
|
|
1745
|
|
|
|
|
|
|
sub remoteroot { |
1746
|
83
|
|
|
83
|
1
|
720
|
my($self, $set) = @_; |
1747
|
83
|
100
|
|
|
|
819
|
if (defined $set) { |
1748
|
14
|
|
|
|
|
74
|
$self->_remoteroot($set); |
1749
|
|
|
|
|
|
|
} |
1750
|
83
|
|
|
|
|
864
|
my $remoteroot = $self->_remoteroot; |
1751
|
83
|
100
|
|
|
|
1483
|
unless (defined $remoteroot) { |
1752
|
5
|
50
|
|
|
|
20
|
$remoteroot = sprintf |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
1753
|
|
|
|
|
|
|
( |
1754
|
|
|
|
|
|
|
"%s%s%s", |
1755
|
|
|
|
|
|
|
defined $self->remote_host ? ($self->remote_host."::") : "", |
1756
|
|
|
|
|
|
|
defined $self->remote_module ? ($self->remote_module."/") : "", |
1757
|
|
|
|
|
|
|
defined $self->remote_dir ? $self->remote_dir : "", |
1758
|
|
|
|
|
|
|
); |
1759
|
5
|
|
|
|
|
25
|
$self->_remoteroot($remoteroot); |
1760
|
|
|
|
|
|
|
} |
1761
|
83
|
|
|
|
|
1020
|
return $remoteroot; |
1762
|
|
|
|
|
|
|
} |
1763
|
|
|
|
|
|
|
|
1764
|
|
|
|
|
|
|
=head2 (void) $obj->split_rfilename ( $recentfilename ) |
1765
|
|
|
|
|
|
|
|
1766
|
|
|
|
|
|
|
Inverse method to C. C<$recentfilename> is a plain filename |
1767
|
|
|
|
|
|
|
of the pattern |
1768
|
|
|
|
|
|
|
|
1769
|
|
|
|
|
|
|
$filenameroot-$interval$serializer_suffix |
1770
|
|
|
|
|
|
|
|
1771
|
|
|
|
|
|
|
e.g. |
1772
|
|
|
|
|
|
|
|
1773
|
|
|
|
|
|
|
RECENT-1M.yaml |
1774
|
|
|
|
|
|
|
|
1775
|
|
|
|
|
|
|
This filename is split into its parts and the parts are fed to the |
1776
|
|
|
|
|
|
|
object itself. |
1777
|
|
|
|
|
|
|
|
1778
|
|
|
|
|
|
|
=cut |
1779
|
|
|
|
|
|
|
|
1780
|
|
|
|
|
|
|
sub split_rfilename { |
1781
|
5
|
|
|
5
|
1
|
40
|
my($self, $rfname) = @_; |
1782
|
5
|
|
|
|
|
75
|
my($splitter) = qr(^(.+)-([^-\.]+)(\.[^\.]+)); |
1783
|
5
|
50
|
|
|
|
110
|
if (my($f,$i,$s) = $rfname =~ $splitter) { |
1784
|
5
|
|
|
|
|
50
|
$self->filenameroot ($f); |
1785
|
5
|
|
|
|
|
35
|
$self->interval ($i); |
1786
|
5
|
|
|
|
|
40
|
$self->serializer_suffix ($s); |
1787
|
|
|
|
|
|
|
} else { |
1788
|
0
|
|
|
|
|
0
|
die "Alert: cannot split '$rfname', doesn't match '$splitter'"; |
1789
|
|
|
|
|
|
|
} |
1790
|
5
|
|
|
|
|
75
|
return; |
1791
|
|
|
|
|
|
|
} |
1792
|
|
|
|
|
|
|
|
1793
|
|
|
|
|
|
|
=head2 my $rfile = $obj->rfile |
1794
|
|
|
|
|
|
|
|
1795
|
|
|
|
|
|
|
Returns the full path of the I |
1796
|
|
|
|
|
|
|
|
1797
|
|
|
|
|
|
|
=cut |
1798
|
|
|
|
|
|
|
|
1799
|
|
|
|
|
|
|
sub rfile { |
1800
|
16101
|
|
|
16101
|
1
|
24705
|
my($self) = @_; |
1801
|
16101
|
|
|
|
|
36342
|
my $rfile = $self->_rfile; |
1802
|
16101
|
100
|
|
|
|
78516
|
return $rfile if defined $rfile; |
1803
|
4980
|
|
|
|
|
11545
|
$rfile = File::Spec->catfile |
1804
|
|
|
|
|
|
|
($self->localroot, |
1805
|
|
|
|
|
|
|
$self->rfilename, |
1806
|
|
|
|
|
|
|
); |
1807
|
4980
|
|
|
|
|
21358
|
$self->_rfile ($rfile); |
1808
|
4980
|
|
|
|
|
21496
|
return $rfile; |
1809
|
|
|
|
|
|
|
} |
1810
|
|
|
|
|
|
|
|
1811
|
|
|
|
|
|
|
=head2 $rsync_obj = $obj->rsync |
1812
|
|
|
|
|
|
|
|
1813
|
|
|
|
|
|
|
The File::Rsync object that this object uses for communicating with an |
1814
|
|
|
|
|
|
|
upstream server. |
1815
|
|
|
|
|
|
|
|
1816
|
|
|
|
|
|
|
=cut |
1817
|
|
|
|
|
|
|
|
1818
|
|
|
|
|
|
|
sub rsync { |
1819
|
69
|
|
|
69
|
1
|
449
|
my($self) = @_; |
1820
|
69
|
|
|
|
|
854
|
my $rsync = $self->_rsync; |
1821
|
69
|
100
|
|
|
|
1003
|
unless (defined $rsync) { |
1822
|
26
|
|
50
|
|
|
325
|
my $rsync_options = $self->rsync_options || {}; |
1823
|
26
|
50
|
|
|
|
300
|
if ($HAVE->{"File::Rsync"}) { |
1824
|
26
|
|
|
|
|
849
|
$rsync = File::Rsync->new($rsync_options); |
1825
|
26
|
|
|
|
|
79319
|
$self->_rsync($rsync); |
1826
|
|
|
|
|
|
|
} else { |
1827
|
0
|
|
|
|
|
0
|
die "File::Rsync required for rsync operations. Cannot continue"; |
1828
|
|
|
|
|
|
|
} |
1829
|
|
|
|
|
|
|
} |
1830
|
69
|
|
|
|
|
961
|
return $rsync; |
1831
|
|
|
|
|
|
|
} |
1832
|
|
|
|
|
|
|
|
1833
|
|
|
|
|
|
|
=head2 (void) $obj->register_rsync_error(@err) |
1834
|
|
|
|
|
|
|
|
1835
|
|
|
|
|
|
|
=head2 (void) $obj->un_register_rsync_error() |
1836
|
|
|
|
|
|
|
|
1837
|
|
|
|
|
|
|
Register_rsync_error is called whenever the File::Rsync object fails |
1838
|
|
|
|
|
|
|
on an exec (say, connection doesn't succeed). It issues a warning and |
1839
|
|
|
|
|
|
|
sleeps for an increasing amount of time. Un_register_rsync_error |
1840
|
|
|
|
|
|
|
resets the error count. See also accessor C. |
1841
|
|
|
|
|
|
|
|
1842
|
|
|
|
|
|
|
=cut |
1843
|
|
|
|
|
|
|
|
1844
|
|
|
|
|
|
|
{ |
1845
|
|
|
|
|
|
|
my $no_success_count = 0; |
1846
|
|
|
|
|
|
|
my $no_success_time = 0; |
1847
|
|
|
|
|
|
|
sub register_rsync_error { |
1848
|
0
|
|
|
0
|
1
|
0
|
my($self, @err) = @_; |
1849
|
0
|
|
|
|
|
0
|
chomp @err; |
1850
|
0
|
|
|
|
|
0
|
$no_success_time = time; |
1851
|
0
|
|
|
|
|
0
|
$no_success_count++; |
1852
|
0
|
|
|
|
|
0
|
my $max_rsync_errors = $self->max_rsync_errors; |
1853
|
0
|
0
|
|
|
|
0
|
$max_rsync_errors = MAX_INT unless defined $max_rsync_errors; |
1854
|
0
|
0
|
0
|
|
|
0
|
if ($max_rsync_errors>=0 && $no_success_count >= $max_rsync_errors) { |
1855
|
0
|
|
|
|
|
0
|
require Carp; |
1856
|
0
|
|
|
|
|
0
|
Carp::confess |
1857
|
|
|
|
|
|
|
( |
1858
|
|
|
|
|
|
|
sprintf |
1859
|
|
|
|
|
|
|
( |
1860
|
|
|
|
|
|
|
"Alert: Error while rsyncing (%s): '%s', error count: %d, exiting now,", |
1861
|
|
|
|
|
|
|
$self->interval, |
1862
|
|
|
|
|
|
|
join(" ",@err), |
1863
|
|
|
|
|
|
|
$no_success_count, |
1864
|
|
|
|
|
|
|
)); |
1865
|
|
|
|
|
|
|
} |
1866
|
0
|
|
|
|
|
0
|
my $sleep = 12 * $no_success_count; |
1867
|
0
|
0
|
|
|
|
0
|
$sleep = 300 if $sleep > 300; |
1868
|
0
|
|
|
|
|
0
|
require Carp; |
1869
|
0
|
|
|
|
|
0
|
Carp::cluck |
1870
|
|
|
|
|
|
|
(sprintf |
1871
|
|
|
|
|
|
|
( |
1872
|
|
|
|
|
|
|
"Warning: %s, Error while rsyncing (%s): '%s', sleeping %d", |
1873
|
|
|
|
|
|
|
scalar(localtime($no_success_time)), |
1874
|
|
|
|
|
|
|
$self->interval, |
1875
|
|
|
|
|
|
|
join(" ",@err), |
1876
|
|
|
|
|
|
|
$sleep, |
1877
|
|
|
|
|
|
|
)); |
1878
|
0
|
|
|
|
|
0
|
sleep $sleep |
1879
|
|
|
|
|
|
|
} |
1880
|
|
|
|
|
|
|
sub un_register_rsync_error { |
1881
|
69
|
|
|
69
|
1
|
746
|
my($self) = @_; |
1882
|
69
|
|
|
|
|
688
|
$no_success_time = 0; |
1883
|
69
|
|
|
|
|
3566
|
$no_success_count = 0; |
1884
|
|
|
|
|
|
|
} |
1885
|
|
|
|
|
|
|
} |
1886
|
|
|
|
|
|
|
|
1887
|
|
|
|
|
|
|
=head2 $clone = $obj->_sparse_clone |
1888
|
|
|
|
|
|
|
|
1889
|
|
|
|
|
|
|
Clones just as much from itself that it does not hurt. Experimental |
1890
|
|
|
|
|
|
|
method. |
1891
|
|
|
|
|
|
|
|
1892
|
|
|
|
|
|
|
Note: what fits better: sparse or shallow? Other suggestions? |
1893
|
|
|
|
|
|
|
|
1894
|
|
|
|
|
|
|
=cut |
1895
|
|
|
|
|
|
|
|
1896
|
|
|
|
|
|
|
sub _sparse_clone { |
1897
|
3226
|
|
|
3226
|
|
6484
|
my($self) = @_; |
1898
|
3226
|
|
|
|
|
13334
|
my $new = bless {}, ref $self; |
1899
|
3226
|
|
|
|
|
7520
|
for my $m (qw( |
1900
|
|
|
|
|
|
|
_interval |
1901
|
|
|
|
|
|
|
_localroot |
1902
|
|
|
|
|
|
|
_remoteroot |
1903
|
|
|
|
|
|
|
_rfile |
1904
|
|
|
|
|
|
|
_use_tempfile |
1905
|
|
|
|
|
|
|
aggregator |
1906
|
|
|
|
|
|
|
filenameroot |
1907
|
|
|
|
|
|
|
ignore_link_stat_errors |
1908
|
|
|
|
|
|
|
is_slave |
1909
|
|
|
|
|
|
|
max_files_per_connection |
1910
|
|
|
|
|
|
|
protocol |
1911
|
|
|
|
|
|
|
rsync_options |
1912
|
|
|
|
|
|
|
serializer_suffix |
1913
|
|
|
|
|
|
|
sleep_per_connection |
1914
|
|
|
|
|
|
|
tempdir |
1915
|
|
|
|
|
|
|
verbose |
1916
|
|
|
|
|
|
|
)) { |
1917
|
51616
|
|
|
|
|
218331
|
my $o = $self->$m; |
1918
|
51616
|
100
|
|
|
|
307385
|
$o = Storable::dclone $o if ref $o; |
1919
|
51616
|
|
|
|
|
98124
|
$new->$m($o); |
1920
|
|
|
|
|
|
|
} |
1921
|
3226
|
|
|
|
|
18080
|
$new; |
1922
|
|
|
|
|
|
|
} |
1923
|
|
|
|
|
|
|
|
1924
|
|
|
|
|
|
|
=head2 $boolean = OBJ->ttl_reached () |
1925
|
|
|
|
|
|
|
|
1926
|
|
|
|
|
|
|
=cut |
1927
|
|
|
|
|
|
|
|
1928
|
|
|
|
|
|
|
sub ttl_reached { |
1929
|
43
|
|
|
43
|
1
|
175
|
my($self) = @_; |
1930
|
43
|
|
100
|
|
|
348
|
my $have_mirrored = $self->have_mirrored || 0; |
1931
|
43
|
|
|
|
|
798
|
my $now = Time::HiRes::time; |
1932
|
43
|
|
|
|
|
279
|
my $ttl = $self->ttl; |
1933
|
43
|
50
|
|
|
|
486
|
$ttl = 24.2 unless defined $ttl; |
1934
|
43
|
100
|
|
|
|
576
|
if ($now > $have_mirrored + $ttl) { |
1935
|
10
|
|
|
|
|
105
|
return 1; |
1936
|
|
|
|
|
|
|
} |
1937
|
33
|
|
|
|
|
308
|
return 0; |
1938
|
|
|
|
|
|
|
} |
1939
|
|
|
|
|
|
|
|
1940
|
|
|
|
|
|
|
=head2 (void) $obj->unlock() |
1941
|
|
|
|
|
|
|
|
1942
|
|
|
|
|
|
|
Unlocking is implemented with an C on a locking directory |
1943
|
|
|
|
|
|
|
(C<.lock> appended to $rfile). |
1944
|
|
|
|
|
|
|
|
1945
|
|
|
|
|
|
|
=cut |
1946
|
|
|
|
|
|
|
|
1947
|
|
|
|
|
|
|
sub unlock { |
1948
|
8286
|
|
|
8286
|
1
|
18565
|
my($self) = @_; |
1949
|
8286
|
100
|
|
|
|
21627
|
return unless $self->_is_locked; |
1950
|
3088
|
|
|
|
|
16563
|
my $rfile = $self->rfile; |
1951
|
3088
|
50
|
|
|
|
146287
|
unlink "$rfile.lock/process" or warn "Could not unlink lockfile '$rfile.lock/process': $!"; |
1952
|
3088
|
50
|
|
|
|
118055
|
rmdir "$rfile.lock" or warn "Could not rmdir lockdir '$rfile.lock': $!";; |
1953
|
3088
|
|
|
|
|
18186
|
$self->_is_locked (0); |
1954
|
|
|
|
|
|
|
} |
1955
|
|
|
|
|
|
|
|
1956
|
|
|
|
|
|
|
=head2 unseed |
1957
|
|
|
|
|
|
|
|
1958
|
|
|
|
|
|
|
Sets this recentfile in the state of not 'seeded'. |
1959
|
|
|
|
|
|
|
|
1960
|
|
|
|
|
|
|
=cut |
1961
|
|
|
|
|
|
|
sub unseed { |
1962
|
67
|
|
|
67
|
1
|
434
|
my($self) = @_; |
1963
|
67
|
|
|
|
|
936
|
$self->seeded(0); |
1964
|
|
|
|
|
|
|
} |
1965
|
|
|
|
|
|
|
|
1966
|
|
|
|
|
|
|
=head2 $ret = $obj->update ($path, $type) |
1967
|
|
|
|
|
|
|
|
1968
|
|
|
|
|
|
|
=head2 $ret = $obj->update ($path, "new", $dirty_epoch) |
1969
|
|
|
|
|
|
|
|
1970
|
|
|
|
|
|
|
=head2 $ret = $obj->update () |
1971
|
|
|
|
|
|
|
|
1972
|
|
|
|
|
|
|
Enter one file into the local I. $path is the (usually |
1973
|
|
|
|
|
|
|
absolute) path. If the path is outside I tree, then it is |
1974
|
|
|
|
|
|
|
ignored. |
1975
|
|
|
|
|
|
|
|
1976
|
|
|
|
|
|
|
C<$type> is one of C or C. |
1977
|
|
|
|
|
|
|
|
1978
|
|
|
|
|
|
|
Events of type C may set $dirty_epoch. $dirty_epoch is normally |
1979
|
|
|
|
|
|
|
not used and the epoch is calculated by the update() routine itself |
1980
|
|
|
|
|
|
|
based on current time. But if there is the demand to insert a |
1981
|
|
|
|
|
|
|
not-so-current file into the dataset, then the caller sets |
1982
|
|
|
|
|
|
|
$dirty_epoch. This causes the epoch of the registered event to become |
1983
|
|
|
|
|
|
|
$dirty_epoch or -- if the exact value given is already taken -- a tiny |
1984
|
|
|
|
|
|
|
bit more. As compensation the dirtymark of the whole dataset is set to |
1985
|
|
|
|
|
|
|
now or the current epoch, whichever is higher. Note: setting the |
1986
|
|
|
|
|
|
|
dirty_epoch to the future is prohibited as it's very unlikely to be |
1987
|
|
|
|
|
|
|
intended: it definitely might wreak havoc with the index files. |
1988
|
|
|
|
|
|
|
|
1989
|
|
|
|
|
|
|
The new file event is unshifted (or, if dirty_epoch is set, inserted |
1990
|
|
|
|
|
|
|
at the place it belongs to, according to the rule to have a sequence |
1991
|
|
|
|
|
|
|
of strictly decreasing timestamps) to the array of recent_events and |
1992
|
|
|
|
|
|
|
the array is shortened to the length of the timespan allowed. This is |
1993
|
|
|
|
|
|
|
usually the timespan specified by the interval of this recentfile but |
1994
|
|
|
|
|
|
|
as long as this recentfile has not been merged to another one, the |
1995
|
|
|
|
|
|
|
timespan may grow without bounds. |
1996
|
|
|
|
|
|
|
|
1997
|
|
|
|
|
|
|
The third form runs an update without inserting a new file. This may |
1998
|
|
|
|
|
|
|
be desired to truncate a recentfile. |
1999
|
|
|
|
|
|
|
|
2000
|
|
|
|
|
|
|
=cut |
2001
|
|
|
|
|
|
|
sub _epoch_monotonically_increasing { |
2002
|
1614
|
|
|
1614
|
|
3772
|
my($self,$epoch,$recent) = @_; |
2003
|
1614
|
100
|
|
|
|
3540
|
return $epoch unless @$recent; # the first one goes unoffended |
2004
|
1579
|
100
|
|
|
|
18003
|
if (_bigfloatgt("".$epoch,$recent->[0]{epoch})) { |
2005
|
1414
|
|
|
|
|
4256
|
return $epoch; |
2006
|
|
|
|
|
|
|
} else { |
2007
|
165
|
|
|
|
|
535
|
return _increase_a_bit($recent->[0]{epoch}); |
2008
|
|
|
|
|
|
|
} |
2009
|
|
|
|
|
|
|
} |
2010
|
|
|
|
|
|
|
sub update { |
2011
|
1646
|
|
|
1646
|
1
|
380517
|
my($self,$path,$type,$dirty_epoch) = @_; |
2012
|
1646
|
50
|
66
|
|
|
7556
|
if (defined $path or defined $type or defined $dirty_epoch) { |
|
|
|
66
|
|
|
|
|
2013
|
1292
|
50
|
|
|
|
4622
|
die "update called without path argument" unless defined $path; |
2014
|
1292
|
50
|
|
|
|
3586
|
die "update called without type argument" unless defined $type; |
2015
|
1292
|
50
|
|
|
|
17492
|
die "update called with illegal type argument: $type" unless $type =~ /(new|delete)/; |
2016
|
|
|
|
|
|
|
} |
2017
|
1646
|
|
|
|
|
7269
|
$self->lock; |
2018
|
1646
|
|
|
|
|
28389
|
my $ctx = $self->_locked_batch_update([{path=>$path,type=>$type,epoch=>$dirty_epoch}]); |
2019
|
1646
|
100
|
|
|
|
10478
|
$self->write_recent($ctx->{recent}) if $ctx->{something_done}; |
2020
|
1646
|
|
|
|
|
9886
|
$self->_assert_symlink; |
2021
|
1646
|
|
|
|
|
6168
|
$self->unlock; |
2022
|
|
|
|
|
|
|
} |
2023
|
|
|
|
|
|
|
|
2024
|
|
|
|
|
|
|
=head2 $obj->batch_update($batch) |
2025
|
|
|
|
|
|
|
|
2026
|
|
|
|
|
|
|
Like update but for many files. $batch is an arrayref containing |
2027
|
|
|
|
|
|
|
hashrefs with the structure |
2028
|
|
|
|
|
|
|
|
2029
|
|
|
|
|
|
|
{ |
2030
|
|
|
|
|
|
|
path => $path, |
2031
|
|
|
|
|
|
|
type => $type, |
2032
|
|
|
|
|
|
|
epoch => $epoch, |
2033
|
|
|
|
|
|
|
} |
2034
|
|
|
|
|
|
|
|
2035
|
|
|
|
|
|
|
|
2036
|
|
|
|
|
|
|
|
2037
|
|
|
|
|
|
|
=cut |
2038
|
|
|
|
|
|
|
sub batch_update { |
2039
|
0
|
|
|
0
|
1
|
0
|
my($self,$batch) = @_; |
2040
|
0
|
|
|
|
|
0
|
$self->lock; |
2041
|
0
|
|
|
|
|
0
|
my $ctx = $self->_locked_batch_update($batch); |
2042
|
0
|
0
|
|
|
|
0
|
$self->write_recent($ctx->{recent}) if $ctx->{something_done}; |
2043
|
0
|
|
|
|
|
0
|
$self->_assert_symlink; |
2044
|
0
|
|
|
|
|
0
|
$self->unlock; |
2045
|
|
|
|
|
|
|
} |
2046
|
|
|
|
|
|
|
sub _locked_batch_update { |
2047
|
1646
|
|
|
1646
|
|
5141
|
my($self,$batch) = @_; |
2048
|
1646
|
|
|
|
|
2935
|
my $something_done = 0; |
2049
|
1646
|
|
|
|
|
6182
|
my $recent = $self->recent_events; |
2050
|
1646
|
100
|
|
|
|
6358
|
unless ($recent->[0]) { |
2051
|
|
|
|
|
|
|
# obstetrics |
2052
|
35
|
|
|
|
|
75
|
$something_done = 1; |
2053
|
|
|
|
|
|
|
} |
2054
|
1646
|
|
|
|
|
4141
|
my %paths_in_recent = map { $_->{path} => undef } @$recent; |
|
61737
|
|
|
|
|
116459
|
|
2055
|
1646
|
|
|
|
|
6962
|
my $interval = $self->interval; |
2056
|
1646
|
|
|
|
|
4121
|
my $canonmeth = $self->canonize; |
2057
|
1646
|
100
|
|
|
|
7785
|
unless ($canonmeth) { |
2058
|
390
|
|
|
|
|
670
|
$canonmeth = "naive_path_normalize"; |
2059
|
|
|
|
|
|
|
} |
2060
|
1646
|
|
|
|
|
3251
|
my $oldest_allowed = 0; |
2061
|
1646
|
|
|
|
|
2333
|
my $setting_new_dirty_mark = 0; |
2062
|
1646
|
|
|
|
|
2362
|
my $console; |
2063
|
1646
|
50
|
66
|
|
|
4357
|
if ($self->verbose && @$batch > 1) { |
2064
|
0
|
|
|
|
|
0
|
eval {require Time::Progress}; |
|
0
|
|
|
|
|
0
|
|
2065
|
0
|
0
|
|
|
|
0
|
warn "dollarat[$@]" if $@; |
2066
|
0
|
|
|
|
|
0
|
$| = 1; |
2067
|
0
|
|
|
|
|
0
|
$console = new Time::Progress; |
2068
|
0
|
|
|
|
|
0
|
$console->attr( min => 1, max => scalar @$batch ); |
2069
|
0
|
|
|
|
|
0
|
print "\n"; |
2070
|
|
|
|
|
|
|
} |
2071
|
1646
|
|
|
|
|
8608
|
my $i = 0; |
2072
|
1646
|
|
|
|
|
2596
|
my $memo_splicepos; |
2073
|
1646
|
|
0
|
|
|
6270
|
ITEM: for my $item (sort {($b->{epoch}||0) <=> ($a->{epoch}||0)} @$batch) { |
|
0
|
|
0
|
|
|
0
|
|
2074
|
1646
|
|
|
|
|
2978
|
$i++; |
2075
|
1646
|
50
|
33
|
|
|
5041
|
print $console->report( "\rdone %p elapsed: %L (%l sec), ETA %E (%e sec)", $i ) if $console and not $i % 50; |
2076
|
1646
|
|
|
|
|
5966
|
my $ctx = $self->_update_batch_item($item,$canonmeth,$recent,$setting_new_dirty_mark,$oldest_allowed,$something_done,\%paths_in_recent,$memo_splicepos); |
2077
|
1646
|
|
|
|
|
3829
|
$something_done = $ctx->{something_done}; |
2078
|
1646
|
|
|
|
|
2602
|
$oldest_allowed = $ctx->{oldest_allowed}; |
2079
|
1646
|
|
|
|
|
2466
|
$setting_new_dirty_mark = $ctx->{setting_new_dirty_mark}; |
2080
|
1646
|
|
|
|
|
3636
|
$recent = $ctx->{recent}; |
2081
|
1646
|
|
|
|
|
5037
|
$memo_splicepos = $ctx->{memo_splicepos}; |
2082
|
|
|
|
|
|
|
} |
2083
|
1646
|
50
|
|
|
|
3639
|
print "\n" if $console; |
2084
|
1646
|
100
|
|
|
|
3575
|
if ($setting_new_dirty_mark) { |
2085
|
32
|
|
|
|
|
54
|
$oldest_allowed = 0; |
2086
|
|
|
|
|
|
|
} |
2087
|
1646
|
|
|
|
|
3651
|
TRUNCATE: while (@$recent) { |
2088
|
2650
|
100
|
|
|
|
7424
|
if (_bigfloatlt($recent->[-1]{epoch}, $oldest_allowed)) { |
2089
|
1004
|
|
|
|
|
1560
|
pop @$recent; |
2090
|
1004
|
|
|
|
|
2532
|
$something_done = 1; |
2091
|
|
|
|
|
|
|
} else { |
2092
|
1646
|
|
|
|
|
4032
|
last TRUNCATE; |
2093
|
|
|
|
|
|
|
} |
2094
|
|
|
|
|
|
|
} |
2095
|
1646
|
|
|
|
|
14232
|
return {something_done=>$something_done,recent=>$recent}; |
2096
|
|
|
|
|
|
|
} |
2097
|
|
|
|
|
|
|
sub _update_batch_item { |
2098
|
1646
|
|
|
1646
|
|
5616
|
my($self,$item,$canonmeth,$recent,$setting_new_dirty_mark,$oldest_allowed,$something_done,$paths_in_recent,$memo_splicepos) = @_; |
2099
|
1646
|
|
|
|
|
2851
|
my($path,$type,$dirty_epoch) = @{$item}{qw(path type epoch)}; |
|
1646
|
|
|
|
|
6251
|
|
2100
|
1646
|
50
|
66
|
|
|
7467
|
if (defined $path or defined $type or defined $dirty_epoch) { |
|
|
|
66
|
|
|
|
|
2101
|
1292
|
|
|
|
|
5962
|
$path = $self->$canonmeth($path); |
2102
|
|
|
|
|
|
|
} |
2103
|
|
|
|
|
|
|
# you must calculate the time after having locked, of course |
2104
|
1646
|
|
|
|
|
4851
|
my $now = Time::HiRes::time; |
2105
|
|
|
|
|
|
|
|
2106
|
1646
|
|
|
|
|
2066
|
my $epoch; |
2107
|
1646
|
100
|
66
|
|
|
6284
|
if (defined $dirty_epoch && _bigfloatgt($now,$dirty_epoch)) { |
2108
|
32
|
|
|
|
|
79
|
$epoch = $dirty_epoch; |
2109
|
|
|
|
|
|
|
} else { |
2110
|
1614
|
|
|
|
|
4844
|
$epoch = $self->_epoch_monotonically_increasing($now,$recent); |
2111
|
|
|
|
|
|
|
} |
2112
|
1646
|
|
50
|
|
|
5307
|
$recent ||= []; |
2113
|
1646
|
|
|
|
|
4303
|
my $merged = $self->merged; |
2114
|
1646
|
100
|
66
|
|
|
7703
|
if ($merged->{epoch} && !$setting_new_dirty_mark) { |
2115
|
877
|
|
|
|
|
3951
|
my $virtualnow = _bigfloatmax($now,$epoch); |
2116
|
|
|
|
|
|
|
# for the lower bound I think we need no big math, we calc already |
2117
|
877
|
|
|
|
|
3269
|
my $secs = $self->interval_secs(); |
2118
|
877
|
|
|
|
|
7089
|
$oldest_allowed = min($virtualnow - $secs, $merged->{epoch}, $epoch); |
2119
|
|
|
|
|
|
|
} else { |
2120
|
|
|
|
|
|
|
# as long as we are not merged at all, no limits! |
2121
|
|
|
|
|
|
|
} |
2122
|
1646
|
|
|
|
|
3756
|
my $lrd = $self->localroot; |
2123
|
1646
|
100
|
66
|
|
|
21695
|
if (defined $path && $path =~ s|^\Q$lrd\E||) { |
2124
|
1292
|
|
|
|
|
4844
|
$path =~ s|^/||; |
2125
|
1292
|
|
|
|
|
2133
|
my $splicepos; |
2126
|
|
|
|
|
|
|
# remove the older duplicates of this $path, irrespective of $type: |
2127
|
1292
|
100
|
|
|
|
2744
|
if (defined $dirty_epoch) { |
2128
|
32
|
|
|
|
|
156
|
my $ctx = $self->_update_with_dirty_epoch($path,$recent,$epoch,$paths_in_recent,$memo_splicepos); |
2129
|
32
|
|
|
|
|
91
|
$recent = $ctx->{recent}; |
2130
|
32
|
|
|
|
|
56
|
$splicepos = $ctx->{splicepos}; |
2131
|
32
|
|
|
|
|
89
|
$epoch = $ctx->{epoch}; |
2132
|
32
|
|
|
|
|
260
|
my $dirtymark = $self->dirtymark; |
2133
|
32
|
|
|
|
|
167
|
my $new_dm = $now; |
2134
|
32
|
50
|
|
|
|
88
|
if (_bigfloatgt($epoch, $now)) { # just in case we had to increase it |
2135
|
0
|
|
|
|
|
0
|
$new_dm = $epoch; |
2136
|
|
|
|
|
|
|
} |
2137
|
32
|
|
|
|
|
120
|
$self->dirtymark($new_dm); |
2138
|
32
|
|
|
|
|
396
|
$setting_new_dirty_mark = 1; |
2139
|
32
|
50
|
33
|
|
|
184
|
if (not defined $merged->{epoch} or _bigfloatlt($epoch,$merged->{epoch})) { |
2140
|
32
|
|
|
|
|
96
|
$self->merged(+{}); |
2141
|
|
|
|
|
|
|
} |
2142
|
|
|
|
|
|
|
} else { |
2143
|
1260
|
|
|
|
|
2524
|
$recent = [ grep { $_->{path} ne $path } @$recent ]; |
|
47041
|
|
|
|
|
74676
|
|
2144
|
1260
|
|
|
|
|
2318
|
$splicepos = 0; |
2145
|
|
|
|
|
|
|
} |
2146
|
1292
|
50
|
|
|
|
3155
|
if (defined $splicepos) { |
2147
|
1292
|
|
|
|
|
10485
|
splice @$recent, $splicepos, 0, { epoch => $epoch, path => $path, type => $type }; |
2148
|
1292
|
|
|
|
|
4201
|
$paths_in_recent->{$path} = undef; |
2149
|
|
|
|
|
|
|
} |
2150
|
1292
|
|
|
|
|
2318
|
$memo_splicepos = $splicepos; |
2151
|
1292
|
|
|
|
|
2143
|
$something_done = 1; |
2152
|
|
|
|
|
|
|
} |
2153
|
|
|
|
|
|
|
return |
2154
|
|
|
|
|
|
|
{ |
2155
|
1646
|
|
|
|
|
11291
|
something_done => $something_done, |
2156
|
|
|
|
|
|
|
oldest_allowed => $oldest_allowed, |
2157
|
|
|
|
|
|
|
setting_new_dirty_mark => $setting_new_dirty_mark, |
2158
|
|
|
|
|
|
|
recent => $recent, |
2159
|
|
|
|
|
|
|
memo_splicepos => $memo_splicepos, |
2160
|
|
|
|
|
|
|
} |
2161
|
|
|
|
|
|
|
} |
2162
|
|
|
|
|
|
|
sub _update_with_dirty_epoch { |
2163
|
32
|
|
|
32
|
|
117
|
my($self,$path,$recent,$epoch,$paths_in_recent,$memo_splicepos) = @_; |
2164
|
32
|
|
|
|
|
59
|
my $splicepos; |
2165
|
32
|
|
|
|
|
86
|
my $new_recent = []; |
2166
|
32
|
50
|
|
|
|
128
|
if (exists $paths_in_recent->{$path}) { |
2167
|
0
|
|
|
|
|
0
|
my $cancel = 0; |
2168
|
0
|
|
|
|
|
0
|
KNOWN_EVENT: for my $i (0..$#$recent) { |
2169
|
0
|
0
|
|
|
|
0
|
if ($recent->[$i]{path} eq $path) { |
2170
|
0
|
0
|
|
|
|
0
|
if ($recent->[$i]{epoch} eq $epoch) { |
2171
|
|
|
|
|
|
|
# nothing to do |
2172
|
0
|
|
|
|
|
0
|
$cancel = 1; |
2173
|
0
|
|
|
|
|
0
|
last KNOWN_EVENT; |
2174
|
|
|
|
|
|
|
} |
2175
|
|
|
|
|
|
|
} else { |
2176
|
0
|
|
|
|
|
0
|
push @$new_recent, $recent->[$i]; |
2177
|
|
|
|
|
|
|
} |
2178
|
|
|
|
|
|
|
} |
2179
|
0
|
0
|
|
|
|
0
|
@$recent = @$new_recent unless $cancel; |
2180
|
|
|
|
|
|
|
} |
2181
|
32
|
50
|
33
|
|
|
214
|
if (!exists $recent->[0] or _bigfloatgt($epoch,$recent->[0]{epoch})) { |
|
|
50
|
|
|
|
|
|
2182
|
0
|
|
|
|
|
0
|
$splicepos = 0; |
2183
|
|
|
|
|
|
|
} elsif (_bigfloatlt($epoch,$recent->[-1]{epoch})) { |
2184
|
32
|
|
|
|
|
76
|
$splicepos = @$recent; |
2185
|
|
|
|
|
|
|
} else { |
2186
|
0
|
|
|
|
|
0
|
my $startingpoint; |
2187
|
0
|
0
|
0
|
|
|
0
|
if (_bigfloatgt($memo_splicepos<=$#$recent && $epoch, $recent->[$memo_splicepos]{epoch})) { |
2188
|
0
|
|
|
|
|
0
|
$startingpoint = 0; |
2189
|
|
|
|
|
|
|
} else { |
2190
|
0
|
|
|
|
|
0
|
$startingpoint = $memo_splicepos; |
2191
|
|
|
|
|
|
|
} |
2192
|
0
|
|
|
|
|
0
|
RECENT: for my $i ($startingpoint..$#$recent) { |
2193
|
0
|
|
|
|
|
0
|
my $ev = $recent->[$i]; |
2194
|
0
|
0
|
|
|
|
0
|
if ($epoch eq $recent->[$i]{epoch}) { |
2195
|
0
|
0
|
|
|
|
0
|
$epoch = _increase_a_bit($epoch, $i ? $recent->[$i-1]{epoch} : undef); |
2196
|
|
|
|
|
|
|
} |
2197
|
0
|
0
|
|
|
|
0
|
if (_bigfloatgt($epoch,$recent->[$i]{epoch})) { |
2198
|
0
|
|
|
|
|
0
|
$splicepos = $i; |
2199
|
0
|
|
|
|
|
0
|
last RECENT; |
2200
|
|
|
|
|
|
|
} |
2201
|
|
|
|
|
|
|
} |
2202
|
|
|
|
|
|
|
} |
2203
|
|
|
|
|
|
|
return { |
2204
|
32
|
|
|
|
|
216
|
recent => $recent, |
2205
|
|
|
|
|
|
|
splicepos => $splicepos, |
2206
|
|
|
|
|
|
|
epoch => $epoch, |
2207
|
|
|
|
|
|
|
} |
2208
|
|
|
|
|
|
|
} |
2209
|
|
|
|
|
|
|
|
2210
|
|
|
|
|
|
|
=head2 seed |
2211
|
|
|
|
|
|
|
|
2212
|
|
|
|
|
|
|
Sets this recentfile in the state of 'seeded' which means it has to |
2213
|
|
|
|
|
|
|
re-evaluate its uptodateness. |
2214
|
|
|
|
|
|
|
|
2215
|
|
|
|
|
|
|
=cut |
2216
|
|
|
|
|
|
|
sub seed { |
2217
|
28
|
|
|
28
|
1
|
204
|
my($self) = @_; |
2218
|
28
|
|
|
|
|
308
|
$self->seeded(1); |
2219
|
|
|
|
|
|
|
} |
2220
|
|
|
|
|
|
|
|
2221
|
|
|
|
|
|
|
=head2 seeded |
2222
|
|
|
|
|
|
|
|
2223
|
|
|
|
|
|
|
Tells if the recentfile is in the state 'seeded'. |
2224
|
|
|
|
|
|
|
|
2225
|
|
|
|
|
|
|
=cut |
2226
|
|
|
|
|
|
|
sub seeded { |
2227
|
134
|
|
|
134
|
1
|
1018
|
my($self, $set) = @_; |
2228
|
134
|
100
|
|
|
|
662
|
if (defined $set) { |
2229
|
95
|
|
|
|
|
1160
|
$self->_seeded ($set); |
2230
|
|
|
|
|
|
|
} |
2231
|
134
|
|
|
|
|
1445
|
my $x = $self->_seeded; |
2232
|
134
|
100
|
|
|
|
1107
|
unless (defined $x) { |
2233
|
8
|
|
|
|
|
40
|
$x = 0; |
2234
|
8
|
|
|
|
|
45
|
$self->_seeded ($x); |
2235
|
|
|
|
|
|
|
} |
2236
|
134
|
|
|
|
|
893
|
return $x; |
2237
|
|
|
|
|
|
|
} |
2238
|
|
|
|
|
|
|
|
2239
|
|
|
|
|
|
|
=head2 uptodate |
2240
|
|
|
|
|
|
|
|
2241
|
|
|
|
|
|
|
True if this object has mirrored the complete interval covered by the |
2242
|
|
|
|
|
|
|
current recentfile. |
2243
|
|
|
|
|
|
|
|
2244
|
|
|
|
|
|
|
=cut |
2245
|
|
|
|
|
|
|
sub uptodate { |
2246
|
56
|
|
|
56
|
1
|
397
|
my($self) = @_; |
2247
|
56
|
|
|
|
|
223
|
my $uptodate; |
2248
|
|
|
|
|
|
|
my $why; |
2249
|
56
|
100
|
66
|
|
|
588
|
if ($self->_uptodateness_ever_reached and not $self->seeded) { |
2250
|
19
|
|
|
|
|
197
|
$why = "saturated"; |
2251
|
19
|
|
|
|
|
112
|
$uptodate = 1; |
2252
|
|
|
|
|
|
|
} |
2253
|
|
|
|
|
|
|
# it's too easy to misconfigure ttl and related timings and then |
2254
|
|
|
|
|
|
|
# never reach uptodateness, so disabled 2009-03-22 |
2255
|
56
|
|
|
|
|
603
|
if (0 and not defined $uptodate) { |
2256
|
|
|
|
|
|
|
if ($self->ttl_reached){ |
2257
|
|
|
|
|
|
|
$why = "ttl_reached returned true, so we are not uptodate"; |
2258
|
|
|
|
|
|
|
$uptodate = 0 ; |
2259
|
|
|
|
|
|
|
} |
2260
|
|
|
|
|
|
|
} |
2261
|
56
|
100
|
|
|
|
441
|
unless (defined $uptodate) { |
2262
|
|
|
|
|
|
|
# look if recentfile has unchanged timestamp |
2263
|
37
|
|
|
|
|
319
|
my $minmax = $self->minmax; |
2264
|
37
|
100
|
|
|
|
500
|
if (exists $minmax->{mtime}) { |
2265
|
21
|
|
|
|
|
263
|
my $rfile = $self->_my_current_rfile; |
2266
|
21
|
|
|
|
|
548
|
my @stat = stat $rfile; |
2267
|
21
|
50
|
|
|
|
216
|
if (@stat) { |
2268
|
21
|
|
|
|
|
123
|
my $mtime = $stat[9]; |
2269
|
21
|
50
|
33
|
|
|
861
|
if (defined $mtime && defined $minmax->{mtime} && $mtime > $minmax->{mtime}) { |
|
|
|
33
|
|
|
|
|
2270
|
0
|
|
|
|
|
0
|
$why = "mtime[$mtime] of rfile[$rfile] > minmax/mtime[$minmax->{mtime}], so we are not uptodate"; |
2271
|
0
|
|
|
|
|
0
|
$uptodate = 0; |
2272
|
|
|
|
|
|
|
} else { |
2273
|
21
|
|
|
|
|
196
|
my $covered = $self->done->covered(@$minmax{qw(max min)}); |
2274
|
21
|
50
|
|
|
|
489
|
$why = sprintf "minmax covered[%s], so we return that", defined $covered ? $covered : "UNDEF"; |
2275
|
21
|
|
|
|
|
199
|
$uptodate = $covered; |
2276
|
|
|
|
|
|
|
} |
2277
|
|
|
|
|
|
|
} else { |
2278
|
0
|
|
|
|
|
0
|
require Carp; |
2279
|
0
|
|
|
|
|
0
|
$why = "Could not stat '$rfile': $!"; |
2280
|
0
|
|
|
|
|
0
|
Carp::cluck($why); |
2281
|
0
|
|
|
|
|
0
|
$uptodate = 0; |
2282
|
|
|
|
|
|
|
} |
2283
|
|
|
|
|
|
|
} |
2284
|
|
|
|
|
|
|
} |
2285
|
56
|
100
|
|
|
|
348
|
unless (defined $uptodate) { |
2286
|
16
|
|
|
|
|
57
|
$why = "fallthrough, so not uptodate"; |
2287
|
16
|
|
|
|
|
28
|
$uptodate = 0; |
2288
|
|
|
|
|
|
|
} |
2289
|
56
|
100
|
|
|
|
316
|
if ($uptodate) { |
2290
|
34
|
|
|
|
|
175
|
$self->_uptodateness_ever_reached(1); |
2291
|
|
|
|
|
|
|
} |
2292
|
56
|
|
|
|
|
625
|
my $remember = |
2293
|
|
|
|
|
|
|
{ |
2294
|
|
|
|
|
|
|
uptodate => $uptodate, |
2295
|
|
|
|
|
|
|
why => $why, |
2296
|
|
|
|
|
|
|
}; |
2297
|
56
|
|
|
|
|
412
|
$self->_remember_last_uptodate_call($remember); |
2298
|
56
|
|
|
|
|
897
|
return $uptodate; |
2299
|
|
|
|
|
|
|
} |
2300
|
|
|
|
|
|
|
|
2301
|
|
|
|
|
|
|
=head2 $obj->write_recent ($recent_files_arrayref) |
2302
|
|
|
|
|
|
|
|
2303
|
|
|
|
|
|
|
Writes a I based on the current reflection of the current |
2304
|
|
|
|
|
|
|
state of the tree limited by the current interval. |
2305
|
|
|
|
|
|
|
|
2306
|
|
|
|
|
|
|
=cut |
2307
|
|
|
|
|
|
|
sub _resort { |
2308
|
0
|
|
|
0
|
|
0
|
my($self) = @_; |
2309
|
0
|
|
|
|
|
0
|
@{$_[1]} = sort { _bigfloatcmp($b->{epoch},$a->{epoch}) } @{$_[1]}; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
2310
|
0
|
|
|
|
|
0
|
return; |
2311
|
|
|
|
|
|
|
} |
2312
|
|
|
|
|
|
|
sub write_recent { |
2313
|
2712
|
|
|
2712
|
1
|
6306
|
my ($self,$recent) = @_; |
2314
|
2712
|
50
|
|
|
|
5618
|
die "write_recent called without argument" unless defined $recent; |
2315
|
2712
|
|
|
|
|
3653
|
my $Last_epoch; |
2316
|
2712
|
|
|
|
|
8960
|
SANITYCHECK: for my $i (0..$#$recent) { |
2317
|
108413
|
50
|
66
|
|
|
240676
|
if (defined($Last_epoch) and _bigfloatge($recent->[$i]{epoch},$Last_epoch)) { |
2318
|
0
|
|
|
|
|
0
|
require Carp; |
2319
|
|
|
|
|
|
|
Carp::confess(sprintf "Warning: disorder '%s'>='%s', re-sorting %s\n", |
2320
|
0
|
|
|
|
|
0
|
$recent->[$i]{epoch}, $Last_epoch, $self->interval); |
2321
|
|
|
|
|
|
|
# you may want to: |
2322
|
|
|
|
|
|
|
# $self->_resort($recent); |
2323
|
|
|
|
|
|
|
# last SANITYCHECK; |
2324
|
|
|
|
|
|
|
} |
2325
|
108413
|
|
|
|
|
187700
|
$Last_epoch = $recent->[$i]{epoch}; |
2326
|
|
|
|
|
|
|
} |
2327
|
2712
|
|
|
|
|
7718
|
my $minmax = $self->minmax; |
2328
|
2712
|
100
|
100
|
|
|
18316
|
if (!defined $minmax->{max} || _bigfloatlt($minmax->{max},$recent->[0]{epoch})) { |
2329
|
1668
|
50
|
33
|
|
|
8825
|
$minmax->{max} = @$recent && exists $recent->[0]{epoch} ? $recent->[0]{epoch} : undef; |
2330
|
|
|
|
|
|
|
} |
2331
|
2712
|
100
|
100
|
|
|
11249
|
if (!defined $minmax->{min} || _bigfloatlt($minmax->{min},$recent->[-1]{epoch})) { |
2332
|
563
|
50
|
33
|
|
|
2900
|
$minmax->{min} = @$recent && exists $recent->[-1]{epoch} ? $recent->[-1]{epoch} : undef; |
2333
|
|
|
|
|
|
|
} |
2334
|
2712
|
|
|
|
|
8116
|
$self->minmax($minmax); |
2335
|
2712
|
|
|
|
|
12139
|
my $meth = sprintf "write_%d", $self->protocol; |
2336
|
2712
|
|
|
|
|
24667
|
$self->$meth($recent); |
2337
|
|
|
|
|
|
|
} |
2338
|
|
|
|
|
|
|
|
2339
|
|
|
|
|
|
|
=head2 $obj->write_0 ($recent_files_arrayref) |
2340
|
|
|
|
|
|
|
|
2341
|
|
|
|
|
|
|
Delegate of C on protocol 0 |
2342
|
|
|
|
|
|
|
|
2343
|
|
|
|
|
|
|
=cut |
2344
|
|
|
|
|
|
|
|
2345
|
|
|
|
|
|
|
sub write_0 { |
2346
|
0
|
|
|
0
|
1
|
0
|
my ($self,$recent) = @_; |
2347
|
0
|
|
|
|
|
0
|
my $rfile = $self->rfile; |
2348
|
0
|
|
|
|
|
0
|
YAML::Syck::DumpFile("$rfile.new",$recent); |
2349
|
0
|
0
|
|
|
|
0
|
rename "$rfile.new", $rfile or die "Could not rename to '$rfile': $!"; |
2350
|
|
|
|
|
|
|
} |
2351
|
|
|
|
|
|
|
|
2352
|
|
|
|
|
|
|
=head2 $obj->write_1 ($recent_files_arrayref) |
2353
|
|
|
|
|
|
|
|
2354
|
|
|
|
|
|
|
Delegate of C on protocol 1 |
2355
|
|
|
|
|
|
|
|
2356
|
|
|
|
|
|
|
=cut |
2357
|
|
|
|
|
|
|
|
2358
|
|
|
|
|
|
|
sub write_1 { |
2359
|
2712
|
|
|
2712
|
1
|
5834
|
my ($self,$recent) = @_; |
2360
|
2712
|
|
|
|
|
6848
|
my $rfile = $self->rfile; |
2361
|
2712
|
|
|
|
|
5930
|
my $suffix = $self->serializer_suffix; |
2362
|
2712
|
|
|
|
|
13577
|
my $data = { |
2363
|
|
|
|
|
|
|
meta => $self->meta_data, |
2364
|
|
|
|
|
|
|
recent => $recent, |
2365
|
|
|
|
|
|
|
}; |
2366
|
2712
|
|
|
|
|
4400
|
my $serialized; |
2367
|
2712
|
100
|
|
|
|
5756
|
if ($suffix eq ".yaml") { |
|
|
50
|
|
|
|
|
|
2368
|
2697
|
|
|
|
|
8781
|
$serialized = YAML::Syck::Dump($data); |
2369
|
|
|
|
|
|
|
} elsif ($HAVE->{"Data::Serializer"}) { |
2370
|
|
|
|
|
|
|
my $serializer = Data::Serializer->new |
2371
|
15
|
|
|
|
|
105
|
( serializer => $serializers{$suffix} ); |
2372
|
15
|
|
|
|
|
14640
|
$serialized = $serializer->raw_serialize($data); |
2373
|
|
|
|
|
|
|
} else { |
2374
|
0
|
|
|
|
|
0
|
die "Data::Serializer not installed, cannot proceed with suffix '$suffix'"; |
2375
|
|
|
|
|
|
|
} |
2376
|
2712
|
50
|
|
|
|
1177222
|
open my $fh, ">", "$rfile.new" or die "Could not open >'$rfile.new': $!"; |
2377
|
2712
|
|
|
|
|
78195
|
print $fh $serialized; |
2378
|
2712
|
50
|
|
|
|
87192
|
close $fh or die "Could not close '$rfile.new': $!"; |
2379
|
2712
|
50
|
|
|
|
341777
|
rename "$rfile.new", $rfile or die "Could not rename to '$rfile': $!"; |
2380
|
|
|
|
|
|
|
} |
2381
|
|
|
|
|
|
|
|
2382
|
|
|
|
|
|
|
BEGIN { |
2383
|
8
|
|
|
8
|
|
99902
|
my $nq = qr/[^"]+/; # non-quotes |
2384
|
8
|
|
|
|
|
108
|
my @pod_lines = |
2385
|
8
|
|
|
|
|
34
|
split /\n/, <<'=cut'; %serializers = map { my @x = /"($nq)"\s+=>\s+"($nq)"/; @x } grep {s/^=item\s+C<<\s+(.+)\s+>>$/$1/} @pod_lines; } |
|
32
|
|
|
|
|
444
|
|
|
32
|
|
|
|
|
1007
|
|
|
136
|
|
|
|
|
387
|
|
2386
|
|
|
|
|
|
|
|
2387
|
|
|
|
|
|
|
=head1 SERIALIZERS |
2388
|
|
|
|
|
|
|
|
2389
|
|
|
|
|
|
|
The following suffixes are supported and trigger the use of these |
2390
|
|
|
|
|
|
|
serializers: |
2391
|
|
|
|
|
|
|
|
2392
|
|
|
|
|
|
|
=over 4 |
2393
|
|
|
|
|
|
|
|
2394
|
|
|
|
|
|
|
=item C<< ".yaml" => "YAML::Syck" >> |
2395
|
|
|
|
|
|
|
|
2396
|
|
|
|
|
|
|
=item C<< ".json" => "JSON" >> |
2397
|
|
|
|
|
|
|
|
2398
|
|
|
|
|
|
|
=item C<< ".sto" => "Storable" >> |
2399
|
|
|
|
|
|
|
|
2400
|
|
|
|
|
|
|
=item C<< ".dd" => "Data::Dumper" >> |
2401
|
|
|
|
|
|
|
|
2402
|
|
|
|
|
|
|
=back |
2403
|
|
|
|
|
|
|
|
2404
|
|
|
|
|
|
|
=cut |
2405
|
|
|
|
|
|
|
|
2406
|
|
|
|
|
|
|
BEGIN { |
2407
|
8
|
|
|
8
|
|
152
|
my @pod_lines = |
2408
|
8
|
|
|
|
|
37
|
split /\n/, <<'=cut'; %seconds = map { eval } grep {s/^=item\s+C<<(.+)>>$/$1/} @pod_lines; } |
|
64
|
|
|
|
|
2557
|
|
|
240
|
|
|
|
|
519
|
|
2409
|
|
|
|
|
|
|
|
2410
|
|
|
|
|
|
|
=head1 INTERVAL SPEC |
2411
|
|
|
|
|
|
|
|
2412
|
|
|
|
|
|
|
An interval spec is a primitive way to express time spans. Normally it |
2413
|
|
|
|
|
|
|
is composed from an integer and a letter. |
2414
|
|
|
|
|
|
|
|
2415
|
|
|
|
|
|
|
As a special case, a string that consists only of the single letter |
2416
|
|
|
|
|
|
|
C, stands for MAX_INT seconds. |
2417
|
|
|
|
|
|
|
|
2418
|
|
|
|
|
|
|
The following letters express the specified number of seconds: |
2419
|
|
|
|
|
|
|
|
2420
|
|
|
|
|
|
|
=over 4 |
2421
|
|
|
|
|
|
|
|
2422
|
|
|
|
|
|
|
=item C<< s => 1 >> |
2423
|
|
|
|
|
|
|
|
2424
|
|
|
|
|
|
|
=item C<< m => 60 >> |
2425
|
|
|
|
|
|
|
|
2426
|
|
|
|
|
|
|
=item C<< h => 60*60 >> |
2427
|
|
|
|
|
|
|
|
2428
|
|
|
|
|
|
|
=item C<< d => 60*60*24 >> |
2429
|
|
|
|
|
|
|
|
2430
|
|
|
|
|
|
|
=item C<< W => 60*60*24*7 >> |
2431
|
|
|
|
|
|
|
|
2432
|
|
|
|
|
|
|
=item C<< M => 60*60*24*30 >> |
2433
|
|
|
|
|
|
|
|
2434
|
|
|
|
|
|
|
=item C<< Q => 60*60*24*90 >> |
2435
|
|
|
|
|
|
|
|
2436
|
|
|
|
|
|
|
=item C<< Y => 60*60*24*365.25 >> |
2437
|
|
|
|
|
|
|
|
2438
|
|
|
|
|
|
|
=back |
2439
|
|
|
|
|
|
|
|
2440
|
|
|
|
|
|
|
=cut |
2441
|
|
|
|
|
|
|
|
2442
|
|
|
|
|
|
|
=head1 SEE ALSO |
2443
|
|
|
|
|
|
|
|
2444
|
|
|
|
|
|
|
L, |
2445
|
|
|
|
|
|
|
L, |
2446
|
|
|
|
|
|
|
L |
2447
|
|
|
|
|
|
|
|
2448
|
|
|
|
|
|
|
=head1 BUGS |
2449
|
|
|
|
|
|
|
|
2450
|
|
|
|
|
|
|
Please report any bugs or feature requests through the web interface |
2451
|
|
|
|
|
|
|
at |
2452
|
|
|
|
|
|
|
L. |
2453
|
|
|
|
|
|
|
I will be notified, and then you'll automatically be notified of |
2454
|
|
|
|
|
|
|
progress on your bug as I make changes. |
2455
|
|
|
|
|
|
|
|
2456
|
|
|
|
|
|
|
=head1 KNOWN BUGS |
2457
|
|
|
|
|
|
|
|
2458
|
|
|
|
|
|
|
Memory hungry: it seems all memory is allocated during the initial |
2459
|
|
|
|
|
|
|
rsync where a list of all files is maintained in memory. |
2460
|
|
|
|
|
|
|
|
2461
|
|
|
|
|
|
|
=head1 SUPPORT |
2462
|
|
|
|
|
|
|
|
2463
|
|
|
|
|
|
|
You can find documentation for this module with the perldoc command. |
2464
|
|
|
|
|
|
|
|
2465
|
|
|
|
|
|
|
perldoc File::Rsync::Mirror::Recentfile |
2466
|
|
|
|
|
|
|
|
2467
|
|
|
|
|
|
|
You can also look for information at: |
2468
|
|
|
|
|
|
|
|
2469
|
|
|
|
|
|
|
=over 4 |
2470
|
|
|
|
|
|
|
|
2471
|
|
|
|
|
|
|
=item * RT: CPAN's request tracker |
2472
|
|
|
|
|
|
|
|
2473
|
|
|
|
|
|
|
L |
2474
|
|
|
|
|
|
|
|
2475
|
|
|
|
|
|
|
=item * AnnoCPAN: Annotated CPAN documentation |
2476
|
|
|
|
|
|
|
|
2477
|
|
|
|
|
|
|
L |
2478
|
|
|
|
|
|
|
|
2479
|
|
|
|
|
|
|
=item * CPAN Ratings |
2480
|
|
|
|
|
|
|
|
2481
|
|
|
|
|
|
|
L |
2482
|
|
|
|
|
|
|
|
2483
|
|
|
|
|
|
|
=item * Search CPAN |
2484
|
|
|
|
|
|
|
|
2485
|
|
|
|
|
|
|
L |
2486
|
|
|
|
|
|
|
|
2487
|
|
|
|
|
|
|
=back |
2488
|
|
|
|
|
|
|
|
2489
|
|
|
|
|
|
|
|
2490
|
|
|
|
|
|
|
=head1 ACKNOWLEDGEMENTS |
2491
|
|
|
|
|
|
|
|
2492
|
|
|
|
|
|
|
Thanks to RJBS for module-starter. |
2493
|
|
|
|
|
|
|
|
2494
|
|
|
|
|
|
|
=head1 AUTHOR |
2495
|
|
|
|
|
|
|
|
2496
|
|
|
|
|
|
|
Andreas König |
2497
|
|
|
|
|
|
|
|
2498
|
|
|
|
|
|
|
=head1 COPYRIGHT & LICENSE |
2499
|
|
|
|
|
|
|
|
2500
|
|
|
|
|
|
|
Copyright 2008,2009 Andreas König. |
2501
|
|
|
|
|
|
|
|
2502
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it |
2503
|
|
|
|
|
|
|
under the same terms as Perl itself. |
2504
|
|
|
|
|
|
|
|
2505
|
|
|
|
|
|
|
|
2506
|
|
|
|
|
|
|
=cut |
2507
|
|
|
|
|
|
|
|
2508
|
|
|
|
|
|
|
1; # End of File::Rsync::Mirror::Recentfile |
2509
|
|
|
|
|
|
|
|
2510
|
|
|
|
|
|
|
# Local Variables: |
2511
|
|
|
|
|
|
|
# mode: cperl |
2512
|
|
|
|
|
|
|
# cperl-indent-level: 4 |
2513
|
|
|
|
|
|
|
# End: |