diff --git a/client b/client index a330f747..5214752e 160000 --- a/client +++ b/client @@ -1 +1 @@ -Subproject commit a330f747090baf32825ae6a7caf5f8d95de6d863 +Subproject commit 5214752ef590a650bbb50786aa99d7228e6dc539 diff --git a/common/proto/tables_data.proto b/common/proto/tables_data.proto index eb855980..7c9d953a 100644 --- a/common/proto/tables_data.proto +++ b/common/proto/tables_data.proto @@ -47,17 +47,15 @@ service TablesData { // - Updates the row and returns the id; queues search indexing (best effort) rpc PutTableData(PutTableDataRequest) returns (PutTableDataResponse); - // Performs a PUT after the user explicitly accepted its cross-profile impact. - // This is meaningful only for global tables; ordinary tables behave exactly - // like PutTableData. - rpc PutTableDataConfirmed(PutTableDataRequest) returns (PutTableDataResponse); + // Performs a PUT after the user explicitly accepted changing a referenced version. + rpc PutTableDataConfirmed(PutTableDataConfirmedRequest) returns (PutTableDataResponse); - // Lists the other profiles whose rows currently point at this global row. - rpc GetGlobalTableUpdateImpact(GlobalTableUpdateImpactRequest) returns (GlobalTableUpdateImpactResponse); + // Lists profiles whose rows currently point at this row version. + rpc GetTableUpdateImpact(TableUpdateImpactRequest) returns (TableUpdateImpactResponse); - // Snapshots the current version of a global row and advances its version. + // Snapshots the current version of a row and advances its version. // Existing references remain immutable; new references use the new version. - rpc ArchiveGlobalTableData(ArchiveGlobalTableDataRequest) returns (ArchiveGlobalTableDataResponse); + rpc ArchiveTableData(ArchiveTableDataRequest) returns (ArchiveTableDataResponse); // Soft-delete a single record (sets deleted = true) if it exists and is not already deleted. // @@ -80,8 +78,8 @@ service TablesData { // - If the physical table is missing but the definition exists, returns INTERNAL rpc GetTableData(GetTableDataRequest) returns (GetTableDataResponse); - // Fetches one exact version of a global row. - rpc GetGlobalTableDataVersion(GetGlobalTableDataVersionRequest) returns (GetTableDataResponse); + // Fetches one exact version of a row. + rpc GetTableDataVersion(GetTableDataVersionRequest) returns (GetTableDataResponse); // Count non-deleted rows in a table. // @@ -214,6 +212,11 @@ message PutTableDataRequest { int64 expected_revision = 5; } +message PutTableDataConfirmedRequest { + PutTableDataRequest update = 1; + repeated string expected_affected_profiles = 2; +} + // Update response. message PutTableDataResponse { // True if the update succeeded (or no-op on empty data). @@ -229,24 +232,24 @@ message PutTableDataResponse { int64 row_revision = 4; } -message GlobalTableUpdateImpactRequest { +message TableUpdateImpactRequest { string profile_name = 1; string table_name = 2; int64 id = 3; } -message GlobalTableUpdateImpactResponse { +message TableUpdateImpactResponse { repeated string affected_profiles = 1; } -message ArchiveGlobalTableDataRequest { +message ArchiveTableDataRequest { string profile_name = 1; string table_name = 2; int64 id = 3; int64 expected_revision = 4; } -message ArchiveGlobalTableDataResponse { +message ArchiveTableDataResponse { bool success = 1; int64 archived_version = 2; string archived_at = 3; @@ -289,7 +292,7 @@ message GetTableDataRequest { } -message GetGlobalTableDataVersionRequest { +message GetTableDataVersionRequest { string profile_name = 1; string table_name = 2; int64 id = 3; diff --git a/common/src/grpc_error.rs b/common/src/grpc_error.rs index 80245001..97497f24 100644 --- a/common/src/grpc_error.rs +++ b/common/src/grpc_error.rs @@ -1,5 +1,6 @@ pub const ERROR_REASON_METADATA_KEY: &str = "komp-ac-error-reason"; +pub const AFFECTED_PROFILES_METADATA_KEY: &str = "komp-ac-affected-profiles"; pub const COMPUTED_VALUE_MISMATCH_REASON: &str = "computed-value-mismatch"; pub const ROW_STALE_REASON: &str = "row-stale"; pub const ROW_ID_CONFLICT_REASON: &str = "row-id-conflict"; -pub const GLOBAL_UPDATE_CONFIRMATION_REASON: &str = "global-update-confirmation"; +pub const VERSIONED_UPDATE_CONFIRMATION_REASON: &str = "versioned-update-confirmation"; diff --git a/common/src/proto/descriptor.bin b/common/src/proto/descriptor.bin index b102d933..429670b6 100644 Binary files a/common/src/proto/descriptor.bin and b/common/src/proto/descriptor.bin differ diff --git a/common/src/proto/komp_ac.tables_data.rs b/common/src/proto/komp_ac.tables_data.rs index 161d1d8b..efbb92a8 100644 --- a/common/src/proto/komp_ac.tables_data.rs +++ b/common/src/proto/komp_ac.tables_data.rs @@ -133,6 +133,15 @@ pub struct PutTableDataRequest { #[prost(int64, tag = "5")] pub expected_revision: i64, } +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PutTableDataConfirmedRequest { + #[prost(message, optional, tag = "1")] + pub update: ::core::option::Option, + #[prost(string, repeated, tag = "2")] + pub expected_affected_profiles: ::prost::alloc::vec::Vec< + ::prost::alloc::string::String, + >, +} /// Update response. #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct PutTableDataResponse { @@ -150,7 +159,7 @@ pub struct PutTableDataResponse { pub row_revision: i64, } #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct GlobalTableUpdateImpactRequest { +pub struct TableUpdateImpactRequest { #[prost(string, tag = "1")] pub profile_name: ::prost::alloc::string::String, #[prost(string, tag = "2")] @@ -159,12 +168,12 @@ pub struct GlobalTableUpdateImpactRequest { pub id: i64, } #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct GlobalTableUpdateImpactResponse { +pub struct TableUpdateImpactResponse { #[prost(string, repeated, tag = "1")] pub affected_profiles: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, } #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct ArchiveGlobalTableDataRequest { +pub struct ArchiveTableDataRequest { #[prost(string, tag = "1")] pub profile_name: ::prost::alloc::string::String, #[prost(string, tag = "2")] @@ -175,7 +184,7 @@ pub struct ArchiveGlobalTableDataRequest { pub expected_revision: i64, } #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct ArchiveGlobalTableDataResponse { +pub struct ArchiveTableDataResponse { #[prost(bool, tag = "1")] pub success: bool, #[prost(int64, tag = "2")] @@ -226,7 +235,7 @@ pub struct GetTableDataRequest { pub id: i64, } #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct GetGlobalTableDataVersionRequest { +pub struct GetTableDataVersionRequest { #[prost(string, tag = "1")] pub profile_name: ::prost::alloc::string::String, #[prost(string, tag = "2")] @@ -497,12 +506,10 @@ pub mod tables_data_client { ); self.inner.unary(req, path, codec).await } - /// Performs a PUT after the user explicitly accepted its cross-profile impact. - /// This is meaningful only for global tables; ordinary tables behave exactly - /// like PutTableData. + /// Performs a PUT after the user explicitly accepted changing a referenced version. pub async fn put_table_data_confirmed( &mut self, - request: impl tonic::IntoRequest, + request: impl tonic::IntoRequest, ) -> std::result::Result< tonic::Response, tonic::Status, @@ -529,12 +536,12 @@ pub mod tables_data_client { ); self.inner.unary(req, path, codec).await } - /// Lists the other profiles whose rows currently point at this global row. - pub async fn get_global_table_update_impact( + /// Lists profiles whose rows currently point at this row version. + pub async fn get_table_update_impact( &mut self, - request: impl tonic::IntoRequest, + request: impl tonic::IntoRequest, ) -> std::result::Result< - tonic::Response, + tonic::Response, tonic::Status, > { self.inner @@ -547,25 +554,25 @@ pub mod tables_data_client { })?; let codec = tonic_prost::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( - "/komp_ac.tables_data.TablesData/GetGlobalTableUpdateImpact", + "/komp_ac.tables_data.TablesData/GetTableUpdateImpact", ); let mut req = request.into_request(); req.extensions_mut() .insert( GrpcMethod::new( "komp_ac.tables_data.TablesData", - "GetGlobalTableUpdateImpact", + "GetTableUpdateImpact", ), ); self.inner.unary(req, path, codec).await } - /// Snapshots the current version of a global row and advances its version. + /// Snapshots the current version of a row and advances its version. /// Existing references remain immutable; new references use the new version. - pub async fn archive_global_table_data( + pub async fn archive_table_data( &mut self, - request: impl tonic::IntoRequest, + request: impl tonic::IntoRequest, ) -> std::result::Result< - tonic::Response, + tonic::Response, tonic::Status, > { self.inner @@ -578,15 +585,12 @@ pub mod tables_data_client { })?; let codec = tonic_prost::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( - "/komp_ac.tables_data.TablesData/ArchiveGlobalTableData", + "/komp_ac.tables_data.TablesData/ArchiveTableData", ); let mut req = request.into_request(); req.extensions_mut() .insert( - GrpcMethod::new( - "komp_ac.tables_data.TablesData", - "ArchiveGlobalTableData", - ), + GrpcMethod::new("komp_ac.tables_data.TablesData", "ArchiveTableData"), ); self.inner.unary(req, path, codec).await } @@ -661,10 +665,10 @@ pub mod tables_data_client { ); self.inner.unary(req, path, codec).await } - /// Fetches one exact version of a global row. - pub async fn get_global_table_data_version( + /// Fetches one exact version of a row. + pub async fn get_table_data_version( &mut self, - request: impl tonic::IntoRequest, + request: impl tonic::IntoRequest, ) -> std::result::Result< tonic::Response, tonic::Status, @@ -679,14 +683,14 @@ pub mod tables_data_client { })?; let codec = tonic_prost::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( - "/komp_ac.tables_data.TablesData/GetGlobalTableDataVersion", + "/komp_ac.tables_data.TablesData/GetTableDataVersion", ); let mut req = request.into_request(); req.extensions_mut() .insert( GrpcMethod::new( "komp_ac.tables_data.TablesData", - "GetGlobalTableDataVersion", + "GetTableDataVersion", ), ); self.inner.unary(req, path, codec).await @@ -832,31 +836,29 @@ pub mod tables_data_server { tonic::Response, tonic::Status, >; - /// Performs a PUT after the user explicitly accepted its cross-profile impact. - /// This is meaningful only for global tables; ordinary tables behave exactly - /// like PutTableData. + /// Performs a PUT after the user explicitly accepted changing a referenced version. async fn put_table_data_confirmed( &self, - request: tonic::Request, + request: tonic::Request, ) -> std::result::Result< tonic::Response, tonic::Status, >; - /// Lists the other profiles whose rows currently point at this global row. - async fn get_global_table_update_impact( + /// Lists profiles whose rows currently point at this row version. + async fn get_table_update_impact( &self, - request: tonic::Request, + request: tonic::Request, ) -> std::result::Result< - tonic::Response, + tonic::Response, tonic::Status, >; - /// Snapshots the current version of a global row and advances its version. + /// Snapshots the current version of a row and advances its version. /// Existing references remain immutable; new references use the new version. - async fn archive_global_table_data( + async fn archive_table_data( &self, - request: tonic::Request, + request: tonic::Request, ) -> std::result::Result< - tonic::Response, + tonic::Response, tonic::Status, >; /// Soft-delete a single record (sets deleted = true) if it exists and is not already deleted. @@ -892,10 +894,10 @@ pub mod tables_data_server { tonic::Response, tonic::Status, >; - /// Fetches one exact version of a global row. - async fn get_global_table_data_version( + /// Fetches one exact version of a row. + async fn get_table_data_version( &self, - request: tonic::Request, + request: tonic::Request, ) -> std::result::Result< tonic::Response, tonic::Status, @@ -1151,7 +1153,7 @@ pub mod tables_data_server { struct PutTableDataConfirmedSvc(pub Arc); impl< T: TablesData, - > tonic::server::UnaryService + > tonic::server::UnaryService for PutTableDataConfirmedSvc { type Response = super::PutTableDataResponse; type Future = BoxFuture< @@ -1160,7 +1162,7 @@ pub mod tables_data_server { >; fn call( &mut self, - request: tonic::Request, + request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { @@ -1192,30 +1194,25 @@ pub mod tables_data_server { }; Box::pin(fut) } - "/komp_ac.tables_data.TablesData/GetGlobalTableUpdateImpact" => { + "/komp_ac.tables_data.TablesData/GetTableUpdateImpact" => { #[allow(non_camel_case_types)] - struct GetGlobalTableUpdateImpactSvc(pub Arc); + struct GetTableUpdateImpactSvc(pub Arc); impl< T: TablesData, - > tonic::server::UnaryService - for GetGlobalTableUpdateImpactSvc { - type Response = super::GlobalTableUpdateImpactResponse; + > tonic::server::UnaryService + for GetTableUpdateImpactSvc { + type Response = super::TableUpdateImpactResponse; type Future = BoxFuture< tonic::Response, tonic::Status, >; fn call( &mut self, - request: tonic::Request< - super::GlobalTableUpdateImpactRequest, - >, + request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::get_global_table_update_impact( - &inner, - request, - ) + ::get_table_update_impact(&inner, request) .await }; Box::pin(fut) @@ -1227,7 +1224,7 @@ pub mod tables_data_server { let max_encoding_message_size = self.max_encoding_message_size; let inner = self.inner.clone(); let fut = async move { - let method = GetGlobalTableUpdateImpactSvc(inner); + let method = GetTableUpdateImpactSvc(inner); let codec = tonic_prost::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) .apply_compression_config( @@ -1243,29 +1240,25 @@ pub mod tables_data_server { }; Box::pin(fut) } - "/komp_ac.tables_data.TablesData/ArchiveGlobalTableData" => { + "/komp_ac.tables_data.TablesData/ArchiveTableData" => { #[allow(non_camel_case_types)] - struct ArchiveGlobalTableDataSvc(pub Arc); + struct ArchiveTableDataSvc(pub Arc); impl< T: TablesData, - > tonic::server::UnaryService - for ArchiveGlobalTableDataSvc { - type Response = super::ArchiveGlobalTableDataResponse; + > tonic::server::UnaryService + for ArchiveTableDataSvc { + type Response = super::ArchiveTableDataResponse; type Future = BoxFuture< tonic::Response, tonic::Status, >; fn call( &mut self, - request: tonic::Request, + request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::archive_global_table_data( - &inner, - request, - ) - .await + ::archive_table_data(&inner, request).await }; Box::pin(fut) } @@ -1276,7 +1269,7 @@ pub mod tables_data_server { let max_encoding_message_size = self.max_encoding_message_size; let inner = self.inner.clone(); let fut = async move { - let method = ArchiveGlobalTableDataSvc(inner); + let method = ArchiveTableDataSvc(inner); let codec = tonic_prost::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) .apply_compression_config( @@ -1382,14 +1375,13 @@ pub mod tables_data_server { }; Box::pin(fut) } - "/komp_ac.tables_data.TablesData/GetGlobalTableDataVersion" => { + "/komp_ac.tables_data.TablesData/GetTableDataVersion" => { #[allow(non_camel_case_types)] - struct GetGlobalTableDataVersionSvc(pub Arc); + struct GetTableDataVersionSvc(pub Arc); impl< T: TablesData, - > tonic::server::UnaryService< - super::GetGlobalTableDataVersionRequest, - > for GetGlobalTableDataVersionSvc { + > tonic::server::UnaryService + for GetTableDataVersionSvc { type Response = super::GetTableDataResponse; type Future = BoxFuture< tonic::Response, @@ -1397,16 +1389,11 @@ pub mod tables_data_server { >; fn call( &mut self, - request: tonic::Request< - super::GetGlobalTableDataVersionRequest, - >, + request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::get_global_table_data_version( - &inner, - request, - ) + ::get_table_data_version(&inner, request) .await }; Box::pin(fut) @@ -1418,7 +1405,7 @@ pub mod tables_data_server { let max_encoding_message_size = self.max_encoding_message_size; let inner = self.inner.clone(); let fut = async move { - let method = GetGlobalTableDataVersionSvc(inner); + let method = GetTableDataVersionSvc(inner); let codec = tonic_prost::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) .apply_compression_config( diff --git a/common/src/system_column.rs b/common/src/system_column.rs index c368e163..3eb6bd37 100644 --- a/common/src/system_column.rs +++ b/common/src/system_column.rs @@ -44,6 +44,8 @@ pub const TRAILING_SYSTEM_COLUMNS: [SystemColumn; 1] = [SystemColumn { /// profile is known rather than spelled out here. pub const ACCOUNT_REFERENCE_COLUMN: &str = "account_id"; pub const ACCOUNT_API_COLUMN: &str = "account"; +/// Internal version of a managed row. It is never exposed as editable data. +pub const ROW_VERSION_COLUMN: &str = "version"; /// The longest name any system column carries physically. /// @@ -69,7 +71,14 @@ const fn longest_system_column_name() -> usize { longest } - let longest = longest_of(&LEADING_SYSTEM_COLUMNS, ACCOUNT_REFERENCE_COLUMN.len()); + let longest = longest_of( + &LEADING_SYSTEM_COLUMNS, + if ACCOUNT_REFERENCE_COLUMN.len() > ROW_VERSION_COLUMN.len() { + ACCOUNT_REFERENCE_COLUMN.len() + } else { + ROW_VERSION_COLUMN.len() + }, + ); longest_of(&TRAILING_SYSTEM_COLUMNS, longest) } @@ -83,6 +92,7 @@ pub fn system_column_names() -> impl Iterator { .chain(TRAILING_SYSTEM_COLUMNS.iter()) .map(|column| column.name) .chain(std::iter::once(ACCOUNT_REFERENCE_COLUMN)) + .chain(std::iter::once(ROW_VERSION_COLUMN)) } /// Whether `name` belongs to the system column vocabulary and is safe to show @@ -145,7 +155,7 @@ mod tests { fn the_name_list_reads_as_a_sentence_fragment() { assert_eq!( system_column_name_list(), - "'id', 'deleted', 'row_revision', 'created_at', 'account_id', 'account'" + "'id', 'deleted', 'row_revision', 'created_at', 'account_id', 'version', 'account'" ); } } diff --git a/server b/server index ac0c12a7..d8130859 160000 --- a/server +++ b/server @@ -1 +1 @@ -Subproject commit ac0c12a7ac86887fbaaefb2915d0b08ddd4493bb +Subproject commit d81308592a92cfbbd53e1b0cbc2455d64deaaf7c