clockworklabs / SpacetimeDB

Multiplayer at the speed of light
https://spacetimedb.com
Other
4.23k stars 105 forks source link

Callback when publishing without -c #1370

Open cloutiertyler opened 1 month ago

cloutiertyler commented 1 month ago

Callback when publishing without -c

Problem

The BitCraft team wants some custom logic to execute when they publish a module without using -c. One thing Alexander wants to be able to do is change the duration of a scheduled reducer (because the durations are part of the static data, which they can update from the client).

Proposed Solution #1

We could add a callback function that is analogous to the init(..) function, except its executed when publishing without -c. Basically in this new update(..) function he could lookup any scheduled reducers, cancel them all, and then reschedule them with the new updated value.

Perhaps we decorate like this:

#[spacetimedb(init)]
pub fn init() {
  // Do stuff in here that should only be done when starting from scratch
}

#[spacetimedb(update)]
pub fn update() {
  // Do stuff in here that should be done when starting from scratch *or* when a module is updated
}

This solution is in the same vein as a migration handler. As specified here this function would not have any ability to migrate any data, but it would give you a handle to be able to do operations on your module

Some questions to answer before we move forward on this:

Proposed Solution #2

(pasted from comments section)

For now we could just tell the BitCraft team to refactor their code. Any code that manages repeating reducers should be in a new reducer called manage_reducers(..) . This reducer would be called from init, but it would also be manually invoked after publishing without -c.

This ticket is about number 1 here:*

  1. You haven’t changed the schema and you’re intending to update the module and you want to run something when that happens
  2. You have changed the schema, but you haven’t written a migration function. If we continue with this you will be in a borked state so we should throw an error
  3. You want to do a migration, you’ve done a schema change and you’ve written a migration function.
cloutiertyler commented 1 month ago

I believe this is possibly already implemented, but we should use this ticket to make sure we're happy with the current design.

kim commented 1 month ago

Yes we have that: init is called on the first publish, and update is called on every subsequent update (aka publish-without-dash-c). Both are transactional, i.e. if the reducers return an error, nothing is committed to the database and either no database is running (on init), or the old database continues running (on update).

cloutiertyler commented 1 week ago

One thing I would like to review for the current design is that if your update function is not idempotent, then publishing a module twice without changing the code will apply the effects twice which might be surprising behavior. Not sure if we care about that though.

Perhaps we just remove this capability and leave it's implementation to be run as a migration with the migration UX

jdetter commented 1 week ago

Possible solution: when you publish a database update you can optionally specify a function to call when the update is applied. e.g.:

#[spacetimedb(init)]
pub fn init(...) {}

// This macro makes it possible to call this reducer during update
#[spacetimedb(update)]
pub fn do_module_update(...) {}

Then to publish:

spacetime publish -s  --update='do_module_update' my_module_name
kim commented 1 week ago

I like the idea of doing this via migrations -- as timer tables are just tables, even spawning a repeated task would fit into the model.

Perhaps we just remove this capability

Maybe replace with migrations.