Skip to content

Commit 6b0585c

Browse files
biodranikKeats
authored andcommitted
Avoid unnecessary checking for already checked links (#2305)
* Fixed "unnecessary mut" warning * Fixed minor typo * Use more than 8 threads for links checking if hardware supports it * Fixed failing azure Linux check * Avoid unnecessary HTTP requests to the same, already checked links
1 parent 670b882 commit 6b0585c

3 files changed

Lines changed: 35 additions & 10 deletions

File tree

azure-pipelines.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ stages:
2121
rustup_toolchain: stable
2222
linux-pinned:
2323
imageName: 'ubuntu-20.04'
24-
rustup_toolchain: 1.64.0
24+
rustup_toolchain: 1.71.1
2525
pool:
2626
vmImage: $(imageName)
2727
steps:

components/content/src/library.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ impl Library {
188188
if !self.sections[&path].meta.transparent {
189189
// Fill siblings
190190
for (i, page_path) in sorted.iter().enumerate() {
191-
let mut p = self.pages.get_mut(page_path).unwrap();
191+
let p = self.pages.get_mut(page_path).unwrap();
192192
if i > 0 {
193193
// lighter / later / title_prev
194194
p.lower = Some(sorted[i - 1].clone());

components/site/src/link_checking.rs

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use core::time;
2-
use std::path::Path;
3-
use std::{collections::HashMap, path::PathBuf, thread};
2+
use std::path::{Path, PathBuf};
3+
use std::{cmp, collections::HashMap, collections::HashSet, iter::FromIterator, thread};
44

55
use config::LinkCheckerLevel;
66
use libs::rayon::prelude::*;
@@ -52,7 +52,7 @@ pub fn check_internal_links_with_anchors(site: &Site) -> Vec<String> {
5252
full_path.push(part);
5353
}
5454
// NOTE: This will also match _index.foobar.md where foobar is not a language
55-
// as well as any other sring containing "_index." which is now referenced as
55+
// as well as any other string containing "_index." which is now referenced as
5656
// unsupported page path in the docs.
5757
if md_path.contains("_index.") {
5858
let section = library.sections.get(&full_path).unwrap_or_else(|| {
@@ -172,7 +172,11 @@ pub fn check_external_links(site: &Site) -> Vec<String> {
172172

173173
println!(
174174
"Checking {} external link(s). Skipping {} external link(s).{}",
175-
checked_links.len(),
175+
// Get unique links count from Vec by creating a temporary HashSet.
176+
HashSet::<&str>::from_iter(
177+
checked_links.iter().map(|link_def| link_def.external_link.as_str())
178+
)
179+
.len(),
176180
skipped_link_count,
177181
if invalid_url_links == 0 {
178182
"".to_string()
@@ -199,24 +203,43 @@ pub fn check_external_links(site: &Site) -> Vec<String> {
199203
}
200204
}
201205

206+
let cpu_count = match thread::available_parallelism() {
207+
Ok(count) => count.get(),
208+
Err(_) => 1,
209+
};
202210
// create thread pool with lots of threads so we can fetch
203211
// (almost) all pages simultaneously, limiting all links for a single
204212
// domain to one thread to avoid rate-limiting
205-
let threads = std::cmp::min(links_by_domain.len(), 8);
206-
match rayon::ThreadPoolBuilder::new().num_threads(threads).build() {
213+
let num_threads = cmp::min(links_by_domain.len(), cmp::max(8, cpu_count));
214+
match rayon::ThreadPoolBuilder::new().num_threads(num_threads).build() {
207215
Ok(pool) => {
208216
let errors = pool.install(|| {
209217
links_by_domain
210218
.par_iter()
211219
.map(|(_, links)| {
212220
let mut num_links_left = links.len();
221+
let mut checked_links: HashMap<&str, Option<link_checker::Result>> =
222+
HashMap::new();
213223
links
214224
.iter()
215225
.filter_map(move |link_def| {
216226
num_links_left -= 1;
217227

228+
// Avoid double-checking the same url (e.g. for translated pages).
229+
let external_link = link_def.external_link.as_str();
230+
if let Some(optional_res) = checked_links.get(external_link) {
231+
if let Some(res) = optional_res {
232+
return Some((
233+
&link_def.file_path,
234+
external_link,
235+
res.clone(),
236+
));
237+
}
238+
return None;
239+
}
240+
218241
let res = link_checker::check_url(
219-
&link_def.external_link,
242+
external_link,
220243
&site.config.link_checker,
221244
);
222245

@@ -226,9 +249,11 @@ pub fn check_external_links(site: &Site) -> Vec<String> {
226249
}
227250

228251
if link_checker::is_valid(&res) {
252+
checked_links.insert(external_link, None);
229253
None
230254
} else {
231-
Some((&link_def.file_path, &link_def.external_link, res))
255+
checked_links.insert(external_link, Some(res.clone()));
256+
return Some((&link_def.file_path, external_link, res));
232257
}
233258
})
234259
.collect::<Vec<_>>()

0 commit comments

Comments
 (0)