// This software is licensed under a dual license model: // // GNU Affero General Public License v3 (AGPLv3): You may use, modify, and // distribute this software under the terms of the AGPLv3. // // Elastic License v2 (ELv2): You may also use, modify, and distribute this // software under the Elastic License v2, which has specific restrictions. // // We welcome any commercial collaboration or support. For inquiries // regarding the licenses, please contact us at: // vectorchord-inquiry@tensorchord.ai // // Copyright (c) 2025 TensorChord Inc. use crate::closure_lifetime_binder::{id_0, id_1, id_2}; use crate::operator::Operator; use crate::tape::{by_directory, by_next}; use crate::tuples::*; use crate::{Opaque, centroids, tape}; use index::accessor::FunctionalAccessor; use index::prefetcher::PrefetcherSequenceFamily; use index::relation::{Page, RelationRead}; use std::fmt::Write; pub fn prewarm<'b, R: RelationRead, O: Operator>( index: &'b R, height: i32, mut prefetch_h0_tuples: impl PrefetcherSequenceFamily<'b, R>, ) -> String where R::Page: Page, { let meta_guard = index.read(0); let meta_bytes = meta_guard.get(1).expect("data corruption"); let meta_tuple = MetaTuple::deserialize_ref(meta_bytes); let height_of_root = meta_tuple.height_of_root(); let mut message = String::new(); writeln!(message, "height of root: {height_of_root}").unwrap(); let prewarm_max_height = if height < 0 { 0 } else { height as u32 }; if prewarm_max_height > height_of_root { return message; } type State = Vec; let mut state: State = { let mut results = Vec::new(); let prefetch = meta_tuple.centroid_prefetch().to_vec().into_iter(); let head = meta_tuple.centroid_head(); let first = meta_tuple.first(); centroids::read::(prefetch.map(|id| index.read(id)), head, ()); results.push(first); writeln!(message, "------------------------").unwrap(); writeln!(message, "number of nodes: {}", results.len()).unwrap(); writeln!(message, "number of pages: {}", 1).unwrap(); results }; drop(meta_guard); let mut step = |state: State| { let mut counter = 0_usize; let mut results = Vec::new(); for first in state { tape::read_h1_tape::( by_next(index, first).inspect(|_| counter += 1), || FunctionalAccessor::new((), id_0(|_, _| ()), id_1(|_, _| [(); _])), |(), head, _, first, prefetch| { centroids::read::(prefetch.iter().map(|&id| index.read(id)), head, ()); results.push(first); }, ); } writeln!(message, "------------------------").unwrap(); writeln!(message, "number of nodes: {}", results.len()).unwrap(); writeln!(message, "number of pages: {counter}").unwrap(); results }; for _ in (std::cmp::max(1, prewarm_max_height)..height_of_root).rev() { state = step(state); } if prewarm_max_height == 0 { let mut counter = 0_usize; let mut results = Vec::new(); for first in state { let jump_guard = index.read(first); let jump_bytes = jump_guard.get(1).expect("data corruption"); let jump_tuple = JumpTuple::deserialize_ref(jump_bytes); if prefetch_h0_tuples.is_not_plain() { let directory = tape::read_directory_tape::(by_next(index, jump_tuple.directory_first())); tape::read_frozen_tape::( by_directory(&mut prefetch_h0_tuples, directory).inspect(|_| counter += 1), || FunctionalAccessor::new((), id_0(|_, _| ()), id_1(|_, _| [(); _])), id_2(|_, _, _, _| { results.push(()); }), ); } else { tape::read_frozen_tape::( by_next(index, jump_tuple.frozen_first()).inspect(|_| counter += 1), || FunctionalAccessor::new((), id_0(|_, _| ()), id_1(|_, _| [(); _])), id_2(|_, _, _, _| { results.push(()); }), ); } tape::read_appendable_tape::( by_next(index, jump_tuple.appendable_first()).inspect(|_| counter += 1), |_, _, _| (), id_2(|_, _, _, _| { results.push(()); }), ); } writeln!(message, "------------------------").unwrap(); writeln!(message, "number of nodes: {}", results.len()).unwrap(); writeln!(message, "number of pages: {counter}").unwrap(); } message }