Skip to main content

nydus_storage/cache/state/
mod.rs

1// Copyright 2021 Ant Group. All rights reserved.
2// Copyright (C) 2021 Alibaba Cloud. All rights reserved.
3//
4// SPDX-License-Identifier: Apache-2.0
5
6//! Chunk or data readiness state tracking drivers.
7//!
8//! To cache data from remote backend storage onto local storage, a cache state tracking mechanism
9//! is needed to track whether a specific chunk or data is ready on local storage and to cooperate
10//! on concurrent data downloading. The [ChunkMap](trait.ChunkMap.html) trait is the main mechanism
11//! to track chunk state. And [BlobStateMap](struct.BlobStateMap.html) is an adapter structure of
12//! [ChunkMap] to support concurrent data downloading, which is based on a base [ChunkMap]
13//! implementation to track chunk readiness state. And [RangeMap](trait.RangeMap.html) objects are
14//! used to track readiness for a range of chunks or data, with support of batch operation.
15//!
16//! There are several implementation of the [ChunkMap] and [RangeMap] trait to track chunk and data
17//! readiness state:
18//! - [BlobStateMap](struct.BlobStateMap.html): an adapter structure to enable concurrent
19//!   synchronization manipulation of readiness state, based on an underlying base [ChunkMap] or
20//!   [RangeMap] object.
21//! - [BlobRangeMap](struct.BlobRangeMap.html): a data state tracking driver using a bitmap file
22//!   to persist state, indexed by data address range.
23//! - [DigestedChunkMap](struct.DigestedChunkMap.html): a chunk state tracking driver
24//!   for legacy Rafs images without chunk array, which uses chunk digest as the id to track chunk
25//!   readiness state. The [DigestedChunkMap] is not optimal in case of performance and memory
26//!   consumption.
27//! - [IndexedChunkMap](struct.IndexedChunkMap.html): a chunk state tracking driver using a bitmap
28//!   file to persist state, indexed by chunk index. There's a state bit in the bitmap file for each
29//!   chunk, and atomic operations are used to manipulate the bitmap for concurrent state
30//!   manipulating. It's the recommended state tracking driver.
31//! - [NoopChunkMap](struct.NoopChunkMap.html): a no-operation chunk state tracking driver,
32//!   which just reports every chunk as always ready to use or not. It may be used to support disk
33//!   based backend storage or dummy cache.
34
35use std::any::Any;
36use std::io::Result;
37
38use crate::device::BlobChunkInfo;
39use crate::StorageResult;
40
41pub use blob_state_map::BlobStateMap;
42pub use digested_chunk_map::DigestedChunkMap;
43pub use indexed_chunk_map::IndexedChunkMap;
44pub use noop_chunk_map::NoopChunkMap;
45pub use range_map::BlobRangeMap;
46
47mod blob_state_map;
48mod digested_chunk_map;
49mod indexed_chunk_map;
50mod noop_chunk_map;
51mod persist_map;
52mod range_map;
53
54/// Trait to track chunk readiness state.
55pub trait ChunkMap: Any + Send + Sync {
56    /// Check whether the chunk is ready for use.
57    fn is_ready(&self, chunk: &dyn BlobChunkInfo) -> Result<bool>;
58
59    /// Check whether the chunk is pending for downloading.
60    fn is_pending(&self, _chunk: &dyn BlobChunkInfo) -> Result<bool> {
61        Ok(false)
62    }
63
64    /// Check whether a chunk is ready for use or pending for downloading.
65    fn is_ready_or_pending(&self, chunk: &dyn BlobChunkInfo) -> Result<bool> {
66        if matches!(self.is_pending(chunk), Ok(true)) {
67            Ok(true)
68        } else {
69            self.is_ready(chunk)
70        }
71    }
72
73    /// Check whether the chunk is ready for use, and mark it as pending if not ready yet.
74    ///
75    /// The function returns:
76    /// - `Err(Timeout)` waiting for inflight backend IO timeouts.
77    /// - `Ok(true)` if the chunk is ready.
78    /// - `Ok(false)` marks the chunk as pending, either set_ready_and_clear_pending() or
79    ///   clear_pending() must be called to clear the pending state.
80    fn check_ready_and_mark_pending(&self, _chunk: &dyn BlobChunkInfo) -> StorageResult<bool> {
81        panic!("no support of check_ready_and_mark_pending()");
82    }
83
84    /// Set the chunk to ready for use and clear the pending state.
85    fn set_ready_and_clear_pending(&self, _chunk: &dyn BlobChunkInfo) -> Result<()> {
86        panic!("no support of check_ready_and_mark_pending()");
87    }
88
89    /// Clear the pending state of the chunk.
90    fn clear_pending(&self, _chunk: &dyn BlobChunkInfo) {
91        panic!("no support of clear_pending()");
92    }
93
94    /// Check whether the implementation supports state persistence.
95    fn is_persist(&self) -> bool {
96        false
97    }
98
99    /// Convert the objet to an [RangeMap](trait.RangeMap.html) object.
100    fn as_range_map(&self) -> Option<&dyn RangeMap<I = u32>> {
101        None
102    }
103}
104
105/// Trait to track chunk or data readiness state.
106///
107/// A `RangeMap` object tracks readiness state of a chunk or data range, indexed by chunk index or
108/// data address. The trait methods are designed to support batch operations for improving
109/// performance by avoid frequently acquire/release locks.
110pub trait RangeMap: Send + Sync {
111    type I: Send + Sync;
112
113    /// Check whether all chunks or data managed by the `RangeMap` object are ready.
114    fn is_range_all_ready(&self) -> bool {
115        false
116    }
117
118    /// Check whether all chunks or data in the range are ready for use.
119    fn is_range_ready(&self, _start: Self::I, _count: Self::I) -> Result<bool> {
120        Err(enosys!())
121    }
122
123    /// Check whether all chunks or data in the range [start, start + count) are ready.
124    ///
125    /// This function checks readiness of a range of chunks or data. If a chunk or data is both not
126    /// ready and not pending(inflight), it will be marked as pending and returned. Following
127    /// actions should be:
128    /// - call set_range_ready_and_clear_pending() to mark data or chunks as ready and clear pending
129    ///   state.
130    /// - clear_range_pending() to clear the pending state without marking data or chunks as ready.
131    /// - wait_for_range_ready() to wait for all data or chunks to clear pending state, including
132    ///   data or chunks marked as pending by other threads.
133    fn check_range_ready_and_mark_pending(
134        &self,
135        _start: Self::I,
136        _count: Self::I,
137    ) -> Result<Option<Vec<Self::I>>> {
138        Err(enosys!())
139    }
140
141    /// Mark all chunks or data in the range as ready for use.
142    fn set_range_ready_and_clear_pending(&self, _start: Self::I, _count: Self::I) -> Result<()> {
143        Err(enosys!())
144    }
145
146    /// Clear the pending state for all chunks or data in the range.
147    fn clear_range_pending(&self, _start: Self::I, _count: Self::I) {}
148
149    /// Wait for all chunks or data in the range to be ready until timeout.
150    fn wait_for_range_ready(&self, _start: Self::I, _count: Self::I) -> Result<bool> {
151        Err(enosys!())
152    }
153}
154
155/// Trait to convert a [BlobChunkInfo](../../device/trait.BlobChunkInfo.html) object to an index
156/// needed by [ChunkMap](trait.ChunkMap.html).
157pub trait ChunkIndexGetter {
158    /// Type of index needed by [ChunkMap].
159    type Index;
160
161    /// Get the chunk's id/key for state tracking.
162    fn get_index(chunk: &dyn BlobChunkInfo) -> Self::Index;
163}
164
165#[cfg(test)]
166mod tests {
167    use crate::test::MockChunkInfo;
168
169    use super::*;
170
171    impl RangeMap for NoopChunkMap {
172        type I = u32;
173    }
174
175    #[test]
176    fn test_trait_default_impl() {
177        let m = NoopChunkMap::new(false);
178        let chunk_info = MockChunkInfo {
179            index: 128,
180            ..Default::default()
181        };
182
183        assert!(m.is_pending(&chunk_info).is_ok());
184        assert!(!m.is_pending(&chunk_info).unwrap());
185        assert!(!m.is_range_all_ready());
186        assert!(m.is_range_ready(0, 1).is_err());
187        assert!(m.check_range_ready_and_mark_pending(0, 1).is_err());
188        assert!(m.set_range_ready_and_clear_pending(0, 1).is_err());
189        m.clear_range_pending(0, 1);
190        assert!(m.wait_for_range_ready(0, 1).is_err());
191        assert!(m.as_range_map().is_none());
192        assert!(!m.is_persist());
193        assert!(!m.is_ready(&chunk_info).unwrap());
194    }
195
196    #[test]
197    #[should_panic]
198    fn test_check_ready_and_mark_pending_default_impl() {
199        let chunk_info = MockChunkInfo::default();
200        let m = NoopChunkMap::new(false);
201        m.check_ready_and_mark_pending(&chunk_info).unwrap();
202    }
203
204    #[test]
205    #[should_panic]
206    fn test_set_ready_and_clear_pending_default_impl() {
207        let chunk_info = MockChunkInfo::default();
208        let m = NoopChunkMap::new(false);
209        m.set_ready_and_clear_pending(&chunk_info).unwrap();
210    }
211}