drive/cache/
data_contract.rs1use crate::drive::contract::DataContractFetchInfo;
2use dpp::data_contract::accessors::v0::DataContractV0Getters;
3use moka::sync::Cache;
4use std::sync::Arc;
5
6pub struct DataContractCache {
8 global_cache: Cache<[u8; 32], Arc<DataContractFetchInfo>>,
9 block_cache: Cache<[u8; 32], Arc<DataContractFetchInfo>>,
10}
11
12impl DataContractCache {
13 pub fn new(global_cache_max_capacity: u64, block_cache_max_capacity: u64) -> Self {
15 Self {
16 global_cache: Cache::new(global_cache_max_capacity),
17 block_cache: Cache::new(block_cache_max_capacity),
18 }
19 }
20
21 pub fn insert(&self, fetch_info: Arc<DataContractFetchInfo>, is_block_cache: bool) {
24 let data_contract_id_bytes = fetch_info.contract.id().to_buffer();
25
26 if is_block_cache {
27 self.block_cache.insert(data_contract_id_bytes, fetch_info);
28 } else {
29 self.global_cache.insert(data_contract_id_bytes, fetch_info);
30 }
31 }
32
33 pub fn get(
37 &self,
38 contract_id: [u8; 32],
39 is_block_cache: bool,
40 ) -> Option<Arc<DataContractFetchInfo>> {
41 let maybe_fetch_info = if is_block_cache {
42 self.block_cache.get(&contract_id)
43 } else {
44 None
45 };
46
47 maybe_fetch_info.or_else(|| self.global_cache.get(&contract_id))
48 }
49
50 pub fn remove(&self, contract_id: [u8; 32]) {
52 self.block_cache.remove(&contract_id);
53 self.global_cache.remove(&contract_id);
54 }
55
56 pub fn merge_and_clear_block_cache(&self) {
58 for (contract_id, fetch_info) in self.block_cache.into_iter() {
59 self.global_cache
60 .insert(Arc::unwrap_or_clone(contract_id), fetch_info);
61 }
62 self.clear_block_cache();
63 }
64
65 pub fn clear_block_cache(&self) {
67 self.block_cache.invalidate_all();
68 }
69
70 pub fn clear(&self) {
72 self.block_cache.invalidate_all();
73 self.global_cache.invalidate_all();
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80 use dpp::version::PlatformVersion;
81
82 mod get {
83 use super::*;
84 use dpp::data_contract::accessors::v0::{DataContractV0Getters, DataContractV0Setters};
85
86 #[test]
87 fn test_get_from_global_cache_when_block_cache_is_not_requested() {
88 let data_contract_cache = DataContractCache::new(10, 10);
89
90 let protocol_version = PlatformVersion::latest().protocol_version;
91
92 let fetch_info_global = Arc::new(DataContractFetchInfo::dpns_contract_fixture(
94 protocol_version,
95 ));
96
97 let contract_id = fetch_info_global.contract.id().to_buffer();
98
99 data_contract_cache
100 .global_cache
101 .insert(contract_id, Arc::clone(&fetch_info_global));
102
103 let mut fetch_info_block =
105 DataContractFetchInfo::dpns_contract_fixture(protocol_version);
106
107 fetch_info_block.contract.increment_version();
108
109 let fetch_info_block_boxed = Arc::new(fetch_info_block);
110
111 data_contract_cache
112 .block_cache
113 .insert(contract_id, Arc::clone(&fetch_info_block_boxed));
114
115 let fetch_info_from_cache = data_contract_cache
116 .get(contract_id, false)
117 .expect("should be present");
118
119 assert_eq!(fetch_info_from_cache, fetch_info_global)
120 }
121
122 #[test]
123 fn test_get_from_global_cache_when_block_cache_does_not_have_contract() {
124 let data_contract_cache = DataContractCache::new(10, 10);
125
126 let protocol_version = PlatformVersion::latest().protocol_version;
127
128 let fetch_info_global = Arc::new(DataContractFetchInfo::dpns_contract_fixture(
129 protocol_version,
130 ));
131
132 let contract_id = fetch_info_global.contract.id().to_buffer();
133
134 data_contract_cache
135 .global_cache
136 .insert(contract_id, Arc::clone(&fetch_info_global));
137
138 let fetch_info_from_cache = data_contract_cache
139 .get(contract_id, true)
140 .expect("should be present");
141
142 assert_eq!(fetch_info_from_cache, fetch_info_global)
143 }
144
145 #[test]
146 fn test_get_from_block_cache() {
147 let data_contract_cache = DataContractCache::new(10, 10);
148
149 let protocol_version = PlatformVersion::latest().protocol_version;
150
151 let fetch_info_block = Arc::new(DataContractFetchInfo::dpns_contract_fixture(
152 protocol_version,
153 ));
154
155 let contract_id = fetch_info_block.contract.id().to_buffer();
156
157 data_contract_cache
158 .block_cache
159 .insert(contract_id, Arc::clone(&fetch_info_block));
160
161 let fetch_info_from_cache = data_contract_cache
162 .get(contract_id, true)
163 .expect("should be present");
164
165 assert_eq!(fetch_info_from_cache, fetch_info_block)
166 }
167 }
168
169 mod remove {
170 use super::*;
171
172 #[test]
173 fn test_remove_clears_global_cache_entry() {
174 let data_contract_cache = DataContractCache::new(10, 10);
175
176 let protocol_version = PlatformVersion::latest().protocol_version;
177 let fetch_info = Arc::new(DataContractFetchInfo::dpns_contract_fixture(
178 protocol_version,
179 ));
180 let contract_id = fetch_info.contract.id().to_buffer();
181
182 data_contract_cache.insert(fetch_info, false);
183 data_contract_cache.remove(contract_id);
184
185 assert!(data_contract_cache.get(contract_id, false).is_none());
186 }
187
188 #[test]
189 fn test_remove_clears_entry_from_both_caches() {
190 let data_contract_cache = DataContractCache::new(10, 10);
191
192 let protocol_version = PlatformVersion::latest().protocol_version;
193 let fetch_info_global = Arc::new(DataContractFetchInfo::dpns_contract_fixture(
194 protocol_version,
195 ));
196 let contract_id = fetch_info_global.contract.id().to_buffer();
197 let fetch_info_block = Arc::clone(&fetch_info_global);
198
199 data_contract_cache.insert(fetch_info_global, false);
200 data_contract_cache.insert(fetch_info_block, true);
201 data_contract_cache.remove(contract_id);
202
203 assert!(data_contract_cache.block_cache.get(&contract_id).is_none());
204 assert!(data_contract_cache.global_cache.get(&contract_id).is_none());
205 }
206 }
207
208 mod merge_and_clear_block_cache {
209 use super::*;
210
211 #[test]
212 fn test_merge_moves_block_items_to_global_cache() {
213 let data_contract_cache = DataContractCache::new(10, 10);
214
215 let protocol_version = PlatformVersion::latest().protocol_version;
216 let fetch_info = Arc::new(DataContractFetchInfo::dpns_contract_fixture(
217 protocol_version,
218 ));
219 let contract_id = fetch_info.contract.id().to_buffer();
220
221 data_contract_cache.insert(fetch_info, true);
222 data_contract_cache.merge_and_clear_block_cache();
223
224 assert!(data_contract_cache.global_cache.get(&contract_id).is_some());
225 }
226
227 #[test]
228 fn test_merge_clears_block_cache() {
229 let data_contract_cache = DataContractCache::new(10, 10);
230
231 let protocol_version = PlatformVersion::latest().protocol_version;
232 let fetch_info = Arc::new(DataContractFetchInfo::dpns_contract_fixture(
233 protocol_version,
234 ));
235 let contract_id = fetch_info.contract.id().to_buffer();
236
237 data_contract_cache.insert(fetch_info, true);
238 data_contract_cache.merge_and_clear_block_cache();
239
240 assert!(data_contract_cache.block_cache.get(&contract_id).is_none());
241 }
242 }
243
244 mod clear {
245 use super::*;
246
247 #[test]
248 fn test_clear_empties_global_and_block_caches() {
249 let data_contract_cache = DataContractCache::new(10, 10);
250
251 let protocol_version = PlatformVersion::latest().protocol_version;
252 let fetch_info_global = Arc::new(DataContractFetchInfo::dpns_contract_fixture(
253 protocol_version,
254 ));
255 let contract_id = fetch_info_global.contract.id().to_buffer();
256 let fetch_info_block = Arc::clone(&fetch_info_global);
257
258 data_contract_cache.insert(fetch_info_global, false);
259 data_contract_cache.insert(fetch_info_block, true);
260 data_contract_cache.clear();
261
262 assert!(data_contract_cache.get(contract_id, false).is_none());
263 assert!(data_contract_cache.block_cache.get(&contract_id).is_none());
264 }
265 }
266}