Skip to content

Commit

Permalink
Deter other processes from using the same data dir (#169)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
ohsayan committed Jun 12, 2021
1 parent 40093e6 commit ca9e482
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 0 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
51 changes: 51 additions & 0 deletions server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -58,6 +62,8 @@ use tokio::signal;
#[cfg(test)]
mod tests;

const PATH: &str = ".sky_pid";

#[cfg(not(target_env = "msvc"))]
use jemallocator::Jemalloc;

Expand All @@ -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()
Expand Down Expand Up @@ -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();
}

Expand Down Expand Up @@ -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
}

0 comments on commit ca9e482

Please sign in to comment.