Show resource and lockfile when waiting (#715)

We lock git checkout directories and the virtualenv to avoid two puffin
instances running in parallel changing files at the same time and
leading to a broken state. When one instance is blocking another, we
need to inform the user (why is the program hanging?) and also add some
information for them to debug the situation.

The new messages will print

```
Waiting to acquire lock for /home/konsti/projects/puffin/.venv (lockfile: /home/konsti/projects/puffin/.venv/.lock)
```

or

```
Waiting to acquire lock for git+https://github.com/pydantic/pydantic-extra-types@0ce9f207a1e09a862287ab77512f0060c1625223 (lockfile: /home/konsti/projects/puffin/cache-all-kinds/git-v0/locks/f157fd329a506a34)
```

The messages aren't perfect but clear enough to see what the contention
is and in the worst case to delete the lockfile.

Fixes #714
This commit is contained in:
konsti 2023-12-21 00:05:49 +01:00 committed by GitHub
parent e60f0ec732
commit b7ad97a823
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 15 additions and 5 deletions

View file

@ -1,3 +1,4 @@
use std::fmt::{Debug, Formatter};
use std::hash::{Hash, Hasher};
use std::ops::Deref;
@ -85,6 +86,12 @@ impl Hash for CanonicalUrl {
}
}
impl std::fmt::Display for CanonicalUrl {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&self.0, f)
}
}
/// Like [`CanonicalUrl`], but attempts to represent an underlying source repository, abstracting
/// away details like the specific commit or branch, or the subdirectory to build within the
/// repository.

View file

@ -653,7 +653,8 @@ impl<'a, T: BuildContext> SourceDistCachedBuilder<'a, T> {
// Avoid races between different processes, too.
let lock_dir = git_dir.join("locks");
fs::create_dir_all(&lock_dir).await?;
let _lock = LockedFile::acquire(lock_dir.join(digest(&CanonicalUrl::new(url))))?;
let canonical_url = CanonicalUrl::new(url);
let _lock = LockedFile::acquire(lock_dir.join(digest(&canonical_url)), &canonical_url)?;
let DirectGitUrl { url, subdirectory } =
DirectGitUrl::try_from(url).map_err(SourceDistError::Git)?;

View file

@ -1,3 +1,4 @@
use std::fmt::Display;
use std::path::{Path, PathBuf};
use fs2::FileExt;
@ -99,14 +100,15 @@ pub fn directories(path: impl AsRef<Path>) -> impl Iterator<Item = PathBuf> {
pub struct LockedFile(fs_err::File);
impl LockedFile {
pub fn acquire(path: impl AsRef<Path>) -> Result<Self, std::io::Error> {
pub fn acquire(path: impl AsRef<Path>, resource: impl Display) -> Result<Self, std::io::Error> {
let file = fs_err::File::create(path.as_ref())?;
match file.file().try_lock_exclusive() {
Ok(()) => Ok(Self(file)),
Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => {
warn_user!(
"Waiting to acquire lock on {}",
path.as_ref().parent().unwrap_or(path.as_ref()).display()
"Waiting to acquire lock for {} (lockfile: {})",
resource,
path.as_ref().display()
);
file.file().lock_exclusive()?;
Ok(Self(file))

View file

@ -104,7 +104,7 @@ impl Virtualenv {
/// Lock the virtual environment to prevent concurrent writes.
pub fn lock(&self) -> Result<LockedFile, std::io::Error> {
LockedFile::acquire(self.root.join(".lock"))
LockedFile::acquire(self.root.join(".lock"), self.root.display())
}
}