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}