Skip to content

Commit b84b91a

Browse files
committedMar 19, 2019
Auto merge of #1675 - sgrif:sg-resilient-crate-download, r=jtgeibel
Allow download counts to fail to be updated This ensures that the download endpoint still works even if counting the download fails. The main case that we expect failure to occur is when the application is in read only mode. However, even if an unexpected failure occurs, we still want `cargo build` to succeed. Counting downloads is always considered optional -- it's much more important that people's builds succeed than having accurate download stats. Note that we still require a database connection from the pool. In theory, we could allow getting the version ID to fail as well, and just blindly redirect to S3 no matter what, and rely on a 404 happening there. However, this could result in successful builds in the event that the index is out of sync with our DB, since we don't routinely clean up the S3 bucket. Our current test behavior says that we 404 instead of redirecting, so I've left things as is. Once sfackler/r2d2#73 is released, my plan is to try to get a DB connection with a shorter timeout, and if we were able to get a DB connection, do our current behavior, but if that fails just blindly redirect to S3. We don't expect getting a connection to fail unless we're being DDOS'd, nor do we expect requests to this endpoint for crates that don't exist (since they should only come for things in the index). Fixes #1387
2 parents ade9a8e + c9a2b20 commit b84b91a

File tree

3 files changed

+13
-14
lines changed

3 files changed

+13
-14
lines changed
 

‎src/controllers/version/downloads.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ use crate::controllers::prelude::*;
66

77
use chrono::{Duration, NaiveDate, Utc};
88

9-
use crate::Replica;
10-
119
use crate::models::{Crate, VersionDownload};
1210
use crate::schema::*;
1311
use crate::views::EncodableVersionDownload;
@@ -20,15 +18,7 @@ pub fn download(req: &mut dyn Request) -> CargoResult<Response> {
2018
let crate_name = &req.params()["crate_id"];
2119
let version = &req.params()["version"];
2220

23-
// If we are a mirror, ignore failure to update download counts.
24-
// API-only mirrors won't have any crates in their database, and
25-
// incrementing the download count will look up the crate in the
26-
// database. Mirrors just want to pass along a redirect URL.
27-
if req.app().config.mirror == Replica::ReadOnlyMirror {
28-
let _ = increment_download_counts(req, crate_name, version);
29-
} else {
30-
increment_download_counts(req, crate_name, version)?;
31-
}
21+
increment_download_counts(req, crate_name, version)?;
3222

3323
let redirect_url = req
3424
.app()
@@ -48,6 +38,14 @@ pub fn download(req: &mut dyn Request) -> CargoResult<Response> {
4838
}
4939
}
5040

41+
/// Increment the download counts for a given crate version.
42+
///
43+
/// Returns an error if we could not load the version ID from the database.
44+
///
45+
/// This ignores any errors that occur updating the download count. Failure is
46+
/// expected if the application is in read only mode, or for API-only mirrors.
47+
/// Even if failure occurs for unexpected reasons, we would rather have `cargo
48+
/// build` succeed and not count the download than break people's builds.
5149
fn increment_download_counts(
5250
req: &dyn Request,
5351
crate_name: &str,
@@ -62,7 +60,9 @@ fn increment_download_counts(
6260
.filter(num.eq(version))
6361
.first(&*conn)?;
6462

65-
VersionDownload::create_or_increment(version_id, &conn)?;
63+
// Wrap in a transaction so we don't poison the outer transaction if this
64+
// fails
65+
let _ = conn.transaction(|| VersionDownload::create_or_increment(version_id, &conn));
6666
Ok(())
6767
}
6868

‎src/tests/krate.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1262,7 +1262,7 @@ fn download() {
12621262

12631263
#[test]
12641264
fn download_nonexistent_version_of_existing_crate_404s() {
1265-
let (app, anon, user) = TestApp::init().with_user();
1265+
let (app, anon, user) = TestApp::with_proxy().with_user();
12661266
let user = user.as_model();
12671267

12681268
app.db(|conn| {

‎src/tests/read_only_mode.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ fn cannot_hit_endpoint_which_writes_db_in_read_only_mode() {
2424
}
2525

2626
#[test]
27-
#[ignore] // Will be implicitly fixed by #1387, no need to special case here
2827
fn can_download_crate_in_read_only_mode() {
2928
let (app, anon, user) = TestApp::with_proxy().with_user();
3029

0 commit comments

Comments
 (0)