Skip to content

Commit

Permalink
feat: use file exclusive lock to prevent start instance twice
Browse files Browse the repository at this point in the history
  • Loading branch information
J0HN50N133 committed Apr 15, 2024
1 parent 344b01c commit 9c44455
Show file tree
Hide file tree
Showing 9 changed files with 95 additions and 3 deletions.
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ resolver = "2"
binrw = "0.13"
bytes = { version = "1.6", features = ["serde"] }
derive_builder = "0.20"
fs4 = { version = "0.8", features = ["sync"] }
futures = { version = "0.3", features = ["std"] }
once_cell = "1.19"
parking_lot = "0.12"
Expand Down
6 changes: 6 additions & 0 deletions src/roxy/src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ pub struct StorageConfig {
rocksdb: RocksDBConfig,
}

impl StorageConfig {
pub fn dbpath(&self) -> &str {
&self.dbpath
}
}

#[derive(
Debug, Clone, Copy, Default, PartialEq, Eq, VariantArray, strum::IntoStaticStr, strum::Display,
)]
Expand Down
1 change: 1 addition & 0 deletions src/server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ path = "src/rudeus.rs"

[dependencies]
bytes.workspace = true
fs4.workspace = true
futures.workspace = true
redis-protocol.workspace = true
roxy.workspace = true
Expand Down
12 changes: 12 additions & 0 deletions src/server/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,18 @@ pub enum Error {
UnknownCommand { cmd: String },
#[snafu(transparent)]
ExecuteError { source: commands::error::Error },
#[snafu(display("Failed to bind to address '{}': {}", bind, source))]
BindError {
bind: String,
source: std::io::Error,
},
#[snafu(display("Another instance is already Instaunning"))]
InstanceAlreadyExists { source: std::io::Error },
#[snafu(display("Failed to acquire process file lock: '{}'", path))]
AcquireFileLock {
source: std::io::Error,
path: String,
},
}

pub type Result<T> = std::result::Result<T, Error>;
1 change: 1 addition & 0 deletions src/server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@

pub mod connection;
pub mod error;
pub mod rudeus_lock;
pub mod server;
4 changes: 3 additions & 1 deletion src/server/src/rudeus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use common_runtime::global_runtime::{block_on_network, init_global_runtimes};
use common_telemetry::log::{self, LoggingOption};
use roxy::storage::{Storage, StorageConfig};
use serde::{Deserialize, Serialize};
use server::rudeus_lock::try_lock_rudeus;
use server::server::{Server, ServerConfig};

#[derive(Debug, Deserialize, Serialize)]
Expand All @@ -35,11 +36,12 @@ fn main() -> Result<(), Box<dyn Error>> {

let config: RudeusConfig = toml::from_str(&config_str)?;
let _log_workers = log::init(&config.logging);
let _rudeus_lock = try_lock_rudeus(config.storage.dbpath())?;

let mut storage = Storage::try_new(config.storage)?;
storage.open(roxy::storage::OpenMode::Default)?;
let server = Server::new(Arc::new(storage), config.server);

block_on_network(server.start());
block_on_network(server.start())?;
Ok(())
}
54 changes: 54 additions & 0 deletions src/server/src/rudeus_lock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright 2024 Rudeus Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Rudeus lock is a simple file lock implementation for ensuring only one instance of a program is running.
use std::fs::{File, OpenOptions};

use fs4::FileExt as _;
use snafu::ResultExt as _;

use crate::error::{AcquireFileLockSnafu, InstanceAlreadyExistsSnafu, Result};

pub struct RudeusLock {
file: File,
}

impl RudeusLock {
fn new(file: File) -> Result<Self> {
file.try_lock_exclusive()
.context(InstanceAlreadyExistsSnafu)?;
Ok(Self { file })
}
}

impl Drop for RudeusLock {
fn drop(&mut self) {
self.file.unlock().unwrap();
}
}

pub fn try_lock_rudeus(storage_path: &str) -> Result<RudeusLock> {
let path = std::path::Path::new(storage_path).join("rudeus.lock");
let file = OpenOptions::new()
.write(true)
.read(true)
.create(true)
.truncate(true)
.open(&path)
.context(AcquireFileLockSnafu {
path: path.display().to_string(),
})?;
RudeusLock::new(file)
}
8 changes: 6 additions & 2 deletions src/server/src/server.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use common_telemetry::log::info;
use roxy::storage::{Storage, StorageRef};
use serde::{Deserialize, Serialize};
use snafu::ResultExt;
use tokio::net::TcpListener;

use crate::connection::Connection;
use crate::error::{BindSnafu, Result};

#[derive(Debug, Serialize, Deserialize)]
pub struct ServerConfig {
Expand All @@ -27,10 +29,12 @@ impl Server {
&self.storage
}

pub async fn start(&self) {
pub async fn start(&self) -> Result<()> {
// static self here is to make sure that the server is alive for the lifetime of the program
info!("Rudeus listening on: {}", self.bind);
let listener = TcpListener::bind(&self.bind).await.unwrap();
let listener = TcpListener::bind(&self.bind).await.context(BindSnafu {
bind: self.bind.clone(),
})?;
loop {
let stream = listener.accept().await.map(|(socket, _)| socket).unwrap();
let storage = self.storage.clone();
Expand Down

0 comments on commit 9c44455

Please sign in to comment.