Add advisory locking for POSIX systems

This commit adds a basic implementation of POSIX advisory record locking
which sets a lock on the `data.bin` file when the database server starts
and releases the lock when it terminates. This is just done for
compliance to let other processes know that we don't want them to use
the file.

However, the result depends entirely on the process that wants to do
'something' with the file. It is the responsibility of the process to
ensure that it respects the file lock.

Also, exclusive locks aren't perfect on Linux, so we can't rely on them.
See discussion #123 for more information.
next
Sayan Nandan 3 years ago
parent 1be348f5a2
commit a9710587bb

10
Cargo.lock generated

@ -58,9 +58,9 @@ checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040"
[[package]]
name = "cc"
version = "1.0.66"
version = "1.0.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48"
checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"
[[package]]
name = "cfg-if"
@ -299,9 +299,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.86"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c"
checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
[[package]]
name = "libsky"
@ -686,12 +686,14 @@ version = "0.5.1"
dependencies = [
"bincode",
"bytes",
"cc",
"chrono",
"clap",
"env_logger",
"futures",
"jemallocator",
"lazy_static",
"libc",
"libsky",
"log",
"openssl",

@ -3,9 +3,7 @@ name = "skyd"
version = "0.5.1"
authors = ["Sayan Nandan <ohsayan@outlook.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
build = "build.rs"
[dependencies]
tokio = { version = "1.5.0", features = ["full"] }
bytes = "1.0.1"
@ -30,3 +28,9 @@ jemallocator = "0.3.2"
[dev-dependencies]
tokio = { version = "1.5.0", features = ["test-util"] }
[target.'cfg(unix)'.build-dependencies]
cc = "1.0.67"
[target.'cfg(unix)'.dependencies]
libc = "0.2.93"

@ -0,0 +1,6 @@
use cc;
fn main() {
cc::Build::new()
.file("native/fscposix.c")
.compile("libflock.a")
}

@ -63,6 +63,9 @@ use tokio::net::TcpStream;
use tokio::sync::Semaphore;
use tokio::sync::{broadcast, mpsc};
use tokio::time::{self, Duration};
#[cfg(unix)]
use crate::diskstore::recordlock;
/// Responsible for gracefully shutting down the server instead of dying randomly
// Sounds very sci-fi ;)
pub struct Terminator {
@ -469,6 +472,15 @@ pub async fn run(
process::exit(0x100);
}
};
#[cfg(unix)]
let lock = match recordlock::FileLock::lock("data.bin") {
Ok(lock) => lock,
Err(e) => {
log::error!("Failed to acquire lock on data file with error: '{}'", e);
process::exit(0x100);
}
};
match fs::create_dir_all(&*DIR_REMOTE_SNAPSHOT) {
Ok(_) => (),
Err(e) => match e.kind() {
@ -549,6 +561,11 @@ pub async fn run(
}
}
}
#[cfg(unix)]
if let Err(e) = lock.unlock() {
log::error!("Failed to release lock on data file with error: '{}'", e);
process::exit(0x100);
}
terminal::write_info("Goodbye :)\n").unwrap();
}

@ -39,6 +39,8 @@ use std::path::PathBuf;
use std::time::Duration;
use tokio::time;
pub mod snapshot;
#[cfg(unix)]
pub mod recordlock;
mod snapstore;
/// This type alias is to be used when deserializing binary data from disk

@ -0,0 +1,69 @@
/*
* Created on Fri Apr 16 2021
*
* This file is a part of Skytable
* Skytable (formerly known as TerrabaseDB or Skybase) is a free and open-source
* NoSQL database written by Sayan Nandan ("the Author") with the
* vision to provide flexibility in data modelling without compromising
* on performance, queryability or scalability.
*
* Copyright (c) 2020, Sayan Nandan <ohsayan@outlook.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
use libc::c_int;
use std::fs::File;
use std::fs::OpenOptions;
use std::io::Error;
use std::os::unix::io::AsRawFd;
extern "C" {
fn lock_file(fd: i32) -> c_int;
fn unlock_file(fd: i32) -> c_int;
}
#[derive(Debug)]
pub struct FileLock {
file: File,
}
impl FileLock {
pub fn lock(filename: &str) -> Result<Self, Error> {
let file = OpenOptions::new()
.read(false)
.write(true)
.create(true)
.open(&filename)?;
let raw_err = unsafe { lock_file(file.as_raw_fd()) };
match raw_err {
0 => Ok(FileLock { file }),
x @ _ => Err(Error::from_raw_os_error(x)),
}
}
pub fn unlock(&self) -> Result<(), Error> {
let raw_err = unsafe { unlock_file(self.file.as_raw_fd()) };
match raw_err {
0 => Ok(()),
x @ _ => Err(Error::from_raw_os_error(x)),
}
}
}
impl Drop for FileLock {
fn drop(&mut self) {
assert!(self.unlock().is_ok());
}
}
Loading…
Cancel
Save