core: expose UnixIO apart from PlatformIO on any Unix, and expose UringIO on Linux with feature io_uring.

cli: add a new argument to select I/O backend (more than one option only for Linux with io_uring feature).

cli: make both Limbo::new() and Limbo::open_db() use get_io(), unifying parsing of database path and eliminating duplicated code.
This commit is contained in:
Jorge López
2025-01-11 15:02:48 +01:00
parent a17464ca5c
commit 2596e0800e
3 changed files with 98 additions and 23 deletions

View File

@@ -38,6 +38,52 @@ pub struct Opts {
pub quiet: bool,
#[clap(short, long, help = "Print commands before execution")]
pub echo: bool,
#[clap(
default_value_t,
value_enum,
short,
long,
help = "Select I/O backend. The only other choice to 'syscall' is\n\
\t'io-uring' when built for Linux with feature 'io_uring'\n"
)]
pub io: Io,
}
#[derive(Clone)]
pub enum DbLocation {
Memory,
Path,
}
#[derive(Clone, ValueEnum)]
pub enum Io {
Syscall,
#[cfg(all(target_os = "linux", feature = "io_uring"))]
IoUring,
}
impl Default for Io {
/// Custom Default impl with cfg! macro, to provide compile-time default to Clap based on platform
/// The cfg! could be elided, but Clippy complains
/// The default value can still be overridden with the Clap argument
fn default() -> Self {
match cfg!(all(target_os = "linux", feature = "io_uring")) {
true => {
#[cfg(all(target_os = "linux", feature = "io_uring"))]
{
Io::IoUring
}
#[cfg(any(
not(target_os = "linux"),
all(target_os = "linux", not(feature = "io_uring"))
))]
{
Io::Syscall
}
}
false => Io::Syscall,
}
}
}
#[derive(ValueEnum, Copy, Clone, Debug, PartialEq, Eq)]
@@ -160,6 +206,7 @@ pub struct Settings {
output_mode: OutputMode,
echo: bool,
is_stdout: bool,
io: Io,
}
impl From<&Opts> for Settings {
@@ -174,6 +221,7 @@ impl From<&Opts> for Settings {
.database
.as_ref()
.map_or(":memory:".to_string(), |p| p.to_string_lossy().to_string()),
io: opts.io.clone(),
}
}
}
@@ -207,7 +255,13 @@ impl Limbo {
.as_ref()
.map_or(":memory:".to_string(), |p| p.to_string_lossy().to_string());
let io = get_io(&db_file)?;
let io_choice = opts.io.clone();
let io = {
match db_file.as_str() {
":memory:" => get_io(DbLocation::Memory, None)?,
_path => get_io(DbLocation::Path, Some(io_choice))?,
}
};
let db = Database::open_file(io.clone(), &db_file)?;
let conn = db.connect();
let interrupt_count = Arc::new(AtomicUsize::new(0));
@@ -293,24 +347,17 @@ impl Limbo {
fn open_db(&mut self, path: &str) -> anyhow::Result<()> {
self.conn.close()?;
match path {
":memory:" => {
let io: Arc<dyn limbo_core::IO> = Arc::new(limbo_core::MemoryIO::new()?);
self.io = Arc::clone(&io);
let db = Database::open_file(self.io.clone(), path)?;
self.conn = db.connect();
self.opts.db_file = ":memory:".to_string();
Ok(())
let io = {
match path {
":memory:" => get_io(DbLocation::Memory, None)?,
_path => get_io(DbLocation::Path, Some(self.opts.io.clone()))?,
}
path => {
let io: Arc<dyn limbo_core::IO> = Arc::new(limbo_core::PlatformIO::new()?);
self.io = Arc::clone(&io);
let db = Database::open_file(self.io.clone(), path)?;
self.conn = db.connect();
self.opts.db_file = path.to_string();
Ok(())
}
}
};
self.io = Arc::clone(&io);
let db = Database::open_file(self.io.clone(), path)?;
self.conn = db.connect();
self.opts.db_file = path.to_string();
Ok(())
}
fn set_output_file(&mut self, path: &str) -> Result<(), String> {
@@ -740,10 +787,31 @@ fn get_writer(output: &str) -> Box<dyn Write> {
}
}
fn get_io(db: &str) -> anyhow::Result<Arc<dyn limbo_core::IO>> {
Ok(match db {
":memory:" => Arc::new(limbo_core::MemoryIO::new()?),
_ => Arc::new(limbo_core::PlatformIO::new()?),
fn get_io(
db_location: DbLocation,
io_choice: Option<Io>,
) -> anyhow::Result<Arc<dyn limbo_core::IO>> {
Ok(match db_location {
DbLocation::Memory => Arc::new(limbo_core::MemoryIO::new()?),
DbLocation::Path => {
match io_choice.unwrap() {
Io::Syscall => {
// We are building for Linux/macOS and syscall backend has been selected
#[cfg(target_family = "unix")]
{
Arc::new(limbo_core::UnixIO::new()?)
}
// We are not building for Linux/macOS and syscall backend has been selected
#[cfg(not(target_family = "unix"))]
{
Arc::new(limbo_core::PlatformIO::new()?)
}
}
// We are building for Linux and io_uring backend has been selected
#[cfg(all(target_os = "linux", feature = "io_uring"))]
Io::IoUring => Arc::new(limbo_core::UringIO::new()?),
}
}
})
}

View File

@@ -166,11 +166,15 @@ impl Buffer {
cfg_block! {
#[cfg(all(target_os = "linux", feature = "io_uring"))] {
mod io_uring;
pub use io_uring::UringIO;
mod unix;
pub use unix::UnixIO;
pub use io_uring::UringIO as PlatformIO;
}
#[cfg(any(all(target_os = "linux",not(feature = "io_uring")), target_os = "macos"))] {
mod unix;
pub use unix::UnixIO;
pub use unix::UnixIO as PlatformIO;
}

View File

@@ -44,8 +44,11 @@ pub type Result<T> = std::result::Result<T, error::LimboError>;
use crate::translate::optimizer::optimize_plan;
pub use io::OpenFlags;
#[cfg(feature = "fs")]
pub use io::PlatformIO;
#[cfg(all(feature = "fs", target_family = "unix"))]
pub use io::UnixIO;
#[cfg(all(feature = "fs", target_os = "linux", feature = "io_uring"))]
pub use io::UringIO;
pub use io::{Buffer, Completion, File, MemoryIO, WriteCompletion, IO};
pub use storage::buffer_pool::BufferPool;
pub use storage::database::DatabaseStorage;