Skip to main content

vortex_file/multi/
session.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4//! Session extension for multi-file scanning, providing a shared footer cache.
5
6use std::any::Any;
7use std::fmt;
8use std::fmt::Debug;
9
10use vortex_session::SessionExt;
11use vortex_session::SessionGuard;
12use vortex_session::SessionVar;
13
14use crate::footer::Footer;
15
16/// Session state for multi-file scanning.
17///
18/// Provides a shared, in-memory footer cache so that repeated scans over the same files
19/// avoid redundant footer I/O. The cache is bounded by entry count and lives as long as
20/// the [`VortexSession`](vortex_session::VortexSession).
21///
22/// # Future Work
23///
24/// Consider generalizing this cache into [`VortexOpenOptions`](crate::VortexOpenOptions) so
25/// that single-file opens also benefit from session-level footer caching.
26#[derive(Clone)]
27pub struct MultiFileSession {
28    footer_cache: moka::sync::Cache<String, Footer>,
29}
30
31impl Default for MultiFileSession {
32    fn default() -> Self {
33        Self {
34            footer_cache: moka::sync::Cache::builder()
35                // Capacity and weigher are in KB
36                .max_capacity(100 * 1024) // 100MB
37                .weigher(|_k, footer: &Footer| {
38                    footer
39                        .approx_byte_size()
40                        .and_then(|bytes| u32::try_from(bytes / 1024).ok())
41                        .unwrap_or(10)
42                })
43                .build(),
44        }
45    }
46}
47
48impl Debug for MultiFileSession {
49    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50        f.debug_struct("MultiFileSession")
51            .field("footer_cache_entry_count", &self.footer_cache.entry_count())
52            .finish()
53    }
54}
55
56impl MultiFileSession {
57    /// Retrieve a cached footer for the given file path.
58    pub(crate) fn get_footer(&self, path: &str) -> Option<Footer> {
59        self.footer_cache.get(path)
60    }
61
62    /// Store a footer under the given file path.
63    pub(crate) fn put_footer(&self, path: &str, footer: Footer) {
64        self.footer_cache.insert(path.to_string(), footer);
65    }
66}
67
68impl SessionVar for MultiFileSession {
69    fn as_any(&self) -> &dyn Any {
70        self
71    }
72
73    fn as_any_mut(&mut self) -> &mut dyn Any {
74        self
75    }
76}
77
78/// Extension trait for accessing the [`MultiFileSession`] from a session.
79pub(super) trait MultiFileSessionExt: SessionExt {
80    /// Returns a reference to the [`MultiFileSession`] state.
81    fn multi_file(&self) -> SessionGuard<'_, MultiFileSession> {
82        self.get::<MultiFileSession>()
83    }
84}
85
86impl<S: SessionExt> MultiFileSessionExt for S {}