Skip to content

Commit 6777978

Browse files
committed
Maintain a persistent checkout of the git index
The time a full clone takes caused too long of a delay when publishing a crate. This instead performs a full clone when the runner boots, and then locks and does `git fetch && git reset --hard origin/master` at the top of every job. The reset means we can (somewhat) scale to multiple runners, and retains unwind safety in the environment. Note that there can still be a delay measured in minutes if a publish is performed right after the server boots, as the job runner starting up does not block the web server from starting.
1 parent fc6c31e commit 6777978

File tree

4 files changed

+39
-11
lines changed

4 files changed

+39
-11
lines changed

src/background_jobs.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use std::panic::AssertUnwindSafe;
2-
use url::Url;
2+
use std::sync::{Mutex, MutexGuard};
33

44
use crate::background::{Builder, Runner};
55
use crate::db::{DieselPool, DieselPooledConn};
6-
use crate::git::{AddCrate, Yank};
6+
use crate::git::{AddCrate, Repository, Yank};
77
use crate::util::CargoResult;
88

99
pub fn job_runner(config: Builder<Environment>) -> Runner<Environment> {
@@ -12,20 +12,20 @@ pub fn job_runner(config: Builder<Environment>) -> Runner<Environment> {
1212

1313
#[allow(missing_debug_implementations)]
1414
pub struct Environment {
15-
pub index_location: Url,
15+
index: Mutex<Repository>,
1616
pub credentials: Option<(String, String)>,
1717
// FIXME: https://github.com/sfackler/r2d2/pull/70
1818
pub connection_pool: AssertUnwindSafe<DieselPool>,
1919
}
2020

2121
impl Environment {
2222
pub fn new(
23-
index_location: Url,
23+
index: Repository,
2424
credentials: Option<(String, String)>,
2525
connection_pool: DieselPool,
2626
) -> Self {
2727
Self {
28-
index_location,
28+
index: Mutex::new(index),
2929
credentials,
3030
connection_pool: AssertUnwindSafe(connection_pool),
3131
}
@@ -40,4 +40,11 @@ impl Environment {
4040
pub fn connection(&self) -> CargoResult<DieselPooledConn> {
4141
self.connection_pool.0.get().map_err(Into::into)
4242
}
43+
44+
pub fn lock_index(&self) -> CargoResult<MutexGuard<'_, Repository>> {
45+
let repo = self.index.lock()
46+
.unwrap_or_else(|e| e.into_inner());
47+
repo.reset_head()?;
48+
Ok(repo)
49+
}
4350
}

src/bin/background-worker.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,15 @@
1010
#![deny(warnings)]
1111

1212
use cargo_registry::{background, background_jobs::*, db};
13+
use cargo_registry::git::Repository;
1314
use diesel::r2d2;
1415
use std::env;
1516
use std::thread::sleep;
1617
use std::time::Duration;
1718

1819
fn main() {
20+
println!("Booting runner");
21+
1922
let config = cargo_registry::Config::default();
2023

2124
// We're only using 1 thread, so we only need 2 connections
@@ -28,7 +31,13 @@ fn main() {
2831
(Ok(u), Ok(p)) => Some((u, p)),
2932
_ => None,
3033
};
31-
let environment = Environment::new(config.index_location, credentials, db_pool.clone());
34+
35+
println!("Cloning index");
36+
37+
let repository = Repository::open(&config.index_location)
38+
.expect("Failed to clone index");
39+
40+
let environment = Environment::new(repository, credentials, db_pool.clone());
3241

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

src/git.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,13 @@ pub struct Dependency {
3939
pub package: Option<String>,
4040
}
4141

42-
struct Repository {
42+
pub struct Repository {
4343
checkout_path: TempDir,
4444
repository: git2::Repository,
4545
}
4646

4747
impl Repository {
48-
fn open(url: &Url) -> CargoResult<Self> {
48+
pub fn open(url: &Url) -> CargoResult<Self> {
4949
let checkout_path = TempDir::new("git")?;
5050
let repository = git2::Repository::clone(url.as_str(), checkout_path.path())?;
5151

@@ -120,6 +120,15 @@ impl Repository {
120120
}
121121
ref_status
122122
}
123+
124+
pub fn reset_head(&self) -> CargoResult<()> {
125+
let mut origin = self.repository.find_remote("origin")?;
126+
origin.fetch(&["refs/heads/*:refs/heads/*"], None, None)?;
127+
let head = self.repository.head()?.target().unwrap();
128+
let obj = self.repository.find_object(head, None)?;
129+
self.repository.reset(&obj, git2::ResetType::Hard, None)?;
130+
Ok(())
131+
}
123132
}
124133

125134
#[derive(Deserialize, Serialize)]
@@ -132,7 +141,7 @@ impl Job for AddCrate {
132141
const JOB_TYPE: &'static str = "add_crate";
133142

134143
fn perform(self, env: &Self::Environment) -> CargoResult<()> {
135-
let repo = Repository::open(&env.index_location)?;
144+
let repo = env.lock_index()?;
136145
let dst = repo.index_file(&self.krate.name);
137146

138147
// Add the crate to its relevant file
@@ -165,7 +174,7 @@ impl Job for Yank {
165174
const JOB_TYPE: &'static str = "yank";
166175

167176
fn perform(self, env: &Self::Environment) -> CargoResult<()> {
168-
let repo = Repository::open(&env.index_location)?;
177+
let repo = env.lock_index()?;
169178
let dst = repo.index_file(&self.krate);
170179

171180
let conn = env.connection()?;

src/middleware/run_pending_background_jobs.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use super::app::RequestApp;
22
use super::prelude::*;
33
use crate::background::Runner;
44
use crate::background_jobs::*;
5+
use crate::git::Repository;
56

67
pub struct RunPendingBackgroundJobs;
78

@@ -13,8 +14,10 @@ impl Middleware for RunPendingBackgroundJobs {
1314
) -> Result<Response, Box<dyn Error + Send>> {
1415
let app = req.app();
1516
let connection_pool = app.diesel_database.clone();
17+
let repo = Repository::open(&app.config.index_location)
18+
.expect("Could not clone index");
1619
let environment = Environment::new(
17-
app.config.index_location.clone(),
20+
repo,
1821
None,
1922
app.diesel_database.clone(),
2023
);

0 commit comments

Comments
 (0)