Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
a64c9aa
chore: add wal files
wHoIsDReAmer Nov 21, 2024
54ec7fa
feat(WAL): 매니저 추가 & 에러, 타입 정의 & 메서드 타입 틀 추가
wHoIsDReAmer Nov 21, 2024
ba1572e
feat: WALBuilder 구현
wHoIsDReAmer Nov 21, 2024
20dd6ed
feat(WALManager): append 작업 정의 & 파일 저장 구현
wHoIsDReAmer Nov 21, 2024
4aef420
chore: WAL 디렉터리 이름, 확장자 추가 & config에 WAL 정보 추가 & WALManager 리팩터링
wHoIsDReAmer Nov 22, 2024
6674a34
fix(initializer): WAL 디렉토리 추가 & 테스트 케이스 알맞게 변경
wHoIsDReAmer Nov 22, 2024
2b6d3a7
feat: 유연함을 위해 encoding / decoding 동작 분리
wHoIsDReAmer Nov 22, 2024
610dfd2
chore: WAL 디렉터리 생성 실패 테스트 추가
wHoIsDReAmer Nov 22, 2024
6ef4303
feat(server): 서버 시작 시 WAL Initializing & process_query에 WALManager 주입
wHoIsDReAmer Nov 27, 2024
61bd47f
refactor(wal): implements로 구현체 분리 및 manager 코드 정리
wHoIsDReAmer May 13, 2025
131c859
refactor(wal): 파일 이름 저장 시 8진수로 저장
wHoIsDReAmer May 13, 2025
3d79b5f
fix(walmanager): 기존 WAL 데이터 복구 로직 수정
wHoIsDReAmer May 13, 2025
1d1b673
test: WALManager 테스트 케이스 작성
wHoIsDReAmer May 13, 2025
2e90ce3
fix(wal): load_data 메소드 동작 수정
wHoIsDReAmer May 13, 2025
f65d69f
refactor(wal): manager 계층화 및 builder 분리, 대용량 엔트리 데이터 분리 구현
wHoIsDReAmer May 16, 2025
f849144
test(wal): 엔트리 분리 테스트 코드 작성
wHoIsDReAmer May 16, 2025
e1a4368
feat(wal): 트랜잭션 롤백 타입 추가
wHoIsDReAmer Aug 28, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ uuid = "1.1.2"
itertools = "0.10.5"
anyhow = "1.0.86"
mockall = "0.12.1"
bitcode = "0.6.3"

[target.'cfg(windows)'.dependencies]
winreg = "0.10.1"
Expand Down
6 changes: 6 additions & 0 deletions src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ pub const DEFAULT_CONFIG_FILENAME: &str = "rrdb.config";
// 기본 Data 디렉터리 이름
pub const DEFAULT_DATA_DIRNAME: &str = "data";

// 기본 WAL 디렉터리 이름
pub const DEFAULT_WAL_DIRNAME: &str = "wal";

// 기본 WAL 확장자
pub const DEFAULT_WAL_EXTENSION: &str = "log";

// 운영체제별 기본 저장 경로를 반환합니다.
#[cfg(target_os = "linux")]
pub const DEFAULT_CONFIG_BASEPATH: &str = "/var/lib/rrdb";
Expand Down
10 changes: 9 additions & 1 deletion src/errors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub mod parsing_error;
pub mod predule;
pub mod server_error;
pub mod type_error;
pub mod wal_errors;

#[derive(Debug, PartialEq)]
pub enum RRDBError {
Expand All @@ -14,6 +15,7 @@ pub enum RRDBError {
ParsingError(parsing_error::ParsingError),
ServerError(server_error::ServerError),
TypeError(type_error::TypeError),
WALError(wal_errors::WALError),
}

impl ToString for RRDBError {
Expand All @@ -25,13 +27,16 @@ impl ToString for RRDBError {
RRDBError::ParsingError(e) => e.to_string(),
RRDBError::ServerError(e) => e.to_string(),
RRDBError::TypeError(e) => e.to_string(),
RRDBError::WALError(e) => e.to_string(),
}
}
}

