Skip to content

Commit 4c80a04

Browse files
authored
Merge pull request #1260 from syphar/fix-lucky-test
refactor random crate search, the random view size is configurable and we don't retry any more
2 parents 97fde48 + bcbde30 commit 4c80a04

File tree

2 files changed

+62
-45
lines changed

2 files changed

+62
-45
lines changed

src/config.rs

+11
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,15 @@ pub struct Config {
3838
// Time between 'git gc --auto' calls in seconds
3939
pub(crate) registry_gc_interval: u64,
4040

41+
// random crate search generates a number of random IDs to
42+
// efficiently find a random crate with > 100 GH stars.
43+
// The amount depends on the ratio of crates with >100 stars
44+
// to the count of all crates.
45+
// At the time of creating this setting, it is set to
46+
// `500` for a ratio of 7249 over 54k crates.
47+
// For unit-tests the number has to be higher.
48+
pub(crate) random_crate_search_view_size: u32,
49+
4150
// Build params
4251
pub(crate) build_attempts: u16,
4352
pub(crate) rustwide_workspace: PathBuf,
@@ -84,6 +93,8 @@ impl Config {
8493
max_parse_memory: env("DOCSRS_MAX_PARSE_MEMORY", 5 * 1024 * 1024)?,
8594
registry_gc_interval: env("DOCSRS_REGISTRY_GC_INTERVAL", 60 * 60)?,
8695

96+
random_crate_search_view_size: env("DOCSRS_RANDOM_CRATE_SEARCH_VIEW_SIZE", 500)?,
97+
8798
rustwide_workspace: env("CRATESFYI_RUSTWIDE_WORKSPACE", PathBuf::from(".workspace"))?,
8899
inside_docker: env("DOCS_RS_DOCKER", false)?,
89100
local_docker_image: maybe_env("DOCS_RS_LOCAL_DOCKER_IMAGE")?,

src/web/releases.rs

+51-45
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::{
55
db::{Pool, PoolClient},
66
impl_webpage,
77
web::{error::Nope, match_version, page::WebPage, redirect_base},
8-
BuildQueue,
8+
BuildQueue, Config,
99
};
1010
use chrono::{DateTime, Utc};
1111
use iron::{
@@ -498,13 +498,18 @@ impl Default for Search {
498498
}
499499

500500
fn redirect_to_random_crate(req: &Request, conn: &mut PoolClient) -> IronResult<Response> {
501-
// since there is a chance of this query returning an empty result,
502-
// we retry a certain amount of times, and log a warning.
503-
for i in 0..20 {
504-
let rows = ctry!(
505-
req,
506-
conn.query(
507-
"WITH params AS (
501+
// We try to find a random crate and redirect to it.
502+
//
503+
// The query is efficient, but relies on a static factor which depends
504+
// on the amount of crates with > 100 GH stars over the amount of all crates.
505+
//
506+
// If random-crate-searches end up being empty, increase that value.
507+
508+
let config = extension!(req, Config);
509+
let rows = ctry!(
510+
req,
511+
conn.query(
512+
"WITH params AS (
508513
-- get maximum possible id-value in crates-table
509514
SELECT last_value AS max_id FROM crates_id_seq
510515
)
@@ -513,55 +518,44 @@ fn redirect_to_random_crate(req: &Request, conn: &mut PoolClient) -> IronResult<
513518
releases.version,
514519
releases.target_name
515520
FROM (
516-
-- generate 500 random numbers in the ID-range.
517-
-- this might have to be increased when we have to repeat
518-
-- this query too often.
519-
-- it depends on the percentage of crates with > 100 stars
521+
-- generate random numbers in the ID-range.
520522
SELECT DISTINCT 1 + trunc(random() * params.max_id)::INTEGER AS id
521-
FROM params, generate_series(1, 500)
523+
FROM params, generate_series(1, $1)
522524
) AS r
523525
INNER JOIN crates ON r.id = crates.id
524526
INNER JOIN releases ON crates.latest_version_id = releases.id
525527
INNER JOIN github_repos ON releases.github_repo = github_repos.id
526528
WHERE
527529
releases.rustdoc_status = TRUE AND
528530
github_repos.stars >= 100
529-
LIMIT 1
530-
",
531-
&[]
532-
),
533-
);
534-
535-
if let Some(row) = rows.into_iter().next() {
536-
let name: String = row.get("name");
537-
let version: String = row.get("version");
538-
let target_name: String = row.get("target_name");
539-
let url = ctry!(
540-
req,
541-
Url::parse(&format!(
542-
"{}/{}/{}/{}/",
543-
redirect_base(req),
544-
name,
545-
version,
546-
target_name
547-
)),
548-
);
531+
LIMIT 1",
532+
&[&(config.random_crate_search_view_size as i32)]
533+
)
534+
);
549535

550-
let mut resp = Response::with((status::Found, Redirect(url)));
551-
resp.headers.set(Expires(HttpDate(time::now())));
536+
if let Some(row) = rows.into_iter().next() {
537+
let name: String = row.get("name");
538+
let version: String = row.get("version");
539+
let target_name: String = row.get("target_name");
540+
let url = ctry!(
541+
req,
542+
Url::parse(&format!(
543+
"{}/{}/{}/{}/",
544+
redirect_base(req),
545+
name,
546+
version,
547+
target_name
548+
)),
549+
);
552550

553-
let metrics = extension!(req, crate::Metrics).clone();
554-
metrics.im_feeling_lucky_searches.inc();
551+
let metrics = extension!(req, crate::Metrics).clone();
552+
metrics.im_feeling_lucky_searches.inc();
555553

556-
log::debug!("finished random crate search on iteration {}", i);
557-
return Ok(resp);
558-
} else {
559-
log::warn!("retrying random crate search");
560-
}
554+
Ok(super::redirect(url))
555+
} else {
556+
log::error!("found no result in random crate search");
557+
Err(Nope::NoResults.into())
561558
}
562-
log::error!("found no result in random crate search");
563-
564-
Err(Nope::NoResults.into())
565559
}
566560

567561
impl_webpage! {
@@ -1095,6 +1089,18 @@ mod tests {
10951089
#[test]
10961090
fn im_feeling_lucky_with_stars() {
10971091
wrapper(|env| {
1092+
{
1093+
// The normal test-setup will offset all primary sequences by 10k
1094+
// to prevent errors with foreign key relations.
1095+
// Random-crate-search relies on the sequence for the crates-table
1096+
// to find a maximum possible ID. This combined with only one actual
1097+
// crate in the db breaks this test.
1098+
// That's why we reset the id-sequence to zero for this test.
1099+
1100+
let mut conn = env.db().conn();
1101+
conn.execute(r#"ALTER SEQUENCE crates_id_seq RESTART WITH 1"#, &[])?;
1102+
}
1103+
10981104
let web = env.frontend();
10991105
env.fake_release()
11001106
.github_stats("some/repo", 333, 22, 11)

0 commit comments

Comments
 (0)