Skip to content

Commit 4a47f76

Browse files
committed
fix serving linked folders when dest changes
1 parent c8fa892 commit 4a47f76

File tree

8 files changed

+135
-42
lines changed

8 files changed

+135
-42
lines changed

lib/MirrorCache/Datamodule.pm

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ my @ROUTES = ( '/browse', '/download' );
3131
has [ '_route', '_route_len' ]; # this is '/download' or '/browse'
3232
has [ 'route', 'route_len' ]; # this may be '/download' or '/browse' or empty if one of TOP_FOLDERS present
3333
has [ 'metalink', 'meta4', 'zsync', 'accept_all', 'accept_metalink', 'accept_meta4', 'accept_zsync' ];
34-
has [ 'metalink_limit' ]; # maximum mirrors to search for metalink
34+
has metalink_limit => 10; # maximum mirrors to search for metalink
3535
has [ '_ip', '_country', '_region', '_lat', '_lng', '_vpn' ];
3636
has [ '_avoid_countries' ];
3737
has [ '_pedantic' ];
@@ -522,7 +522,7 @@ sub _init_path($self) {
522522
$regex = $v if eval { m/$v/; 1; };
523523
} elsif ($k eq 'P' || $k eq 'GLOB') {
524524
my $x = _glob2re($v);
525-
next unless eval { m/$x/; 1; };
525+
next unless ($x && eval { m/$x/; 1; });
526526
$glob = $v;
527527
$glob_regex = $x;
528528
}

lib/MirrorCache/Schema/Result/FolderDiff.pm

+3-1
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,13 @@ __PACKAGE__->add_columns(
5656
{ data_type => "bigint", is_foreign_key => 1, is_nullable => 1 },
5757
"hash",
5858
{ data_type => "varchar", is_nullable => 1, size => 40 },
59-
"dt",
59+
"dt",
6060
{
6161
data_type => 'timestamp',
6262
is_nullable => 0
6363
},
64+
"realfolder_id",
65+
{ data_type => "bigint", is_foreign_key => 1, is_nullable => 1 },
6466
);
6567

6668
=head1 PRIMARY KEY

lib/MirrorCache/Schema/ResultSet/Server.pm