#[cfg(test)]
mod tests {
use predule::{ExecuteError, IntoError, LexingError, ParsingError, ServerError, TypeError};
use predule::{
ExecuteError, IntoError, LexingError, ParsingError, ServerError, TypeError, WALError,
};

use super::*;

Expand All @@ -54,5 +59,8 @@ mod tests {

let error = TypeError::wrap("test");
assert!(error.to_string().contains("test"));

let error = WALError::wrap("test");
assert!(error.to_string().contains("test"));
}
}
1 change: 1 addition & 0 deletions src/errors/predule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ pub use super::lexing_error::*;
pub use super::parsing_error::*;
pub use super::server_error::*;
pub use super::type_error::*;
pub use super::wal_errors::*;
55 changes: 55 additions & 0 deletions src/errors/wal_errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use super::RRDBError;

#[derive(Debug)]
pub struct WALError {
pub message: String,
pub backtrace: std::backtrace::Backtrace,
}

impl PartialEq for WALError {
fn eq(&self, other: &Self) -> bool {
self.message == other.message
}
}

impl WALError {
pub fn wrap<T: ToString>(message: T) -> RRDBError {
RRDBError::WALError(Self {
message: message.to_string(),
backtrace: std::backtrace::Backtrace::capture(),
})
}
}

impl std::error::Error for WALError {}

impl std::fmt::Display for WALError {
fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "wal error: {}", self.message)
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_wal_error_eq() {
let error1 = WALError::wrap("test");
let error2 = WALError::wrap("test");
assert_eq!(error1, error2);
}

#[test]
fn test_wal_error_display() {
let error = WALError::wrap("test");

assert_eq!(error.to_string(), "wal error: test");
}

#[test]
fn test_wal_error_wrap() {
let error = WALError::wrap("test");
assert_eq!(error.to_string(), "wal error: test");
}
}
18 changes: 17 additions & 1 deletion src/executor/config/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,21 @@ use std::path::PathBuf;

use serde::{Deserialize, Serialize};

use crate::constants::{DEFAULT_CONFIG_BASEPATH, DEFAULT_CONFIG_FILENAME, DEFAULT_DATA_DIRNAME};
use crate::constants::{
DEFAULT_CONFIG_BASEPATH, DEFAULT_CONFIG_FILENAME, DEFAULT_DATA_DIRNAME, DEFAULT_WAL_DIRNAME,
DEFAULT_WAL_EXTENSION,
};

#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct GlobalConfig {
pub port: u32,
pub host: String,
pub data_directory: String,

pub wal_enabled: bool,
pub wal_directory: String,
pub wal_segment_size: u32,
pub wal_extension: String,
}

