canic_core/model/memory/sharding/
mod.rs1mod registry;
2
3pub(crate) use registry::ShardingRegistry;
4
5use crate::{
6 cdk::structures::{BTreeMap, DefaultMemoryImpl, Memory, memory::VirtualMemory},
7 eager_static, ic_memory,
8 ids::CanisterRole,
9 impl_storable_bounded,
10 model::memory::id::sharding::{SHARDING_ASSIGNMENT_ID, SHARDING_REGISTRY_ID},
11 types::{BoundedString32, BoundedString128, Principal},
12};
13use candid::CandidType;
14use serde::{Deserialize, Serialize};
15use std::cell::RefCell;
16
17eager_static! {
22 static SHARDING_CORE: RefCell<ShardingCore<VirtualMemory<DefaultMemoryImpl>>> = RefCell::new(
23 ShardingCore::new(
24 BTreeMap::init(ic_memory!(ShardingRegistry, SHARDING_REGISTRY_ID)),
25 BTreeMap::init(ic_memory!(ShardingRegistry, SHARDING_ASSIGNMENT_ID)),
26 )
27 );
28}
29
30#[derive(CandidType, Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
36pub struct ShardKey {
37 pub pool: BoundedString32,
38 pub tenant: BoundedString128,
39}
40
41impl ShardKey {
42 pub const STORABLE_MAX_SIZE: u32 = 160;
43
44 #[must_use]
45 pub(crate) fn new(pool: &str, tenant: &str) -> Self {
46 Self {
47 pool: pool.try_into().unwrap(),
48 tenant: tenant.try_into().unwrap(),
49 }
50 }
51}
52
53impl_storable_bounded!(ShardKey, ShardKey::STORABLE_MAX_SIZE, false);
54
55#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
61pub struct ShardEntry {
62 #[serde(default = "ShardEntry::slot_default")]
64 pub slot: u32,
65 pub capacity: u32,
66 pub count: u32,
67 pub pool: String,
68 pub canister_type: CanisterRole,
69 pub created_at: u64,
70}
71
72impl ShardEntry {
73 pub const STORABLE_MAX_SIZE: u32 = 208;
74 pub const UNASSIGNED_SLOT: u32 = u32::MAX;
75
76 #[must_use]
77 pub(crate) fn new(
78 pool: &str,
79 slot: u32,
80 ty: CanisterRole,
81 capacity: u32,
82 created_at: u64,
83 ) -> Self {
84 Self {
85 slot,
86 canister_type: ty,
87 capacity,
88 count: 0,
89 pool: pool.to_string(),
90 created_at,
91 }
92 }
93
94 #[must_use]
96 pub const fn has_capacity(&self) -> bool {
97 self.count < self.capacity
98 }
99
100 #[must_use]
102 pub const fn load_bps(&self) -> Option<u64> {
103 if self.capacity == 0 {
104 None
105 } else {
106 Some((self.count as u64).saturating_mul(10_000) / self.capacity as u64)
107 }
108 }
109
110 #[inline]
111 const fn slot_default() -> u32 {
112 Self::UNASSIGNED_SLOT
113 }
114
115 #[must_use]
116 pub const fn has_assigned_slot(&self) -> bool {
117 self.slot != Self::UNASSIGNED_SLOT
118 }
119}
120
121impl_storable_bounded!(ShardEntry, ShardEntry::STORABLE_MAX_SIZE, false);
122
123pub(crate) struct ShardingCore<M: Memory> {
129 registry: BTreeMap<Principal, ShardEntry, M>,
130 assignments: BTreeMap<ShardKey, Principal, M>,
131}
132
133impl<M: Memory> ShardingCore<M> {
134 pub const fn new(
135 registry: BTreeMap<Principal, ShardEntry, M>,
136 assignments: BTreeMap<ShardKey, Principal, M>,
137 ) -> Self {
138 Self {
139 registry,
140 assignments,
141 }
142 }
143
144 pub(crate) fn insert_entry(&mut self, pid: Principal, entry: ShardEntry) {
149 self.registry.insert(pid, entry);
150 }
151
152 pub(crate) fn get_entry(&self, pid: &Principal) -> Option<ShardEntry> {
153 self.registry.get(pid)
154 }
155
156 pub(crate) fn all_entries(&self) -> Vec<(Principal, ShardEntry)> {
157 self.registry
158 .iter()
159 .map(|e| (*e.key(), e.value()))
160 .collect()
161 }
162
163 pub(crate) fn insert_assignment(&mut self, key: ShardKey, shard: Principal) {
168 self.assignments.insert(key, shard);
169 }
170
171 pub(crate) fn remove_assignment(&mut self, key: &ShardKey) -> Option<Principal> {
172 self.assignments.remove(key)
173 }
174
175 pub(crate) fn get_assignment(&self, key: &ShardKey) -> Option<Principal> {
176 self.assignments.get(key)
177 }
178
179 pub(crate) fn all_assignments(&self) -> Vec<(ShardKey, Principal)> {
180 self.assignments
181 .iter()
182 .map(|e| (e.key().clone(), e.value()))
183 .collect()
184 }
185}