Canister lifecycle methods init and post_upgrade expecting the same arguments is unintuitive

Creating a separate thread and continuing the conversation from here since this is a different issue.

Context is:

postupgrade also takes arguments. In Motoko, we assume this is the same type as the init args in actor class. In Rust, I think we made the same assumption, but theoretically it can take a different type. So for upgrade, you need to provide the init arg, and the behavior is specified in the post-upgrade function, not the init function.

Ideally, shouldn’t they be allowed to accept different arguments since on init one would be passing along initialization criteria that needs to be set once.
And on upgrade, one would be passing on things that need to be updated constantly.

Doesn’t make sense for them to accept/force the same parameter.

Also, the documentation doesn’t mention this here and here

The init documentation explicitly talks about arguments to the init function whereas the post_upgrade does not.

@chenyan Thoughts?

A quick workaround would be to have a struct like struct InstallParams {init: Option<*>, pre_upgrade: Option...}

Thank you for the input. I have a workaround at this stage already where I just read the incoming arg without requiring the parameter from the lifecycle parameter.

This thread is primarily meant as a bug report so that the API is ultimately updated to be uniform and intuitive across the different lifecycle methods :slight_smile:

2 Likes

Upgrade is always a confusing topic. When you install the canister, either init or postupgrade is invoked depending on the installation mode. Theoretically, the argument of these two functions can be different. But since both functions are considered as the “entry point” when you install the canister, it’s also weird for them to have different argument types.

Also, note that when you upgrade a canister, what gets invoked is the new canister’s postupgrade method, not the existing code. So you can always change the argument types in the new canister code to whatever type you want.

And on upgrade, one would be passing on things that need to be updated constantly.

I guess these parameters will be set during the initialization as well? I can see two workaround: 1) use optional record fields, so that you can set a subset of the parameters; 2) use a variant type as @GLdev suggested.

I’m open to suggestions for making these two “entry point” methods to have different types if there is a good use case.

Right, so that is the thing. There are a couple of inconsistencies here that are my main concern:

  • If the arguments are supposed to be the same, why does the init() method let you pass in the argument as shown here but post_upgrade doesn’t have any available arguments as shown here
    Also, you can just pass different arguments and consume them using arg_data which implies that there isn’t anything really stopping you from accepting different types between them
  • In which case, if different arguments are allowed, then why does dfx force you to pass the same argument type in both of those cases even though post_upgrade doesn’t even have a arg passed in the lifecycle method definition as shown below when we call dfx canister install <canister_name> --mode upgrade
    image

Additionally, if ic_cdk formalized the arguments and allowed different arguments, which it does, then the developer can just choose to pass the same type to both and be able to achieve the desired outcome of the same argument type. But passing different types would be difficult if having both of them be the same type is enforced everywhere.

This is basically an ask to consolidate and make it uniform across both dfx and ic_cdk. And allow different types to be passed and consumed. As well as add the argument to the lifecycle method here

That’s certainly a bug. cc @lwshang

Also, you can just pass different arguments and consume them using arg_data which implies that there isn’t anything really stopping you from accepting different types between them

Correct. At the platform level, there is nothing stopping you from doing this. The platform also doesn’t enforce you to use Candid. But we need to a good use case that init and post_upgrade has to have different types.

1 Like

There is no syntax for post_upgrade in Candid yet. When do upgrade mode canister install, dfx check the input argument with init arg type in candid. Which forces post_upgrade and init to take the same type of argument.

IMO, the type check is valuable for most cases. While make Candid be able to express post_upgrade may be complicated.

So I propose to simply add a flag to dfx canister install to skip argument type check.

And I certainly should improve #[post_upgrade] doc.

1 Like