#[allow(clippy::derivable_impls)]
Expand All @@ -24,6 +32,14 @@ impl std::default::Default for GlobalConfig {
.to_str()
.unwrap()
.to_string(),
wal_enabled: true,
wal_directory: base_path
.join(DEFAULT_WAL_DIRNAME)
.to_str()
.unwrap()
.to_string(),
wal_segment_size: 1024 * 1024 * 16, // 16MB 세그먼트 사이즈
wal_extension: DEFAULT_WAL_EXTENSION.to_string(),
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/executor/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ use crate::errors::execute_error::ExecuteError;
use crate::errors::RRDBError;
use crate::executor::predule::ExecuteResult;
use crate::logger::predule::Logger;
use crate::wal::endec::implements::bitcode::BitcodeEncoder;
use crate::wal::manager::WALManager;

use super::config::global::GlobalConfig;
use super::mocking::{CommandRunner, FileSystem, RealCommandRunner, RealFileSystem};
Expand All @@ -28,10 +30,13 @@ impl Executor {
pub async fn process_query(
&self,
statement: SQLStatement,
wal_manager: Arc<WALManager<BitcodeEncoder>>,
_connection_id: String,
) -> Result<ExecuteResult, RRDBError> {
Logger::info(format!("AST echo: {:?}", statement));

// TODO: WAL 로깅 추가

// 쿼리 실행
let result = match statement {
SQLStatement::DDL(DDLStatement::CreateDatabaseQuery(query)) => {
Expand Down
4 changes: 3 additions & 1 deletion src/executor/implements/dml/scan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ impl Executor {
Err(ExecuteError::wrap(format!("full scan failed {}", error)))
}
},
Err(error) => Err(ExecuteError::wrap(format!("full scan failed {}", error))),
Err(error) => {
Err(ExecuteError::wrap(format!("full scan failed {}", error)))
}
}
});

Expand Down
119 changes: 109 additions & 10 deletions src/executor/initializer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,13 @@ impl Executor {
// 3. 데이터 디렉터리 생성 (없다면)
self.create_data_directory_if_not_exists().await?;

// 4. 데몬 설정파일 생성 (없다면)
// 4. WAL 디렉터리 생성 (없다면)
self.create_wal_directory_if_not_exists().await?;

// 5. 데몬 설정파일 생성 (없다면)
self.create_daemon_config_if_not_exists().await?;

// 5. 데몬 실행
// 6. 데몬 실행
self.start_daemon().await?;

Ok(())
Expand Down Expand Up @@ -96,6 +99,18 @@ impl Executor {
Ok(())
}

async fn create_wal_directory_if_not_exists(&self) -> Result<(), RRDBError> {
let wal_path = self.config.wal_directory.clone();

if let Err(error) = self.file_system.create_dir(&wal_path).await {
if error.kind() != std::io::ErrorKind::AlreadyExists {
return Err(ExecuteError::wrap(error.to_string()));
}
}

Ok(())
}

#[cfg(target_os = "linux")]
async fn create_daemon_config_if_not_exists(&self) -> Result<(), RRDBError> {
use crate::constants::SYSTEMD_DAEMON_SCRIPT;
Expand Down Expand Up @@ -188,8 +203,9 @@ mod tests {
async fn test_init_config() {
use mockall::predicate::eq;

use crate::executor::mocking::{
CommandRunner, FileSystem, MockCommandRunner, MockFileSystem,
use crate::{
constants::{DEFAULT_DATA_DIRNAME, DEFAULT_WAL_DIRNAME},
executor::mocking::{CommandRunner, FileSystem, MockCommandRunner, MockFileSystem},
};

use super::*;
Expand All @@ -198,6 +214,10 @@ mod tests {
const CONFIG: &[u8] = br##"port = 22208
host = "0.0.0.0"
data_directory = "/var/lib/rrdb/data"
wal_enabled = true
wal_directory = "/var/lib/rrdb/wal"
wal_segment_size = 16777216
wal_extension = "log"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: 추후에는 yaml 이든, 자체 포맷 구현이든 포맷 하나 골라서 구조체 형태로 기본값 정의하기

"##;

use crate::constants::SYSTEMD_DAEMON_SCRIPT;
Expand Down Expand Up @@ -240,10 +260,20 @@ data_directory = "/var/lib/rrdb/data"
// 3. 데이터 디렉터리 생성
mock.expect_create_dir()
.times(1)
.with(eq(DEFAULT_CONFIG_BASEPATH.to_owned() + "/data"))
.with(eq(DEFAULT_CONFIG_BASEPATH.to_owned()
+ "/"
+ DEFAULT_DATA_DIRNAME))
.returning(|_| Ok(()));

// 4. WAL 디렉터리 생성
mock.expect_create_dir()
.times(1)
.with(eq(DEFAULT_CONFIG_BASEPATH.to_owned()
+ "/"
+ DEFAULT_WAL_DIRNAME))
.returning(|_| Ok(()));

// 4. 데몬 설정파일 생성
// 5. 데몬 설정파일 생성
mock.expect_write_file()
.times(1)
.with(
Expand Down Expand Up @@ -294,10 +324,19 @@ data_directory = "/var/lib/rrdb/data"

// 3. 데이터 디렉터리 생성
mock.expect_create_dir()
.with(eq(DEFAULT_CONFIG_BASEPATH.to_owned() + "/data"))
.with(eq(DEFAULT_CONFIG_BASEPATH.to_owned()
+ "/"
+ DEFAULT_DATA_DIRNAME))
.returning(|_| Ok(()));

// 4. 데몬 설정파일 생성
// 4. WAL 디렉터리 생성
mock.expect_create_dir()
.with(eq(DEFAULT_CONFIG_BASEPATH.to_owned()
+ "/"
+ DEFAULT_WAL_DIRNAME))
.returning(|_| Ok(()));

// 5. 데몬 설정파일 생성
mock.expect_write_file()
.with(
eq("/etc/systemd/system/rrdb.service"),
Expand All @@ -316,6 +355,56 @@ data_directory = "/var/lib/rrdb/data"
Arc::new(mock)
}),
},
TestCase {
name: "WAL 디렉터리 생성 실패",
want_error: true,
mock_config: Box::new(|| {
let config = GlobalConfig::default();

Arc::new(config)
}),
mock_file_system: Box::new(move || {
let mut mock = MockFileSystem::new();

// 1. 최상위 디렉터리 생성
mock.expect_create_dir()
.times(1)
.with(eq(DEFAULT_CONFIG_BASEPATH))
.returning(|_| Ok(()));

// 2. 전역 설정파일 생성
mock.expect_write_file()
.times(1)
.with(
eq(DEFAULT_CONFIG_BASEPATH.to_owned() + "/" + DEFAULT_CONFIG_FILENAME),
eq(CONFIG),
)
.returning(|_, _| Ok(()));

// 3. 데이터 디렉터리 생성
mock.expect_create_dir()
.times(1)
.with(eq(DEFAULT_CONFIG_BASEPATH.to_owned()
+ "/"
+ DEFAULT_DATA_DIRNAME))
.returning(|_| Ok(()));

// 4. WAL 디렉터리 생성
mock.expect_create_dir()
.times(1)
.with(eq(DEFAULT_CONFIG_BASEPATH.to_owned()
+ "/"
+ DEFAULT_WAL_DIRNAME))
.returning(|_| Err(Error::from_raw_os_error(1)));

Arc::new(mock)
}),
mock_command_runner: Box::new(|| {
let mock = MockCommandRunner::new();

Arc::new(mock)
}),
},
TestCase {
name: "데몬 설정파일 생성 실패",
want_error: true,
Expand Down Expand Up @@ -348,7 +437,15 @@ data_directory = "/var/lib/rrdb/data"
.with(eq(DEFAULT_CONFIG_BASEPATH.to_owned() + "/data"))
.returning(|_| Ok(()));

// 4. 데몬 설정파일 생성
// 4. WAL 디렉터리 생성
mock.expect_create_dir()
.times(1)
.with(eq(DEFAULT_CONFIG_BASEPATH.to_owned()
+ "/"
+ DEFAULT_WAL_DIRNAME))
.returning(|_| Ok(()));

// 5. 데몬 설정파일 생성
mock.expect_write_file()
.times(1)
.with(
Expand Down Expand Up @@ -397,7 +494,9 @@ data_directory = "/var/lib/rrdb/data"
// 3. 데이터 디렉터리 생성
mock.expect_create_dir()
.times(1)
.with(eq(DEFAULT_CONFIG_BASEPATH.to_owned() + "/data"))
.with(eq(DEFAULT_CONFIG_BASEPATH.to_owned()
+ "/"
+ DEFAULT_DATA_DIRNAME))
.returning(|_| Err(Error::from_raw_os_error(1)));

Arc::new(mock)
Expand Down
Loading
Loading