Skip to main content

vortex_layout/scan/v2/
cache.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4//! State derived from a [`ScanFile`](super::ScanFile) that repeated V2 scans can share.
5
6use std::sync::Arc;
7
8use parking_lot::Mutex;
9use rustc_hash::FxHashMap;
10use vortex_array::expr::BoundExpression;
11use vortex_array::expr::ExactBoundExpr;
12
13use super::io::SegmentRanges;
14use crate::plan::physical::PlanRef;
15use crate::plan::physical::pipeline::ValueCache;
16
17/// Reusable state for repeated V2 scans of one file, the V2 counterpart of a cached
18/// [`LayoutReader`](crate::LayoutReader) tree.
19///
20/// Every scan otherwise lowers the layout into a fresh physical plan, optimizes its projection
21/// over it, indexes every segment's byte range, and decodes the dictionary values it needs. A scan
22/// that finds an entry here reuses it, and one that misses builds the entry and keeps it for later
23/// scans. The lowered plan keeps the children it has initialized, each distinct projection keeps
24/// its optimized plan, and dictionary values stay keyed by the cached plan that produced them.
25///
26/// A cache describes one file's layout, segment locations, and segment source. Use a new cache
27/// when any of them changes. Cached plans and values live as long as the cache, one set per
28/// distinct projection.
29#[derive(Clone, Default)]
30pub struct ScanCache(Arc<Inner>);
31
32#[derive(Default)]
33struct Inner {
34    root: Mutex<Option<PlanRef>>,
35    /// Optimized plans of non-identity projections. Values are cached by the plan that produced
36    /// them, so a projection must reuse its plan for its values to be found again.
37    projections: Mutex<FxHashMap<ExactBoundExpr, PlanRef>>,
38    ranges: Mutex<Option<SegmentRanges>>,
39    values: ValueCache,
40}
41
42impl ScanCache {
43    /// The lowered root plan an earlier scan kept.
44    pub(super) fn root(&self) -> Option<PlanRef> {
45        self.0.root.lock().clone()
46    }
47
48    /// Keeps `root`, returning the plan already kept if another scan got there first.
49    pub(super) fn insert_root(&self, root: PlanRef) -> PlanRef {
50        self.0.root.lock().get_or_insert(root).clone()
51    }
52
53    /// The optimized plan of `projection` an earlier scan kept.
54    pub(super) fn projection(&self, projection: &BoundExpression) -> Option<PlanRef> {
55        self.0
56            .projections
57            .lock()
58            .get(&ExactBoundExpr(projection.clone()))
59            .cloned()
60    }
61
62    /// Keeps the optimized plan of `projection`, returning the plan already kept if another scan
63    /// got there first.
64    pub(super) fn insert_projection(&self, projection: BoundExpression, plan: PlanRef) -> PlanRef {
65        self.0
66            .projections
67            .lock()
68            .entry(ExactBoundExpr(projection))
69            .or_insert(plan)
70            .clone()
71    }
72
73    /// The segment byte-range index an earlier scan kept.
74    pub(super) fn ranges(&self) -> Option<SegmentRanges> {
75        self.0.ranges.lock().clone()
76    }
77
78    /// Keeps `ranges`, returning the index already kept if another scan got there first.
79    pub(super) fn insert_ranges(&self, ranges: SegmentRanges) -> SegmentRanges {
80        Arc::clone(self.0.ranges.lock().get_or_insert(ranges))
81    }
82
83    pub(super) fn values(&self) -> ValueCache {
84        self.0.values.clone()
85    }
86}