sui_storage/
package_object_cache.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use lru::LruCache;
5use parking_lot::RwLock;
6use std::num::NonZeroUsize;
7use std::sync::Arc;
8use sui_types::base_types::ObjectID;
9use sui_types::error::{SuiErrorKind, SuiResult, UserInputError};
10use sui_types::storage::{ObjectStore, PackageObject};
11
12pub struct PackageObjectCache {
13    cache: RwLock<LruCache<ObjectID, PackageObject>>,
14}
15
16const CACHE_CAP: usize = 1024 * 1024;
17
18impl PackageObjectCache {
19    pub fn new() -> Arc<Self> {
20        Arc::new(Self {
21            cache: RwLock::new(LruCache::new(NonZeroUsize::new(CACHE_CAP).unwrap())),
22        })
23    }
24
25    pub fn get_package_object(
26        &self,
27        package_id: &ObjectID,
28        store: &impl ObjectStore,
29    ) -> SuiResult<Option<PackageObject>> {
30        // TODO: Here the use of `peek` doesn't update the internal use record,
31        // and hence the LRU is really used as a capped map here.
32        // This is OK because we won't typically have too many entries.
33        // We cannot use `get` here because it requires a mut reference and that would
34        // require unnecessary lock contention on the mutex, which defeats the purpose.
35        if let Some(p) = self.cache.read().peek(package_id) {
36            #[cfg(debug_assertions)]
37            {
38                assert_eq!(
39                    store.get_object(package_id).unwrap().digest(),
40                    p.object().digest(),
41                    "Package object cache is inconsistent for package {:?}",
42                    package_id
43                )
44            }
45            return Ok(Some(p.clone()));
46        }
47        if let Some(p) = store.get_object(package_id) {
48            if p.is_package() {
49                let p = PackageObject::new(p);
50                self.cache.write().push(*package_id, p.clone());
51                Ok(Some(p))
52            } else {
53                Err(SuiErrorKind::UserInputError {
54                    error: UserInputError::MoveObjectAsPackage {
55                        object_id: *package_id,
56                    },
57                }
58                .into())
59            }
60        } else {
61            Ok(None)
62        }
63    }
64
65    pub fn force_reload_system_packages(
66        &self,
67        system_package_ids: impl IntoIterator<Item = ObjectID>,
68        store: &impl ObjectStore,
69    ) {
70        for package_id in system_package_ids {
71            if let Some(p) = store.get_object(&package_id) {
72                assert!(p.is_package());
73                self.cache.write().push(package_id, PackageObject::new(p));
74            }
75            // It's possible that a package is not found if it's newly added system package ID
76            // that hasn't got created yet. This should be very rare though.
77        }
78    }
79}