Skip to content

Commit 5400248

Browse files
committed
Yanking only updates the database after the index is updated
Previously, we'd immediately update the database and then queue the index update to be run later. Jobs aren't guaranteed to run in the order they were queued (though in practice they likely will). This means that if someone yanked and unyanked a crate in rapid succession, the database may end up permanently out of sync with the index. With this change, the final result may still be out of order, but the database will be in sync with the index whatever the outcome is. Since rapidly yanking and unyanking isn't something we expect users to do, and even if they did jobs should be running in order under normal circumstances, I don't think we need to do anything to ensure more consistency than this.
1 parent 766b29d commit 5400248

File tree

6 files changed

+51
-28
lines changed

6 files changed

+51
-28
lines changed

src/background_jobs.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
use url::Url;
2+
use std::panic::AssertUnwindSafe;
23

34
use crate::background::{Builder, Runner};
5+
use crate::db::{DieselPool, DieselPooledConn};
46
use crate::git::{AddCrate, Yank};
7+
use crate::util::CargoResult;
58

69
pub fn job_runner(config: Builder<Environment>) -> Runner<Environment> {
710
config.register::<AddCrate>().register::<Yank>().build()
@@ -11,12 +14,30 @@ pub fn job_runner(config: Builder<Environment>) -> Runner<Environment> {
1114
pub struct Environment {
1215
pub index_location: Url,
1316
pub credentials: Option<(String, String)>,
17+
// FIXME: https://github.com/sfackler/r2d2/pull/70
18+
pub connection_pool: AssertUnwindSafe<DieselPool>,
1419
}
1520

1621
impl Environment {
22+
pub fn new(
23+
index_location: Url,
24+
credentials: Option<(String, String)>,
25+
connection_pool: DieselPool,
26+
) -> Self {
27+
Self {
28+
index_location,
29+
credentials,
30+
connection_pool: AssertUnwindSafe(connection_pool),
31+
}
32+
}
33+
1734
pub fn credentials(&self) -> Option<(&str, &str)> {
1835
self.credentials
1936
.as_ref()
2037
.map(|(u, p)| (u.as_str(), p.as_str()))
2138
}
39+
40+
pub fn connection(&self) -> CargoResult<DieselPooledConn> {
41+
self.connection_pool.0.get().map_err(Into::into)
42+
}
2243
}

src/bin/background-worker.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,21 @@ use std::time::Duration;
1818
fn main() {
1919
let config = cargo_registry::Config::default();
2020

21+
// We're only using 1 thread, so we only need 2 connections
22+
let db_config = r2d2::Pool::builder().max_size(1);
23+
let db_pool = db::diesel_pool(&config.db_url, config.env, db_config);
24+
2125
let username = env::var("GIT_HTTP_USER");
2226
let password = env::var("GIT_HTTP_PWD");
2327
let credentials = match (username, password) {
2428
(Ok(u), Ok(p)) => Some((u, p)),
2529
_ => None,
2630
};
27-
let environment = Environment {
28-
index_location: config.index_location,
31+
let environment = Environment::new(
32+
config.index_location,
2933
credentials,
30-
};
31-
32-
// We're only using 1 thread, so we only need 1 connection
33-
let db_config = r2d2::Pool::builder().max_size(1);
34-
let db_pool = db::diesel_pool(&config.db_url, config.env, db_config);
34+
db_pool.clone(),
35+
);
3536

3637
let builder = background::Runner::builder(db_pool, environment).thread_count(1);
3738
let runner = job_runner(builder);

src/controllers/version/yank.rs

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,8 @@
33
use crate::controllers::prelude::*;
44

55
use crate::git;
6-
use crate::util::errors::CargoError;
76

87
use crate::models::Rights;
9-
use crate::schema::*;
108

119
use super::version_and_crate;
1210

@@ -39,13 +37,7 @@ fn modify_yank(req: &mut dyn Request, yanked: bool) -> CargoResult<Response> {
3937
}
4038

4139
if version.yanked != yanked {
42-
conn.transaction::<_, Box<dyn CargoError>, _>(|| {
43-
diesel::update(&version)
44-
.set(versions::yanked.eq(yanked))
45-
.execute(&*conn)?;
46-
git::yank(&conn, krate.name, &version.num, yanked)?;
47-
Ok(())
48-
})?;
40+
git::yank(&conn, krate.name, version, yanked)?;
4941
}
5042

5143
#[derive(Serialize)]

src/git.rs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ use url::Url;
1010

1111
use crate::background::Job;
1212
use crate::background_jobs::Environment;
13-
use crate::models::DependencyKind;
13+
use crate::models::{DependencyKind, Version};
14+
use crate::schema::versions;
1415
use crate::util::{internal, CargoResult};
1516

1617
#[derive(Serialize, Deserialize, Debug)]
@@ -155,7 +156,7 @@ pub fn add_crate(conn: &PgConnection, krate: Crate) -> CargoResult<()> {
155156
#[derive(Serialize, Deserialize)]
156157
pub struct Yank {
157158
krate: String,
158-
version: String,
159+
version: Version,
159160
yanked: bool,
160161
}
161162

@@ -168,12 +169,13 @@ impl Job for Yank {
168169
let dst = repo.index_file(&self.krate);
169170

170171
let prev = fs::read_to_string(&dst)?;
172+
let version = self.version.num.to_string();
171173
let new = prev
172174
.lines()
173175
.map(|line| {
174176
let mut git_crate = serde_json::from_str::<Crate>(line)
175177
.map_err(|_| internal(&format_args!("couldn't decode: `{}`", line)))?;
176-
if git_crate.name != self.krate || git_crate.vers != self.version {
178+
if git_crate.name != self.krate || git_crate.vers != version {
177179
return Ok(line.to_string());
178180
}
179181
git_crate.yanked = Some(self.yanked);
@@ -188,11 +190,17 @@ impl Job for Yank {
188190
"{} crate `{}#{}`",
189191
if self.yanked { "Yanking" } else { "Unyanking" },
190192
self.krate,
191-
self.version
193+
self.version.num
192194
),
193195
&repo.relative_index_file(&self.krate),
194196
env.credentials(),
195-
)
197+
)?;
198+
199+
diesel::update(&self.version)
200+
.set(versions::yanked.eq(self.yanked))
201+
.execute(&*env.connection()?)?;
202+
203+
Ok(())
196204
}
197205
}
198206

@@ -203,12 +211,12 @@ impl Job for Yank {
203211
pub fn yank(
204212
conn: &PgConnection,
205213
krate: String,
206-
version: &semver::Version,
214+
version: Version,
207215
yanked: bool,
208216
) -> CargoResult<()> {
209217
Yank {
210218
krate,
211-
version: version.to_string(),
219+
version,
212220
yanked,
213221
}
214222
.enqueue(conn)

src/middleware/run_pending_background_jobs.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ impl Middleware for RunPendingBackgroundJobs {
1313
) -> Result<Response, Box<dyn Error + Send>> {
1414
let app = req.app();
1515
let connection_pool = app.diesel_database.clone();
16-
let environment = Environment {
17-
index_location: app.config.index_location.clone(),
18-
credentials: None,
19-
};
16+
let environment = Environment::new(
17+
app.config.index_location.clone(),
18+
None,
19+
app.diesel_database.clone(),
20+
);
2021

2122
let config = Runner::builder(connection_pool, environment);
2223
let runner = job_runner(config);

src/models/version.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::schema::*;
1010
use crate::views::{EncodableVersion, EncodableVersionLinks};
1111

1212
// Queryable has a custom implementation below
13-
#[derive(Clone, Identifiable, Associations, Debug, Queryable)]
13+
#[derive(Clone, Identifiable, Associations, Debug, Queryable, Deserialize, Serialize)]
1414
#[belongs_to(Crate)]
1515
pub struct Version {
1616
pub id: i32,

0 commit comments

Comments
 (0)