mirror of
https://github.com/neondatabase/neon.git
synced 2026-01-04 03:52:56 +00:00
Move daemonization logic into `control_plane`. Storage binaries now only crate a lockfile to avoid concurrent services running in the same directory.
82 lines
3.1 KiB
Rust
82 lines
3.1 KiB
Rust
//! A module to create and read lock files. A lock file ensures that only one
|
|
//! process is running at a time, in a particular directory.
|
|
//!
|
|
//! File locking is done using [`fcntl::flock`], which means that holding the
|
|
//! lock on file only prevents acquiring another lock on it; all other
|
|
//! operations are still possible on files. Other process can still open, read,
|
|
//! write, or remove the file, for example.
|
|
//! If the file is removed while a process is holding a lock on it,
|
|
//! the process that holds the lock does not get any error or notification.
|
|
//! Furthermore, you can create a new file with the same name and lock the new file,
|
|
//! while the old process is still running.
|
|
//! Deleting the lock file while the locking process is still running is a bad idea!
|
|
|
|
use std::{fs, os::unix::prelude::AsRawFd, path::Path};
|
|
|
|
use anyhow::Context;
|
|
use nix::fcntl;
|
|
|
|
use crate::crashsafe;
|
|
|
|
pub enum LockCreationResult {
|
|
Created {
|
|
new_lock_contents: String,
|
|
file: fs::File,
|
|
},
|
|
AlreadyLocked {
|
|
existing_lock_contents: String,
|
|
},
|
|
CreationFailed(anyhow::Error),
|
|
}
|
|
|
|
/// Creates a lock file in the path given and writes the given contents into the file.
|
|
/// Note: The lock is automatically released when the file closed. You might want to use Box::leak to make sure it lives until the end of the program.
|
|
pub fn create_lock_file(lock_file_path: &Path, contents: String) -> LockCreationResult {
|
|
let lock_file = match fs::OpenOptions::new()
|
|
.create(true) // O_CREAT
|
|
.write(true)
|
|
.open(lock_file_path)
|
|
.context("Failed to open lock file")
|
|
{
|
|
Ok(file) => file,
|
|
Err(e) => return LockCreationResult::CreationFailed(e),
|
|
};
|
|
|
|
match fcntl::flock(
|
|
lock_file.as_raw_fd(),
|
|
fcntl::FlockArg::LockExclusiveNonblock,
|
|
) {
|
|
Ok(()) => {
|
|
match lock_file
|
|
.set_len(0)
|
|
.context("Failed to truncate lockfile")
|
|
.and_then(|()| {
|
|
fs::write(lock_file_path, &contents).with_context(|| {
|
|
format!("Failed to write '{contents}' contents into lockfile")
|
|
})
|
|
})
|
|
.and_then(|()| {
|
|
crashsafe::fsync_file_and_parent(lock_file_path)
|
|
.context("Failed to fsync lockfile")
|
|
}) {
|
|
Ok(()) => LockCreationResult::Created {
|
|
new_lock_contents: contents,
|
|
file: lock_file,
|
|
},
|
|
Err(e) => LockCreationResult::CreationFailed(e),
|
|
}
|
|
}
|
|
Err(nix::errno::Errno::EAGAIN) => {
|
|
match fs::read_to_string(lock_file_path).context("Failed to read lockfile contents") {
|
|
Ok(existing_lock_contents) => LockCreationResult::AlreadyLocked {
|
|
existing_lock_contents,
|
|
},
|
|
Err(e) => LockCreationResult::CreationFailed(e),
|
|
}
|
|
}
|
|
Err(e) => {
|
|
LockCreationResult::CreationFailed(anyhow::anyhow!("Failed to lock lockfile: {e}"))
|
|
}
|
|
}
|
|
}
|