+17-12
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ my $MIRRORCACHE_MAX_PATH = int($ENV{MIRRORCACHE_MAX_PATH} // 512);
2525

2626
sub mirrors_query {
2727
my (
28-
$self, $country, $region, $folder_id, $file_id, $project_id,
28+
$self, $country, $region, $realfolder_id, $folder_id, $file_id,
29+
$realproject_id, $project_id,
2930
$capability, $ipv, $lat, $lng, $avoid_countries,
3031
$limit, $avoid_region, $schemastrict, $ipvstrict, $vpn
3132
) = @_;
@@ -36,6 +37,9 @@ sub mirrors_query {
3637
$lat = 0 unless $lat;
3738
$lng = 0 unless $lng;
3839

40+
$realproject_id = $project_id unless $realproject_id;
41+
$realfolder_id = ($folder_id // 0) unless $realfolder_id;
42+
3943
my $rsource = $self->result_source;
4044
my $schema = $rsource->schema;
4145
my $dbh = $schema->storage->dbh;
@@ -96,15 +100,15 @@ sub mirrors_query {
96100
my $join_server_project = "";
97101
my $condition_our_regions = $schema->condition_our_regions;
98102
my $condition_server_project = "";
99-
if ($project_id) {
100-
$join_server_project = "left join server_project sp on (project_id,sp.server_id) = ($project_id,s.id) and state < 1";
103+
if ($project_id and $realfolder_id == $folder_id) {
104+
$join_server_project = "left join server_project sp on sp.server_id = s.id and project_id in ($project_id,$realproject_id) and state < 1";
101105
$condition_server_project = "and sp.server_id IS NULL";
102106
}
103107

104108
my $join_file_cond = "fl.folder_id = fd.folder_id";
105109
# license.tar* and info.xml* might be kept with the same name through updates, so timestamp on them is unreliable in mirrorlist for folders
106110
my $file_dt = ", max(case when fdf.file_id is null and fl.name ~ '[0-9]' and fl.name not like '%license.tar.%' and fl.name not like '%info.xml.%' then fl.mtime else null end) as mtime";
107-
my $group_by = "group by s.id, s.hostname, s.hostname_vpn, s.urldir, s.region, s.country, s.lat, s.lng, s.score";
111+
my $group_by = "group by s.id, s.hostname, s.hostname_vpn, s.urldir, s.region, s.country, s.lat, s.lng, s.score, fd.folder_id";
108112

109113
if ($file_id) {
110114
$join_file_cond = "fl.id = ?";
@@ -122,6 +126,7 @@ mtime,
122126
dist,
123127
case $weight_country_case when region $avoid_region= '$region' then 1 else 0 end rating_country,
124128
score, country, region, lat, lng,
129+
realfolderscore,
125130
support_scheme,
126131
rating_scheme,
127132
support_ipv,
@@ -136,22 +141,22 @@ case when $lat=0 and $lng=0 then 0
136141
else
137142
( 6371 * acos( cos( radians($lat) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians($lng) ) + sin( radians($lat) ) * sin( radians( lat ) ) ) )
138143
end as dist,
139-
s.country, s.region, s.score,
144+
s.country, s.region, s.score, case when f.id = $realfolder_id then 0 else 1 end realfolderscore,
140145
CASE WHEN COALESCE(stability_scheme.rating, 0) > 0 OR COALESCE(stability_schemex.rating, 0) = 0 THEN 1 ELSE 0 END AS support_scheme, -- we show here 0 only when opposite scheme is supported
141146
CASE WHEN COALESCE(stability_scheme.rating, 0) > 0 OR COALESCE(stability_schemex.rating, 0) = 0 THEN ( scf.capability is NULL AND COALESCE(scd.enabled, 't') = 't' ) ELSE ( scf2.capability is NULL AND COALESCE(scd2.enabled, 't') = 't' ) END AS not_disabled,
142147
CASE WHEN COALESCE(stability_scheme.rating, 0) > 0 THEN stability_scheme.rating WHEN COALESCE(stability_schemex.rating, 0) > 0 THEN stability_schemex.rating ELSE 0 END AS rating_scheme,
143148
CASE WHEN COALESCE(stability_ipv.rating, 0) > 0 THEN 1 ELSE 0 END AS support_ipv,
144149
CASE WHEN COALESCE(stability_ipv.rating, 0) > 0 THEN stability_ipv.rating WHEN COALESCE(stability_ipvx.rating, 0) > 0 THEN stability_ipvx.rating ELSE 0 END AS rating_ipv
145150
from (
146-
select s.id, s.hostname, s.hostname_vpn, s.urldir, s.region, s.country, s.lat, s.lng, s.score $file_dt
151+
select s.id, s.hostname, s.hostname_vpn, s.urldir, s.region, s.country, s.lat, s.lng, s.score $file_dt, fd.folder_id
147152
from folder_diff fd
148153
join file fl on $join_file_cond
149154
join folder_diff_server fds on fd.id = fds.folder_diff_id and date_trunc('second', fl.dt) <= fds.dt
150155
join server s on fds.server_id = s.id and s.enabled $country_condition $condition_our_regions
151156
left join server_capability_declaration scd on s.id = scd.server_id and scd.capability = 'country'
152157
left join folder_diff_file fdf on fdf.file_id = fl.id and fdf.folder_diff_id = fd.id
153158
$join_server_project
154-
where fd.folder_id = ? and fdf.file_id is NULL $condition_server_project
159+
where fd.folder_id in (?,?) and coalesce(fd.realfolder_id, ?) = ? and (fdf.file_id is NULL and fl.folder_id = ?) $condition_server_project
155160
and ( -- here mirrors may be declared to handle only specific countries
156161
scd.server_id is null
157162
or
@@ -163,7 +168,7 @@ from (
163168
)
164169
$group_by
165170
) s
166-
join folder f on f.id = ?
171+
join folder f on f.id = s.folder_id
167172
left join server_capability_declaration scd on s.id = scd.server_id and scd.capability = '$capability' and NOT scd.enabled
168173
left join server_capability_force scf on s.id = scf.server_id and scf.capability = '$capability'
169174
left join server_capability_declaration scd2 on s.id = scd2.server_id and scd.capability = '$ipv' and NOT scd.enabled
@@ -174,10 +179,10 @@ left join server_stability stability_ipv on s.id = stability_ipv.server_id
174179
left join server_stability stability_ipvx on s.id = stability_ipvx.server_id and stability_ipvx.capability = '$ipvx'
175180
) x
176181
WHERE not_disabled $extra
177-
order by rating_country desc, (dist/100)::int, support_scheme desc, rating_scheme desc, support_ipv desc, rating_ipv desc, score, random()
182+
order by rating_country desc, (dist/100)::int, support_scheme desc, rating_scheme desc, support_ipv desc, rating_ipv desc, score, realfolderscore, random()
178183
limit $limit1
179184
) xx
180-
order by support_scheme desc, rating_scheme desc, support_ipv desc, rating_ipv desc, rating_country desc, (dist/100)::int, score, random()
185+
order by support_scheme desc, rating_scheme desc, support_ipv desc, rating_ipv desc, rating_country desc, (dist/100)::int, score, realfolderscore, random()
181186
limit $limit;
182187
END_SQL
183188

@@ -189,9 +194,9 @@ END_SQL
189194
my $prep = $dbh->prepare($sql);
190195

191196
if ($file_id) {
192-
$prep->execute($file_id, @country_params, $folder_id, $country, $country, $country, $folder_id);
197+
$prep->execute($file_id, @country_params, $realfolder_id, $folder_id, $realfolder_id, $realfolder_id, $realfolder_id, $country, $country, $country);
193198
} else {
194-
$prep->execute(@country_params, $folder_id, $country, $country, $country, $folder_id);
199+
$prep->execute(@country_params, $realfolder_id, $folder_id, $realfolder_id, $realfolder_id, $realfolder_id, $country, $country, $country);
195200
}
196201
my $server_arrayref = $dbh->selectall_arrayref($prep, { Slice => {} });
197202
return $server_arrayref;

lib/MirrorCache/Task/MirrorScan.pm

+3-2
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ sub _scan {
5757
}
5858
return undef unless $dbfiles;
5959

60-
my $count = _doscan($app, $job, $path, $folder_id, $latestdt, $max_dt, $dbfiles, $dbfileids, $dbfileprefixes);
60+
my $count = _doscan($app, $job, $path, $realfolder_id, $folder_id, $latestdt, $max_dt, $dbfiles, $dbfileids, $dbfileprefixes);
6161
$job->note($count => 1);
6262
return $job->finish;
6363
}
@@ -103,7 +103,7 @@ sub _dbfiles {
103103
}
104104

105105
sub _doscan {
106-
my ($app, $job, $path, $folder_id, $latestdt, $max_dt, $dbfiles, $dbfileids, $dbfileprefixes) = @_;
106+
my ($app, $job, $path, $realfolder_id, $folder_id, $latestdt, $max_dt, $dbfiles, $dbfileids, $dbfileprefixes) = @_;
107107
my @dbfiles = @$dbfiles;
108108
my %dbfileids = %$dbfileids;
109109
my %dbfileprefixes = %$dbfileprefixes;
@@ -228,6 +228,7 @@ unless ($hasall) {
228228
$folder_diff = $schema->resultset('FolderDiff')->find_or_new({folder_id => $folder_id, hash => $digest});
229229
unless($folder_diff->in_storage) {
230230
$folder_diff->dt($latestdt);
231+
$folder_diff->realfolder_id($realfolder_id) if $realfolder_id && $realfolder_id != $folder_id;
231232
$folder_diff->insert;
232233

233234
foreach my $id (@missing_files) {

lib/MirrorCache/WebAPI/Plugin/RenderFileFromMirror.pm

+16-25
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ sub register {
4646
}
4747
my (@mirrors_country, @mirrors_region, @mirrors_rest);
4848
my $project_id = $c->mcproject->get_id($path);
49-
my $cnt = _collect_mirrors($dm, \@mirrors_country, \@mirrors_region, \@mirrors_rest, undef, undef, $folder_id, $project_id);
49+
my $cnt = _collect_mirrors($dm, \@mirrors_country, \@mirrors_region, \@mirrors_rest, undef, undef, $folder_id, $project_id, undef, undef, 16);
5050

5151
return $c->render(status => 204, text => 'No mirrors found') unless $cnt;
5252
my @mirrors;
@@ -186,13 +186,11 @@ sub register {
186186

187187
my (@mirrors_country, @mirrors_region, @mirrors_rest);
188188
my $project_id = $c->mcproject->get_id($dirname);
189-
_collect_mirrors($dm, \@mirrors_country, \@mirrors_region, \@mirrors_rest, $file->{id}, $file->{name}, $folder_id, $project_id);
189+
my $realproject_id;
190+
$realproject_id = $c->mcproject->get_id($realdirname) if ($realfolder_id && $realfolder_id != $folder_id);
191+
my $limit = $dm->mirrorlist ? 300 : (( $dm->metalink || $dm->meta4 || $dm->zsync || $dm->pedantic )? $dm->metalink_limit : 1);
192+
my $cnt = _collect_mirrors($dm, \@mirrors_country, \@mirrors_region, \@mirrors_rest, $file->{id}, $file->{name}, $folder_id, $project_id, $realfolder_id, $realproject_id, $limit);
190193

191-
# add mirrors that have realpath
192-
if ($realfolder_id && $realfolder_id != $folder_id) {
193-
my $realproject_id = $c->mcproject->get_id($realdirname);
194-
_collect_mirrors($dm, \@mirrors_country, \@mirrors_region, \@mirrors_rest, $file->{id}, $file->{name}, $realfolder_id, $realproject_id);
195-
}
196194
my $mirror;
197195
$mirror = $mirrors_country[0] if @mirrors_country;
198196
$mirror = $mirrors_region[0] if !$mirror && @mirrors_region;
@@ -736,7 +734,7 @@ sub _build_metalink() {
736734
}
737735

738736
sub _collect_mirrors {
739-
my ($dm, $mirrors_country, $mirrors_region, $mirrors_rest, $file_id, $file_name, $folder_id, $project_id) = @_;
737+
my ($dm, $mirrors_country, $mirrors_region, $mirrors_rest, $file_id, $file_name, $folder_id, $project_id, $realfolder_id, $realproject_id, $limit) = @_;
740738

741739
my $country = $dm->country;
742740
my $region = $dm->region;
@@ -748,61 +746,54 @@ sub _collect_mirrors {
748746
my $mirrorlist = $dm->mirrorlist;
749747
my $ipvstrict = $dm->ipvstrict;
750748
my $metalink = $dm->metalink || $dm->meta4 || $dm->zsync;
751-
my $limit = $mirrorlist ? 200 : (( $metalink || $dm->pedantic )? 10 : 1);
752-
my $hard_limit = $dm->metalink_limit;
753-
$limit = $hard_limit if $hard_limit;
754749
my $rs = $dm->c->schema->resultset('Server');
755750

756751
my $m;
757752
$m = $rs->mirrors_query(
758-
$country, $region, $folder_id, $file_id, $project_id,
753+
$country, $region, $realfolder_id, $folder_id, $file_id, $realproject_id, $project_id,
759754
$scheme, $ipv, $lat, $lng, $avoid_countries, $limit, 0,
760755
!$mirrorlist, $ipvstrict, $vpn
761756
) if $country;
762757

763758
if ($m && scalar(@$m)) {
764-
splice(@$m, $hard_limit) if $hard_limit && $hard_limit > scalar(@$m);
759+
splice(@$m, $limit) if $limit > scalar(@$m);
765760
push @$mirrors_country, @$m;
766761
}
767762
my $found_count = scalar(@$mirrors_country) + scalar(@$mirrors_region) + scalar(@$mirrors_rest);
768763

769-
if ($region && ($found_count < $limit) && (!$hard_limit || $found_count < $hard_limit)) {
764+
if ($region && ($found_count < $limit)) {
770765
my @avoid_countries;
771766
push @avoid_countries, @$avoid_countries if $avoid_countries && scalar(@$avoid_countries);
772767
push @avoid_countries, $country if ($country and !(grep { $country eq $_ } @avoid_countries));
773768
$m = $rs->mirrors_query(
774-
$country, $region, $folder_id, $file_id, $project_id,
769+
$country, $region, $realfolder_id, $folder_id, $file_id, $realproject_id, $project_id,
775770
$scheme, $ipv, $lat, $lng, \@avoid_countries, $limit, 0,
776771
!$mirrorlist, $ipvstrict, $vpn
777772
);
778773
my $found_more;
779774

780775
$found_more = scalar(@$m) if $m;
781776
if ($found_more) {
782-
if ($hard_limit && $found_count + $found_more > $hard_limit) {
783-
$found_more = $hard_limit - $found_count;
777+
if ($limit && $found_count + $found_more > $limit) {
778+
$found_more = $limit - $found_count;
784779
splice @$m, $found_more;
785780
}
786781
$found_count += $found_more;
787782
push @$mirrors_region, @$m;
788783
}
789784
}
790785

791-
if (
792-
($found_count < $limit && !$dm->root_country && (!$hard_limit || $hard_limit > $found_count)) ||
793-
($metalink && $found_count < 3) ||
794-
$mirrorlist
795-
) {
786+
if ($found_count < $limit) {
796787
$m = $rs->mirrors_query(
797-
$country, $region, $folder_id, $file_id, $project_id,
788+
$country, $region, $realfolder_id, $folder_id, $file_id, $realproject_id, $project_id,
798789
$scheme, $ipv, $lat, $lng, $avoid_countries, $limit, 1,
799790
!$mirrorlist, $ipvstrict, $vpn
800791
);
801792
my $found_more;
802793
$found_more = scalar(@$m) if $m;
803794
if ($found_more) {
804-
if ($hard_limit && $found_count + $found_more > $hard_limit) {
805-
$found_more = $hard_limit - $found_count;
795+
if ($found_count + $found_more > $limit) {
796+
$found_more = $limit - $found_count;
806797
splice @$m, $found_more;
807798
}
808799
$found_count += $found_more;

lib/MirrorCache/resources/migrations/Pg.sql

+3
Original file line numberDiff line numberDiff line change
@@ -410,3 +410,6 @@ create table rollout_server (
410410
-- 34 up
411411
alter table rollout_server add column if not exists scan_dt timestamp;
412412
create index if not exists rollout_version_inx on rollout(version);
413+
-- 35 up
414+
alter table folder_diff add column if not exists realfolder_id bigint;
415+

lib/MirrorCache/resources/migrations/mysql.sql

+2
Original file line numberDiff line numberDiff line change
@@ -422,3 +422,5 @@ alter table rollout_server
422422
-- 34 up
423423
alter table rollout_server add column if not exists scan_dt timestamp;
424424
create index if not exists i_rollout_version on rollout(version);
425+
-- 35 up
426+
alter table folder_diff add column if not exists realfolder_id bigint;

t/environ/15-local-symlink-3.sh

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#!lib/test-in-container-environ.sh
2+
set -ex
3+
4+
mc=$(environ mc $(pwd))
5+
6+
ap8=$(environ ap8)
7+
ap7=$(environ ap7)
8+
9+
10+
$mc/start
11+
$mc/status
12+
13+
mkdir -p $mc/dt/folder1
14+
echo 123456 > $mc/dt/folder1/file1.1.dat
15+
16+
mkdir -p $mc/dt/folder2
17+
echo abcdef > $mc/dt/folder2/file1.2.dat
18+
19+
mkdir -p $mc/dt/updates/tool
20+
(
21+
cd $mc/dt/updates/tool/
22+
ln -s ../../folder1 v1
23+
)
24+
25+
ls -la $mc/dt/updates/tool/
26+
27+
cp -r $mc/dt/folder*/ $ap7/dt/
28+
mkdir -p $ap8/dt/updates/tool/v1
29+
cp $mc/dt/folder1/* $ap8/dt/updates/tool/v1/
30+
31+
32+
$ap7/start
33+
$ap7/curl /folder1/ | grep file1.1.dat
34+
35+
$ap8/start
36+
$ap8/curl /updates/tool/v1/ | grep file1.1.dat
37+
38+
39+
$mc/sql "insert into server(hostname,urldir,enabled,country,region) select '$($ap7/print_address)','','t','us','na'"
40+
$mc/sql "insert into server(hostname,urldir,enabled,country,region) select '$($ap8/print_address)','','t','de','eu'"
41+
42+
$mc/curl -I /download/updates/tool/v1/file1.1.dat.mirrorlist
43+
44+
$mc/backstage/job -e folder_sync -a '["/folder1"]'
45+
$mc/backstage/job -e mirror_scan -a '["/folder1"]'
46+
$mc/backstage/job -e folder_sync -a '["/folder2"]'
47+
$mc/backstage/job -e mirror_scan -a '["/folder2"]'
48+
$mc/backstage/shoot
49+
50+
# normally it is created inside FolderSyncScheduleFromMisses
51+
$mc/sql "insert into folder(path) select '/updates/tool/v1'"
52+
53+
$mc/backstage/job -e folder_sync -a '["/updates/tool/v1"]'
54+
$mc/backstage/job -e mirror_scan -a '["/updates/tool/v1"]'
55+
$mc/backstage/shoot
56+
57+
echo redirect is to symlinked folder
58+
$mc/curl -I /download/updates/tool/v1/file1.1.dat | grep $($ap7/print_address)/folder1/file1.1.dat
59+
60+
$mc/curl /download/updates/tool/v1/ | grep file1.1.dat
61+
62+
# make sure db doesn't have info about symlinked folder
63+
$mc/sql_test 0 == "select count(*) from file where folder_id = (select id from folder where path = '/updates/tool/v1')"
64+
# even if we scanned symlinked folder, the real folder is in db
65+
$mc/sql_test 1 == "select count(*) from file where folder_id = (select id from folder where path = '/folder1')"
66+
67+
$mc/curl -I /download/updates/tool/v1/file1.1.dat?COUNTRY=us | grep $($ap7/print_address)/folder1/file1.1.dat
68+
$mc/curl -I /download/updates/tool/v1/file1.1.dat?COUNTRY=de | grep $($ap8/print_address)/updates/tool/v1/file1.1.dat
69+
70+
$mc/curl /download/updates/tool/v1/file1.1.dat.mirrorlist | grep -C 20 $($ap7/print_address)/folder1/file1.1.dat | grep $($ap8/print_address)/updates/tool/v1/file1.1.dat
71+
$mc/curl /download/updates/tool/v1/file1.1.dat.metalink | grep -C 10 $($ap7/print_address)/folder1/file1.1.dat | grep $($ap8/print_address)/updates/tool/v1/file1.1.dat
72+
73+
74+
echo now change destination of the symlink and scan it
75+
(
76+
cd $mc/dt/updates/tool/
77+
rm v1
78+
ln -s ../../folder2 v1
79+
ls -la
80+
)
81+
82+
$mc/curl -I /download/updates/tool/v1/file1.2.dat?COUNTRY=us
83+
rc=0
84+
$mc/curl -IL /download/updates/tool/v1/file1.2.dat?COUNTRY=de
85+
$mc/curl -IL /download/updates/tool/v1/file1.2.dat?COUNTRY=de | grep '404 Not Found' || rc=$?
86+
87+
test $rc -gt 0
88+
89+
echo success

0 commit comments

Comments
 (0)