diff --git a/crates/core/src/host/host_controller.rs b/crates/core/src/host/host_controller.rs index dc34677b4a1..2f9c232bb0b 100644 --- a/crates/core/src/host/host_controller.rs +++ b/crates/core/src/host/host_controller.rs @@ -100,14 +100,14 @@ pub struct ModuleHostWithBootstrap { /// The handle owns any wait state needed to prove that the database has a /// durable `st_module` row. pub struct BootstrapCompletion { - program_hash: Hash, + bootstrap_generation: u64, status: ProgramBootstrap, } /// Has the module been bootstrapped from the controldb? /// /// Once we have inserted into `st_module`, bootstrapping is no longer necessary, -/// and the initial program bytes can be dropped fromt the controldb. +/// and the initial program bytes can be dropped from the controldb. enum ProgramBootstrap { /// The module's program bytes have already been written to `st_module` Durable, @@ -119,16 +119,16 @@ enum ProgramBootstrap { } impl BootstrapCompletion { - fn durable(program_hash: Hash) -> Self { + fn durable(bootstrap_generation: u64) -> Self { Self { - program_hash, + bootstrap_generation, status: ProgramBootstrap::Durable, } } - fn pending(program_hash: Hash, tx_offset: TransactionOffset, durable_offset: Option) -> Self { + fn pending(bootstrap_generation: u64, tx_offset: TransactionOffset, durable_offset: Option) -> Self { Self { - program_hash, + bootstrap_generation, status: ProgramBootstrap::Pending { tx_offset, durable_offset, @@ -136,15 +136,15 @@ impl BootstrapCompletion { } } - pub fn program_hash(&self) -> Hash { - self.program_hash + pub fn bootstrap_generation(&self) -> u64 { + self.bootstrap_generation } /// Wait until it is safe to complete program bootstrap in controldb. /// That is, wait until the initial `st_module` insert becomes durable. - pub async fn wait(self) -> anyhow::Result { + pub async fn wait(self) -> anyhow::Result<()> { match self.status { - ProgramBootstrap::Durable => Ok(self.program_hash), + ProgramBootstrap::Durable => Ok(()), ProgramBootstrap::Pending { tx_offset, durable_offset, @@ -160,7 +160,7 @@ impl BootstrapCompletion { .context("failed waiting for initialized program to become durable")?; } - Ok(self.program_hash) + Ok(()) } } } @@ -1132,7 +1132,8 @@ impl Host { (program, true) } }; - let mut bootstrap_completion = Some(BootstrapCompletion::durable(program.hash)); + let bootstrap_generation = database.bootstrap_generation; + let mut bootstrap_completion = Some(BootstrapCompletion::durable(bootstrap_generation)); let relational_db = Arc::new(db); let (program, launched) = match HostType::from(program.kind) { @@ -1222,13 +1223,12 @@ impl Host { }; if program_needs_init { - let program_hash = program.hash; let InitDatabaseResult { reducer, tx_offset } = launched.module_host.init_database(program).await?; if let Some(call_result) = reducer { Result::from(call_result)?; } bootstrap_completion = Some(BootstrapCompletion::pending( - program_hash, + bootstrap_generation, tx_offset, launched.module_host.durable_tx_offset(), )); @@ -1540,6 +1540,7 @@ pub(crate) async fn extract_schema_with_pools( owner_identity, host_type, initial_program: program.hash, + bootstrap_generation: 0, }; let core = AllocatedJobCore::default(); diff --git a/crates/core/src/host/instance_env.rs b/crates/core/src/host/instance_env.rs index a0601f9a3b5..982ab6dcbff 100644 --- a/crates/core/src/host/instance_env.rs +++ b/crates/core/src/host/instance_env.rs @@ -1377,6 +1377,7 @@ mod test { owner_identity: Identity::ZERO, host_type: HostType::Wasm, initial_program: Hash::ZERO, + bootstrap_generation: 0, }, replica_id: 0, logger, diff --git a/crates/core/src/messages/control_db.rs b/crates/core/src/messages/control_db.rs index 865be57e410..8a28a95d8e3 100644 --- a/crates/core/src/messages/control_db.rs +++ b/crates/core/src/messages/control_db.rs @@ -37,6 +37,8 @@ pub struct Database { /// /// Updating the database's module will **not** change this value. pub initial_program: Hash, + /// Generation of the current bootstrap requirement for `initial_program`. + pub bootstrap_generation: u64, } #[derive(Clone, PartialEq, Serialize, Deserialize)] diff --git a/crates/standalone/src/control_db.rs b/crates/standalone/src/control_db.rs index 3128fb1d248..a637d38afcc 100644 --- a/crates/standalone/src/control_db.rs +++ b/crates/standalone/src/control_db.rs @@ -668,6 +668,7 @@ mod compat { owner_identity, host_type, initial_program, + bootstrap_generation: 0, } } } @@ -680,6 +681,7 @@ mod compat { owner_identity, host_type, initial_program, + bootstrap_generation: _, }: CanonicalDatabase, ) -> Self { Self { diff --git a/crates/standalone/src/control_db/tests.rs b/crates/standalone/src/control_db/tests.rs index e1e09c92a49..171fcadffc7 100644 --- a/crates/standalone/src/control_db/tests.rs +++ b/crates/standalone/src/control_db/tests.rs @@ -126,6 +126,7 @@ fn test_decode() -> ResultTest<()> { owner_identity: id, host_type: HostType::Wasm, initial_program: Hash::ZERO, + bootstrap_generation: 0, }; cdb.insert_database(db.clone())?; diff --git a/crates/standalone/src/lib.rs b/crates/standalone/src/lib.rs index 541dd5b6a39..f806f18e995 100644 --- a/crates/standalone/src/lib.rs +++ b/crates/standalone/src/lib.rs @@ -292,6 +292,7 @@ impl spacetimedb_client_api::ControlStateWriteAccess for StandaloneEnv { owner_identity: *publisher, host_type: spec.host_type, initial_program: program.hash, + bootstrap_generation: 0, }; let _hash_for_assert = program.hash; @@ -432,9 +433,8 @@ impl spacetimedb_client_api::ControlStateWriteAccess for StandaloneEnv { .await?; let _stored_hash_for_assert = self.program_store.put(program_bytes).await?; debug_assert_eq!(_hash_for_assert, _stored_hash_for_assert); - - self.control_db.update_database(database)?; } + self.control_db.update_database(database)?; for instance in self.control_db.get_replicas_by_database(database_id)? { self.delete_replica(instance.id).await?;