Deter other processes from using the same data dir (#169)

* Deter other processes from using the same data dir

For more information, see #167

* Don't lock `pid_file`

Windows has mandatory locking so second instance won't be able to read
the PID of the other process. We'll just keep the file descriptor/handle
open
next
Sayan 3 years ago committed by GitHub
parent 40093e602c
commit ca9e482f47
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -2,6 +2,12 @@
All changes in this project will be noted in this file.
## Unreleased
### Fixes
- The save operation now automatically attempts to recover on failure during termination [see [#166](https://github.com/skytable/skytable/pull/166)]
- More than one process can no longer concurrently use the same data directory, preventing any possible data corruption [see [#169](https://github.com/skytable/skytable/pull/169), [#167](https://github.com/skytable/skytable/issues/167)]
## Version 0.6.1 [2021-06-07]
> No breaking changes

@ -35,10 +35,14 @@ use crate::config::PortConfig;
use crate::config::SnapshotConfig;
use libsky::URL;
use libsky::VERSION;
use std::io::Write;
use std::path;
use std::thread;
use std::time;
mod config;
use std::env;
use std::fs;
use std::process;
mod actions;
mod admin;
mod coredb;
@ -58,6 +62,8 @@ use tokio::signal;
#[cfg(test)]
mod tests;
const PATH: &str = ".sky_pid";
#[cfg(not(target_env = "msvc"))]
use jemallocator::Jemalloc;
@ -73,6 +79,8 @@ fn main() {
Builder::new()
.parse_filters(&env::var("SKY_LOG").unwrap_or_else(|_| "info".to_owned()))
.init();
// check if any other process is using the data directory and lock it if not (else error)
let pid_file = run_pre_startup_tasks();
// Start the server which asynchronously waits for a CTRL+C signal
// which will safely shut down the server
let runtime = tokio::runtime::Builder::new_multi_thread()
@ -117,6 +125,12 @@ fn main() {
}
thread::sleep(time::Duration::from_secs(10));
}
// close the PID file and remove it
drop(pid_file);
if let Err(e) = fs::remove_file(PATH) {
log::error!("Shutdown failure: Failed to remove pid file: {}", e);
process::exit(0x100);
}
terminal::write_info("Goodbye :)\n").unwrap();
}
@ -146,3 +160,40 @@ async fn check_args_and_get_cfg() -> (PortConfig, BGSave, SnapshotConfig, Option
};
binding_and_cfg
}
/// On startup, we attempt to check if a `.sky_pid` file exists. If it does, then
/// this file will contain the kernel/operating system assigned process ID of the
/// skyd process. We will attempt to read that and log an error complaining that
/// the directory is in active use by another process. If the file doesn't then
/// we're free to create our own file and write our own PID to it. Any subsequent
/// processes will detect this and this helps us prevent two processes from writing
/// to the same directory which can cause potentially undefined behavior.
///
fn run_pre_startup_tasks() -> fs::File {
let path = path::Path::new(PATH);
if path.exists() {
let pid = fs::read_to_string(path).unwrap_or_else(|_| "unknown".to_owned());
log::error!(
"Startup failure: Another process with parent PID {} is using the data directory",
pid
);
process::exit(0x100);
}
let mut file = match fs::OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(PATH)
{
Ok(fle) => fle,
Err(e) => {
log::error!("Startup failure: Failed to open pid file: {}", e);
process::exit(0x100);
}
};
if let Err(e) = file.write_all(process::id().to_string().as_bytes()) {
log::error!("Startup failure: Failed to write to pid file: {}", e);
process::exit(0x100);
}
file
}

Loading…
Cancel
Save