diff --git a/backend/canisters/user_index/CHANGELOG.md b/backend/canisters/user_index/CHANGELOG.md index 9c178d472b..9e1bc07d43 100644 --- a/backend/canisters/user_index/CHANGELOG.md +++ b/backend/canisters/user_index/CHANGELOG.md @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [unreleased] +### Added + +- Uninstall canisters of empty users ([#6018](https://github.com/open-chat-labs/open-chat/pull/6018)) + ## [[2.0.1231](https://github.com/open-chat-labs/open-chat/releases/tag/v2.0.1231-user_index)] - 2024-07-08 ### Fixed diff --git a/backend/canisters/user_index/impl/src/lib.rs b/backend/canisters/user_index/impl/src/lib.rs index b4cd25c61a..96cef759d7 100644 --- a/backend/canisters/user_index/impl/src/lib.rs +++ b/backend/canisters/user_index/impl/src/lib.rs @@ -231,6 +231,8 @@ impl RuntimeState { oc_public_key: self.data.oc_key_pair.public_key_pem().to_string(), empty_users: self.data.empty_users.iter().take(100).copied().collect(), empty_users_length: self.data.empty_users.len(), + deleted_users: self.data.deleted_users.iter().take(100).map(|u| u.user_id).collect(), + deleted_users_length: self.data.deleted_users.len(), } } } @@ -490,6 +492,8 @@ pub struct Metrics { pub oc_public_key: String, pub empty_users: Vec, pub empty_users_length: usize, + pub deleted_users: Vec, + pub deleted_users_length: usize, } #[derive(Serialize, Debug)] diff --git a/backend/canisters/user_index/impl/src/lifecycle/post_upgrade.rs b/backend/canisters/user_index/impl/src/lifecycle/post_upgrade.rs index 2fda4e04cf..1bdebebf9c 100644 --- a/backend/canisters/user_index/impl/src/lifecycle/post_upgrade.rs +++ b/backend/canisters/user_index/impl/src/lifecycle/post_upgrade.rs @@ -1,9 +1,10 @@ use crate::lifecycle::{init_env, init_state}; use crate::memory::get_upgrades_memory; -use crate::Data; +use crate::{mutate_state, Data}; use canister_logger::LogEntry; use canister_tracing_macros::trace; use ic_cdk::post_upgrade; +use local_user_index_canister::{DeleteUser, Event}; use stable_memory::get_reader; use tracing::info; use user_index_canister::post_upgrade::Args; @@ -25,16 +26,18 @@ fn post_upgrade(args: Args) { info!(version = %args.wasm_version, "Post-upgrade complete"); - // Enable this code block once Users and LocalUserIndexes have been upgraded - // mutate_state(|state| { - // for user_id in state.data.empty_users.iter().copied() { - // state.push_event_to_local_user_index( - // user_id, - // Event::DeleteUser(DeleteUser { - // user_id, - // triggered_by_user: false, - // }), - // ); - // } - // }) + mutate_state(|state| { + for user_id in std::mem::take(&mut state.data.empty_users) { + if let Some(canister_id) = state.data.local_index_map.get_index_canister(&user_id) { + state.data.user_index_event_sync_queue.push( + canister_id, + Event::DeleteUser(DeleteUser { + user_id, + triggered_by_user: false, + }), + ); + } + } + crate::jobs::sync_events_to_local_user_index_canisters::start_job_if_required(state); + }) } diff --git a/backend/canisters/user_index/impl/src/model/local_user_index_map.rs b/backend/canisters/user_index/impl/src/model/local_user_index_map.rs index 86873ca7d5..a5648ccdd6 100644 --- a/backend/canisters/user_index/impl/src/model/local_user_index_map.rs +++ b/backend/canisters/user_index/impl/src/model/local_user_index_map.rs @@ -44,6 +44,18 @@ impl LocalUserIndexMap { false } + pub fn remove_user(&mut self, user_id: &UserId) -> bool { + if let Some(index) = self.user_to_index.remove(user_id) { + self.index_map + .entry(index) + .and_modify(|i| i.user_count = i.user_count.saturating_sub(1)); + + true + } else { + false + } + } + pub fn index_for_new_user(&self) -> Option { self.index_map .iter() diff --git a/backend/canisters/user_index/impl/src/updates/c2c_notify_events.rs b/backend/canisters/user_index/impl/src/updates/c2c_notify_events.rs index f4ad7ab93e..d67a8fd4c6 100644 --- a/backend/canisters/user_index/impl/src/updates/c2c_notify_events.rs +++ b/backend/canisters/user_index/impl/src/updates/c2c_notify_events.rs @@ -90,6 +90,8 @@ fn handle_event(event: Event, state: &mut RuntimeState) { Event::UserDeleted(ev) => { let now = state.env.now(); state.data.users.delete_user(ev.user_id, now); + state.data.local_index_map.remove_user(&ev.user_id); + state.data.empty_users.remove(&ev.user_id); state.data.deleted_users.push(DeletedUser { user_id: ev.user_id, triggered_by_user: false,