sui_storage/
package_object_cache.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

use lru::LruCache;
use parking_lot::RwLock;
use std::num::NonZeroUsize;
use std::sync::Arc;
use sui_types::base_types::ObjectID;
use sui_types::error::{SuiError, SuiResult, UserInputError};
use sui_types::storage::{ObjectStore, PackageObject};

pub struct PackageObjectCache {
    cache: RwLock<LruCache<ObjectID, PackageObject>>,
}

const CACHE_CAP: usize = 1024 * 1024;

impl PackageObjectCache {
    pub fn new() -> Arc<Self> {
        Arc::new(Self {
            cache: RwLock::new(LruCache::new(NonZeroUsize::new(CACHE_CAP).unwrap())),
        })
    }

    pub fn get_package_object(
        &self,
        package_id: &ObjectID,
        store: &impl ObjectStore,
    ) -> SuiResult<Option<PackageObject>> {
        // TODO: Here the use of `peek` doesn't update the internal use record,
        // and hence the LRU is really used as a capped map here.
        // This is OK because we won't typically have too many entries.
        // We cannot use `get` here because it requires a mut reference and that would
        // require unnecessary lock contention on the mutex, which defeats the purpose.
        if let Some(p) = self.cache.read().peek(package_id) {
            #[cfg(debug_assertions)]
            {
                assert_eq!(
                    store.get_object(package_id).unwrap().digest(),
                    p.object().digest(),
                    "Package object cache is inconsistent for package {:?}",
                    package_id
                )
            }
            return Ok(Some(p.clone()));
        }
        if let Some(p) = store.get_object(package_id) {
            if p.is_package() {
                let p = PackageObject::new(p);
                self.cache.write().push(*package_id, p.clone());
                Ok(Some(p))
            } else {
                Err(SuiError::UserInputError {
                    error: UserInputError::MoveObjectAsPackage {
                        object_id: *package_id,
                    },
                })
            }
        } else {
            Ok(None)
        }
    }

    pub fn force_reload_system_packages(
        &self,
        system_package_ids: impl IntoIterator<Item = ObjectID>,
        store: &impl ObjectStore,
    ) {
        for package_id in system_package_ids {
            if let Some(p) = store.get_object(&package_id) {
                assert!(p.is_package());
                self.cache.write().push(package_id, PackageObject::new(p));
            }
            // It's possible that a package is not found if it's newly added system package ID
            // that hasn't got created yet. This should be very very rare though.
        }
    }
}