[UIO] Genericize ChunkedVectors.status (#8928)

* genericize ChunkedVectors.status, remove `Sized` bound

* check exists with `UniversalReadFileOps`

* Inline UioChunkedVectors bound, drop the alias (#8952)

The empty trait + blanket impl was a stable-Rust trait-alias workaround
that hid a fairly short bound (UniversalWrite<T> + UniversalWrite<Status>
+ Send + 'static) at the cost of an indirection readers had to mentally
unwind. Spelling it out at the three sites that need it is shorter overall
and immediately tells the reader what is required.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Andrey Vasnetsov <andrey@vasnetsov.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Luis Cossío
2026-05-08 07:51:48 -04:00
committed by GitHub
parent edde26e8b9
commit dcbbdeba8e
4 changed files with 42 additions and 22 deletions

View File

@@ -18,7 +18,7 @@ fn check_mmap_file_name_pattern(file_name: &str) -> Option<usize> {
.and_then(|file_name| file_name.parse::<usize>().ok())
}
pub fn read_chunks<T: Sized + Copy + 'static, S: UniversalRead<T>>(
pub fn read_chunks<T: Copy + 'static, S: UniversalRead<T>>(
directory: &Path,
advice: AdviceSetting,
populate: bool,

View File

@@ -7,6 +7,7 @@ pub(super) const MMAP_CHUNKS_PATTERN_START: &str = "chunk_";
// TODO: rename for other storages?
pub(super) const MMAP_CHUNKS_PATTERN_END: &str = ".mmap";
#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
#[repr(C)]
pub struct Status {
pub len: usize,

View File

@@ -25,7 +25,7 @@ use crate::vector_storage::{VectorOffset, VectorOffsetType};
/// not refreshed afterwards. Mutating storage uses [`super::ChunkedVectors`]
/// which wraps this and adds a writable status mmap.
#[derive(Debug)]
pub struct ChunkedVectorsRead<T: Copy + Sized + 'static, S: UniversalRead<T>> {
pub struct ChunkedVectorsRead<T: Copy + 'static, S: UniversalRead<T>> {
pub(super) config: ChunkedVectorsConfig,
/// Number of vectors currently stored. Snapshot for read-only mode; for
/// [`super::ChunkedVectors`] this is kept in sync with the writable status
@@ -35,7 +35,7 @@ pub struct ChunkedVectorsRead<T: Copy + Sized + 'static, S: UniversalRead<T>> {
pub(super) directory: PathBuf,
}
impl<T: Sized + Copy + 'static, S: UniversalRead<T>> ChunkedVectorsRead<T, S> {
impl<T: Copy + 'static, S: UniversalRead<T>> ChunkedVectorsRead<T, S> {
pub(super) fn config_file(directory: &Path) -> PathBuf {
directory.join(CONFIG_FILE_NAME)
}

View File

@@ -1,13 +1,12 @@
use std::cmp::max;
use std::ops::Deref;
use std::path::Path;
use std::path::{Path, PathBuf};
use common::counter::hardware_counter::HardwareCounterCell;
use common::fs::atomic_save_json;
use common::mmap::{Advice, AdviceSetting, MmapType, open_write_mmap};
use common::universal_io::UniversalWrite;
use common::mmap::AdviceSetting;
use common::universal_io::{OpenOptions, StoredStruct, UniversalWrite};
use fs_err as fs;
use memmap2::MmapMut;
use num_traits::AsPrimitive;
use super::chunks::{create_chunk, read_chunks};
@@ -19,12 +18,20 @@ use crate::vector_storage::VectorOffsetType;
use crate::vector_storage::common::CHUNK_SIZE;
#[derive(Debug)]
pub struct ChunkedVectors<T: Copy + Sized + 'static, S: UniversalWrite<T>> {
pub struct ChunkedVectors<T, S>
where
T: Copy + 'static,
S: UniversalWrite<T> + UniversalWrite<Status> + Send + 'static,
{
inner: ChunkedVectorsRead<T, S>,
status: MmapType<Status>,
status: StoredStruct<S, Status>,
}
impl<T: Copy + Sized + 'static, S: UniversalWrite<T>> Deref for ChunkedVectors<T, S> {
impl<T, S> Deref for ChunkedVectors<T, S>
where
T: Copy + 'static,
S: UniversalWrite<T> + UniversalWrite<Status> + Send + 'static,
{
type Target = ChunkedVectorsRead<T, S>;
fn deref(&self) -> &Self::Target {
@@ -32,20 +39,21 @@ impl<T: Copy + Sized + 'static, S: UniversalWrite<T>> Deref for ChunkedVectors<T
}
}
impl<T: Sized + Copy + 'static, S: UniversalWrite<T>> ChunkedVectors<T, S> {
pub fn ensure_status_file(directory: &Path) -> OperationResult<MmapMut> {
impl<T, S> ChunkedVectors<T, S>
where
T: Copy + 'static,
S: UniversalWrite<T> + UniversalWrite<Status> + Send + 'static,
{
pub fn ensure_status_file(directory: &Path) -> OperationResult<PathBuf> {
let status_file = ChunkedVectorsRead::<T, S>::status_file(directory);
if !status_file.exists() {
if !S::exists(&status_file)? {
{
let length = std::mem::size_of::<usize>() as u64;
common::mmap::create_and_ensure_length(&status_file, length as usize)?;
let length = std::mem::size_of::<Status>();
// TODO(uio): migrate when UniversalWriteFileOps is available
common::mmap::create_and_ensure_length(&status_file, length)?;
}
}
Ok(open_write_mmap(
&status_file,
AdviceSetting::from(Advice::Normal),
false, // Status file is write-only
)?)
Ok(status_file)
}
fn ensure_config(
@@ -107,8 +115,19 @@ impl<T: Sized + Copy + 'static, S: UniversalWrite<T>> ChunkedVectors<T, S> {
populate: Option<bool>,
) -> OperationResult<Self> {
fs::create_dir_all(directory)?;
let status_mmap = Self::ensure_status_file(directory)?;
let status: MmapType<Status> = unsafe { MmapType::from(status_mmap) };
let status_path = Self::ensure_status_file(directory)?;
let status: StoredStruct<S, Status> = StoredStruct::open(
status_path,
OpenOptions {
writeable: true,
need_sequential: false,
disk_parallel: None,
populate,
advice: None,
prevent_caching: None,
},
)?;
let config = Self::ensure_config(directory, dim, populate)?;
let chunks = read_chunks(directory, advice, populate.unwrap_or_default(), true)?;