Skip to content

Commit

Permalink
fix: cycle estimation for canister (#414)
Browse files Browse the repository at this point in the history
* fix cycle estimation for canister

* fix cycles test

* further optimize cycle estimation
  • Loading branch information
ravi-sawlani-yral authored Oct 4, 2024
1 parent 7a8eefe commit d5432bc
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 31 deletions.
7 changes: 5 additions & 2 deletions src/canister/user_index/src/util/canister_management.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use shared_utils::{
constant::{
INDIVIDUAL_USER_CANISTER_RECHARGE_AMOUNT, SUBNET_ORCHESTRATOR_CANISTER_CYCLES_THRESHOLD,
},
cycles::calculate_recharge_and_threshold_cycles_for_canister,
cycles::calculate_threshold_and_recharge_cycles_for_canister,
};

use crate::CANISTER_DATA;
Expand Down Expand Up @@ -198,9 +198,12 @@ pub async fn recharge_canister_if_below_threshold(canister_id: &Principal) -> Re
let idle_cycles_burned_per_day =
u128::try_from(individual_canister_status.idle_cycles_burned_per_day.0)
.map_err(|e| e.to_string())?;
let reserved_cycles = u128::try_from(individual_canister_status.reserved_cycles.0)
.map_err(|e| e.to_string())?;
let (threshold_balance, recharge_amount) =
calculate_recharge_and_threshold_cycles_for_canister(
calculate_threshold_and_recharge_cycles_for_canister(
idle_cycles_burned_per_day,
reserved_cycles,
None,
);
let individual_canister_current_balance =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use ic_cdk::api::management_canister::main::{
use ic_stable_structures::Log;
use shared_utils::{
canister_specific::platform_orchestrator::types::args::UpgradeCanisterArg,
cycles::calculate_recharge_and_threshold_cycles_for_canister,
cycles::calculate_threshold_and_recharge_cycles_for_canister,
};

use crate::CANISTER_DATA;
Expand Down Expand Up @@ -45,11 +45,16 @@ impl IndividualUserCanister {
let idle_cycles_burned_in_a_day =
u128::try_from(user_canister_status.idle_cycles_burned_per_day.0)
.map_err(|e| e.to_string())?;
let reserved_cycles =
u128::try_from(user_canister_status.reserved_cycles.0).map_err(|e| e.to_string())?;
let current_user_canister_balance =
u128::try_from(user_canister_status.cycles.0).map_err(|e| e.to_string())?;

let (threeshold, recharge_amount) =
calculate_recharge_and_threshold_cycles_for_canister(idle_cycles_burned_in_a_day, None);
let (threeshold, recharge_amount) = calculate_threshold_and_recharge_cycles_for_canister(
idle_cycles_burned_in_a_day,
reserved_cycles,
None,
);

if current_user_canister_balance <= threeshold {
return deposit_cycles(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1062,6 +1062,8 @@ fn hotornot_game_simulation_test_2() {
})
.unwrap();

pic.add_cycles(last_individual_template_canister_id, 2_000_000_000_000); //recharge by 2T cycles

// All 500 users bet on the post

for i in 1..=110 {
Expand Down
8 changes: 4 additions & 4 deletions src/lib/shared_utils/src/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ pub const BASE_COST_FOR_INGRESS_MESSAGE: u128 = 1_200_000; //1.2M cycles
pub const BASE_COST_FOR_EXECUTION: u128 = 590_000; // 590K cycles
pub const COST_PER_BYTE_FOR_INGRESS_MESSAGE: u128 = 2_000; //2k cycles
pub const COST_PER_BILLION_INSTRUCTION_EXECUTED: u128 = 400_000_000; //400M cycles for per 1B instructions
pub const ASSUMED_NUMBER_OF_INGRESS_CALL_PER_SEC: u128 = 1;
pub const ASSUMED_BYTES_PER_INGRESS_CALL: u128 = 10; // 10 bytes
pub const ASSUMED_NUMBER_OF_INSTRUCTIONS_PER_INGRESS_CALL: u128 = 5_000_000; //5M instructions
pub const ASSUMED_NUMBER_OF_INGRESS_CALL_PER_DAY: u128 = 150;
pub const ASSUMED_BYTES_PER_INGRESS_CALL: u128 = 100; // 100 bytes
pub const ASSUMED_NUMBER_OF_INSTRUCTIONS_PER_INGRESS_CALL: u128 = 5_000_000_000; //5B instructions (no matter the number of instructions executed 5B is reserved)
pub const RESERVED_NUMBER_OF_INSTRUCTIONS_FOR_INSTALL_CODE: u128 = 200_000_000_000; //200B instructions
pub const THRESHOLD_NUMBER_OF_DAYS_TO_KEEP_CANISTER_RUNNING: u128 = 3;
pub const THRESHOLD_NUMBER_OF_DAYS_TO_KEEP_CANISTER_RUNNING: u128 = 1;
pub const MAX_NUMBER_OF_DAYS_TO_KEEP_CANISTER_RUNNING: u128 = 7;

pub fn get_backup_individual_user_canister_batch_size() -> u64 {
Expand Down
106 changes: 84 additions & 22 deletions src/lib/shared_utils/src/cycles.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::constant::{
ASSUMED_BYTES_PER_INGRESS_CALL, ASSUMED_NUMBER_OF_INGRESS_CALL_PER_SEC,
ASSUMED_BYTES_PER_INGRESS_CALL, ASSUMED_NUMBER_OF_INGRESS_CALL_PER_DAY,
ASSUMED_NUMBER_OF_INSTRUCTIONS_PER_INGRESS_CALL, BASE_COST_FOR_EXECUTION,
BASE_COST_FOR_INGRESS_MESSAGE, COST_PER_BILLION_INSTRUCTION_EXECUTED,
COST_PER_BYTE_FOR_INGRESS_MESSAGE, DEFAULT_FREEZING_THRESHOLD,
Expand Down Expand Up @@ -32,32 +32,75 @@ pub fn get_cycles_required_per_ingress_message_reception() -> u128 {
res
}

pub fn calculate_cost_for_canister_per_day(idle_cycles_burned_per_day: u128) -> u128 {
let ingress_message_cycles_cost_per_day = (24 * 60 * 60)
* get_cycles_required_per_ingress_message_reception()
* ASSUMED_NUMBER_OF_INGRESS_CALL_PER_SEC;
let storage_cost_per_day = idle_cycles_burned_per_day;
let execution_cost_per_day = (24 * 60 * 60)
* get_execution_cost_per_ingress_message()
* ASSUMED_NUMBER_OF_INGRESS_CALL_PER_SEC;
pub fn calculate_compute_cost_for_canister_per_day() -> u128 {
let ingress_message_cycles_cost_per_day = get_cycles_required_per_ingress_message_reception()
* ASSUMED_NUMBER_OF_INGRESS_CALL_PER_DAY;
let execution_cost_per_day =
get_execution_cost_per_ingress_message() * ASSUMED_NUMBER_OF_INGRESS_CALL_PER_DAY;

ingress_message_cycles_cost_per_day + storage_cost_per_day + execution_cost_per_day
ingress_message_cycles_cost_per_day + execution_cost_per_day
}

pub fn calculate_recharge_and_threshold_cycles_for_canister(
fn calculate_threshold_and_recharge_cycles_for_storage_of_canister(
idle_cycles_burned_per_day: u128,
freezing_threshold_in_days: Option<u128>,
) -> (u128, u128) {
let freezing_threshold_cycles = get_cycles_reserved_in_freezing_threshold(
idle_cycles_burned_per_day,
freezing_threshold_in_days,
);
let canister_cost_per_day = calculate_cost_for_canister_per_day(idle_cycles_burned_per_day);
let threshold_cycles_to_keep_canister_running: u128 = freezing_threshold_cycles
+ (THRESHOLD_NUMBER_OF_DAYS_TO_KEEP_CANISTER_RUNNING * canister_cost_per_day);
let mut recharge_amount_for_canister: u128 = freezing_threshold_cycles
+ (MAX_NUMBER_OF_DAYS_TO_KEEP_CANISTER_RUNNING * canister_cost_per_day);
recharge_amount_for_canister = recharge_amount_for_canister.min(5_000_000_000_000); //maximum 5T cycles alloted

let threshold_storage_cost_for_canister = freezing_threshold_cycles
+ (THRESHOLD_NUMBER_OF_DAYS_TO_KEEP_CANISTER_RUNNING * idle_cycles_burned_per_day);

let storage_recharge_amount_for_canister = freezing_threshold_cycles
+ (MAX_NUMBER_OF_DAYS_TO_KEEP_CANISTER_RUNNING * idle_cycles_burned_per_day);

(
threshold_storage_cost_for_canister,
storage_recharge_amount_for_canister,
)
}

fn calculate_threshold_and_recharge_cycles_for_compute_of_canister() -> (u128, u128) {
let canister_compute_cost_per_day = calculate_compute_cost_for_canister_per_day();
let threshold_compute_cost_for_canister: u128 =
THRESHOLD_NUMBER_OF_DAYS_TO_KEEP_CANISTER_RUNNING * canister_compute_cost_per_day;
let recharge_compute_cost_for_canister =
MAX_NUMBER_OF_DAYS_TO_KEEP_CANISTER_RUNNING * canister_compute_cost_per_day;

(
threshold_compute_cost_for_canister,
recharge_compute_cost_for_canister,
)
}

pub fn calculate_threshold_and_recharge_cycles_for_canister(
idle_cycles_burned_per_day: u128,
reserved_cycles: u128,
freezing_threshold_in_days: Option<u128>,
) -> (u128, u128) {
let mut threshold_cycles_to_keep_canister_running = 0_u128;
let mut recharge_amount_for_canister: u128 = 0_128;

let (threshold_storage_cost_for_canister, storage_recharge_amount_for_canister) =
calculate_threshold_and_recharge_cycles_for_storage_of_canister(
idle_cycles_burned_per_day,
freezing_threshold_in_days,
);

// If reserved cycles are not enough to pay for storage cost, we need to recharge the main balance
if reserved_cycles <= threshold_storage_cost_for_canister {
threshold_cycles_to_keep_canister_running += threshold_storage_cost_for_canister;
recharge_amount_for_canister += storage_recharge_amount_for_canister;
}

let (threshold_compute_cost_for_canister, recharge_compute_cost_for_canister) =
calculate_threshold_and_recharge_cycles_for_compute_of_canister();

threshold_cycles_to_keep_canister_running += threshold_compute_cost_for_canister;
recharge_amount_for_canister += recharge_compute_cost_for_canister;
recharge_amount_for_canister = recharge_amount_for_canister.min(3_000_000_000_000); //maximum 3T cycles alloted
(
threshold_cycles_to_keep_canister_running,
recharge_amount_for_canister,
Expand Down Expand Up @@ -86,22 +129,41 @@ mod test {
#[test]
fn test_cycles_required_for_upgrade_for_idle_canister() {
let (threshold, recharge) =
calculate_recharge_and_threshold_cycles_for_canister(27_000_000, None);
calculate_threshold_and_recharge_cycles_for_canister(27_000_000, 0, None);
let cycles_required_for_upgrade = calulate_required_cycles_for_upgrading(27_000_000, None);
assert!(threshold > cycles_required_for_upgrade);
assert!(recharge > cycles_required_for_upgrade);
assert!(recharge < 5_000_000_000_000);
assert!(recharge < 3_000_000_000_000);
}

#[test]
fn test_threshold_and_recharge_for_filled_canister() {
let idle_cycles_burned_per_day: u128 = 5_000_000_000;
let (threshold, recharge) =
calculate_recharge_and_threshold_cycles_for_canister(idle_cycles_burned_per_day, None);
let (threshold, recharge) = calculate_threshold_and_recharge_cycles_for_canister(
idle_cycles_burned_per_day,
0,
None,
);
let cycles_required_for_upgrade =
calulate_required_cycles_for_upgrading(idle_cycles_burned_per_day, None);
assert!(threshold > cycles_required_for_upgrade);
assert!(recharge > cycles_required_for_upgrade);
assert!(recharge < 3_000_000_000_000);
}

#[test]
fn test_threshold_and_recharge_for_filled_canister_with_reserved_cycles() {
let idle_cycles_burned_per_day: u128 = 5_000_000_000;
let reserved_cycles = 769_189_622_552_u128;
let (threshold, recharge) = calculate_threshold_and_recharge_cycles_for_canister(
idle_cycles_burned_per_day,
reserved_cycles,
None,
);
let cycles_required_for_upgrade =
calulate_required_cycles_for_upgrading(idle_cycles_burned_per_day, None);
assert!(threshold > cycles_required_for_upgrade);
assert!(recharge > cycles_required_for_upgrade);
assert!(recharge < 5_000_000_000_000);
assert!(recharge < 3_000_000_000_000);
}
}

0 comments on commit d5432bc

Please sign in to comment.