use std::os::unix::io::AsRawFd;
use std::path::Path;
pub fn advise_willneed(path: &Path) -> std::io::Result<u64> {
let file = std::fs::File::open(path)?;
let len = file.metadata()?.len();
if len == 0 {
return Ok(0);
}
let ret =
unsafe { libc::posix_fadvise(file.as_raw_fd(), 0, len as i64, libc::POSIX_FADV_WILLNEED) };
if ret != 0 {
return Err(std::io::Error::from_raw_os_error(ret));
}
Ok(len)
}
pub fn advise_dontneed(path: &Path) -> std::io::Result<()> {
let file = std::fs::File::open(path)?;
let len = file.metadata()?.len();
if len == 0 {
return Ok(());
}
let ret =
unsafe { libc::posix_fadvise(file.as_raw_fd(), 0, len as i64, libc::POSIX_FADV_DONTNEED) };
if ret != 0 {
return Err(std::io::Error::from_raw_os_error(ret));
}
Ok(())
}
pub fn prefetch_partition_columns(partition_dir: &Path, needed_columns: &[String]) {
for col_name in needed_columns {
let col_path = partition_dir.join(format!("{col_name}.col"));
let _ = advise_willneed(&col_path);
}
let _ = advise_willneed(&partition_dir.join("schema.json"));
let _ = advise_willneed(&partition_dir.join("partition.meta"));
}
pub fn release_partition_columns(partition_dir: &Path, needed_columns: &[String]) {
for col_name in needed_columns {
let col_path = partition_dir.join(format!("{col_name}.col"));
let _ = advise_dontneed(&col_path);
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Write;
#[test]
fn advise_willneed_existing_file() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("test.col");
{
let mut f = std::fs::File::create(&path).unwrap();
f.write_all(&[0u8; 8192]).unwrap();
}
let len = advise_willneed(&path).unwrap();
assert_eq!(len, 8192);
}
#[test]
fn advise_willneed_missing_file() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("missing.col");
assert!(advise_willneed(&path).is_err());
}
#[test]
fn advise_dontneed_existing_file() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("test.col");
{
let mut f = std::fs::File::create(&path).unwrap();
f.write_all(&[0u8; 4096]).unwrap();
}
advise_dontneed(&path).unwrap();
}
#[test]
fn prefetch_partition_columns_best_effort() {
let dir = tempfile::tempdir().unwrap();
for name in &["timestamp", "value", "qtype"] {
let path = dir.path().join(format!("{name}.col"));
let mut f = std::fs::File::create(&path).unwrap();
f.write_all(&[0u8; 4096]).unwrap();
}
std::fs::write(dir.path().join("schema.json"), b"{}").unwrap();
std::fs::write(dir.path().join("partition.meta"), b"{}").unwrap();
let cols = vec![
"timestamp".to_string(),
"value".to_string(),
"qtype".to_string(),
"missing_col".to_string(), ];
prefetch_partition_columns(dir.path(), &cols);
release_partition_columns(dir.path(), &cols);
}
#[test]
fn empty_file_no_error() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("empty.col");
std::fs::File::create(&path).unwrap();
let len = advise_willneed(&path).unwrap();
assert_eq!(len, 0);
advise_dontneed(&path).unwrap();
}
}