From 74bfbab2fac6213baf3e5c44632083b7007a1004 Mon Sep 17 00:00:00 2001 From: Kim Altintop Date: Mon, 10 Jul 2023 09:42:14 +0200 Subject: [PATCH] Re-instantiate propagating the result of updating a db back to the user This cannot be satisfied in legacy cloud, but potentially it can in the next iteration of it. --- crates/client-api/src/lib.rs | 25 ++++++++-- crates/client-api/src/routes/database.rs | 47 ++++++++++++++----- crates/standalone/src/lib.rs | 60 +++++++++++------------- test/tests/update-module.sh | 2 +- 4 files changed, 84 insertions(+), 50 deletions(-) diff --git a/crates/client-api/src/lib.rs b/crates/client-api/src/lib.rs index 0a5cd41b730..ce1ff4a6805 100644 --- a/crates/client-api/src/lib.rs +++ b/crates/client-api/src/lib.rs @@ -7,7 +7,7 @@ use spacetimedb::address::Address; use spacetimedb::auth::identity::{DecodingKey, EncodingKey}; use spacetimedb::client::ClientActorIndex; use spacetimedb::database_instance_context_controller::DatabaseInstanceContextController; -use spacetimedb::host::HostController; +use spacetimedb::host::{HostController, UpdateDatabaseResult}; use spacetimedb::identity::Identity; use spacetimedb::messages::control_db::{Database, DatabaseInstance, EnergyBalance, IdentityEmail, Node}; use spacetimedb::messages::worker_db::DatabaseInstanceState; @@ -122,7 +122,21 @@ pub trait ControlStateReadAccess { pub trait ControlStateWriteAccess: Send + Sync { // Databases async fn create_address(&self) -> spacetimedb::control_db::Result
; - async fn publish_database(&self, identity: &Identity, spec: DatabaseDef) -> spacetimedb::control_db::Result<()>; + + /// Publish a database acc. to [`DatabaseDef`]. + /// + /// If the database with the given address was successfully published before, + /// it is updated acc. to the module lifecycle conventions. `Some` result is + /// returned in that case. + /// + /// Otherwise, `None` is returned meaning that the database was freshly + /// initialized. + async fn publish_database( + &self, + identity: &Identity, + spec: DatabaseDef, + ) -> spacetimedb::control_db::Result>; + async fn delete_database(&self, identity: &Identity, address: &Address) -> spacetimedb::control_db::Result<()>; // Identities @@ -224,9 +238,14 @@ impl ControlStateWriteAccess for ArcEnv self.0.create_address().await } - async fn publish_database(&self, identity: &Identity, spec: DatabaseDef) -> spacetimedb::control_db::Result<()> { + async fn publish_database( + &self, + identity: &Identity, + spec: DatabaseDef, + ) -> spacetimedb::control_db::Result> { self.0.publish_database(identity, spec).await } + async fn delete_database(&self, identity: &Identity, address: &Address) -> spacetimedb::control_db::Result<()> { self.0.delete_database(identity, address).await } diff --git a/crates/client-api/src/routes/database.rs b/crates/client-api/src/routes/database.rs index 2c5849e16d6..3686156bd14 100644 --- a/crates/client-api/src/routes/database.rs +++ b/crates/client-api/src/routes/database.rs @@ -12,6 +12,7 @@ use spacetimedb::host::EntityDef; use spacetimedb::host::ReducerArgs; use spacetimedb::host::ReducerCallError; use spacetimedb::host::ReducerOutcome; +use spacetimedb::host::UpdateDatabaseSuccess; use spacetimedb_lib::name; use spacetimedb_lib::name::DomainName; use spacetimedb_lib::name::DomainParsingError; @@ -581,7 +582,6 @@ pub async fn dns( Query(DNSQueryParams {}): Query, ) -> axum::response::Result { let domain = database_name.parse().map_err(DomainParsingRejection)?; - log::debug!("dns with `{domain}`"); let address = ctx.lookup_address(&domain).map_err(log_and_500)?; let response = if let Some(address) = address { DnsLookupResponse::Success { @@ -804,20 +804,41 @@ pub async fn publish( } }; - ctx.publish_database( - &auth.identity, - DatabaseDef { - address: db_addr, - program_bytes: body.into(), - num_replicas: 1, - trace_log: should_trace(trace_log), - }, - ) - .await - .map_err(log_and_500)?; + let maybe_updated = ctx + .publish_database( + &auth.identity, + DatabaseDef { + address: db_addr, + program_bytes: body.into(), + num_replicas: 1, + trace_log: should_trace(trace_log), + }, + ) + .await + .map_err(log_and_500)?; + + if let Some(updated) = maybe_updated { + match updated { + Ok(success) => { + if let UpdateDatabaseSuccess { + // An update reducer was defined, and it was run + update_result: Some(update_result), + // Not yet implemented + migrate_results: _, + } = success + { + let ror = reducer_outcome_response(&auth.identity, "update", update_result.outcome); + if !matches!(ror, (StatusCode::OK, _)) { + return Err(ror.into()); + } + } + } + Err(e) => return Err((StatusCode::BAD_REQUEST, format!("Database update rejected: {e}")).into()), + } + } Ok(axum::Json(PublishResult::Success { - domain: db_name.map(|domain| domain.to_string()), + domain: db_name.as_ref().map(ToString::to_string), address: db_addr.to_hex(), op, })) diff --git a/crates/standalone/src/lib.rs b/crates/standalone/src/lib.rs index a70c8444a77..cef713cc1ea 100644 --- a/crates/standalone/src/lib.rs +++ b/crates/standalone/src/lib.rs @@ -25,7 +25,6 @@ use spacetimedb::sendgrid_controller::SendGridController; use spacetimedb::{stdb_path, worker_metrics}; use spacetimedb_lib::name::{DomainName, InsertDomainResult, RegisterTldResult, Tld}; use spacetimedb_lib::recovery::RecoveryCode; -use spacetimedb_lib::Hash; use std::fs::File; use std::io::Write; use std::path::{Path, PathBuf}; @@ -271,19 +270,27 @@ impl spacetimedb_client_api::ControlStateWriteAccess for StandaloneEnv { &self, identity: &Identity, spec: spacetimedb_client_api::DatabaseDef, - ) -> spacetimedb::control_db::Result<()> { + ) -> spacetimedb::control_db::Result> { let existing_db = self.control_db.get_database_by_address(&spec.address)?; - let mut database = existing_db.clone().unwrap_or(Database { - id: 0, - address: spec.address, - identity: *identity, - host_type: HostType::Wasmer, - num_replicas: spec.num_replicas, - program_bytes_address: Hash::ZERO, - trace_log: spec.trace_log, - }); - let addr = self.object_db.insert_object(spec.program_bytes)?; - database.program_bytes_address = addr; + let program_bytes_address = self.object_db.insert_object(spec.program_bytes)?; + let mut database = match existing_db.as_ref() { + Some(existing) => Database { + address: spec.address, + num_replicas: spec.num_replicas, + program_bytes_address, + trace_log: spec.trace_log, + ..existing.clone() + }, + None => Database { + id: 0, + address: spec.address, + identity: *identity, + host_type: HostType::Wasmer, + num_replicas: spec.num_replicas, + program_bytes_address, + trace_log: spec.trace_log, + }, + }; if let Some(existing) = existing_db.as_ref() { if &existing.identity != identity { @@ -306,11 +313,14 @@ impl spacetimedb_client_api::ControlStateWriteAccess for StandaloneEnv { self.schedule_database(Some(database), existing_db).await?; if should_update_instances { - // TODO(kim): cannot return result in cloud nor here - let _ = self.update_database_instances(database_id).await?; + let leader = self + .control_db + .get_leader_database_instance_by_database(database_id) + .ok_or_else(|| anyhow!("Not found: leader instance for database {database_id}"))?; + Ok(self.update_database_instance(leader).await?) + } else { + Ok(None) } - - Ok(()) } async fn delete_database(&self, identity: &Identity, address: &Address) -> spacetimedb::control_db::Result<()> { @@ -452,22 +462,6 @@ impl StandaloneEnv { Ok(()) } - // TODO(kim): update should only run on the leader instance, and this - // method should return a single result - async fn update_database_instances( - &self, - database_id: u64, - ) -> Result>, anyhow::Error> { - let instances = self.control_db.get_database_instances_by_database(database_id)?; - let mut results = Vec::with_capacity(instances.len()); - for instance in instances { - let res = self.update_database_instance(instance).await?; - results.push(res); - } - - Ok(results) - } - async fn deschedule_replicas(&self, database_id: u64, num_replicas: u32) -> Result<(), anyhow::Error> { for _ in 0..num_replicas { let instances = self.control_db.get_database_instances_by_database(database_id)?; diff --git a/test/tests/update-module.sh b/test/tests/update-module.sh index 1b88212b091..c571ecf41aa 100644 --- a/test/tests/update-module.sh +++ b/test/tests/update-module.sh @@ -24,7 +24,7 @@ pub struct Person { #[spacetimedb(reducer)] pub fn add(name: String) { - Person::insert(Person { id: 0, name }); + let _ = Person::insert(Person { id: 0, name }).unwrap(); } #[spacetimedb(reducer)]