From 52d290a6aba454ffbdf915b12c73221fff55117a Mon Sep 17 00:00:00 2001 From: Lan Lou Date: Fri, 8 Nov 2024 12:02:12 -0500 Subject: [PATCH] Fix update_stats and add all tests --- optd-persistent/init.db | Bin 147456 -> 0 bytes optd-persistent/src/cost_model/interface.rs | 14 +- optd-persistent/src/cost_model/orm.rs | 272 +++++++++++++++----- optd-persistent/src/db/init.db | Bin 147456 -> 147456 bytes 4 files changed, 221 insertions(+), 65 deletions(-) delete mode 100644 optd-persistent/init.db diff --git a/optd-persistent/init.db b/optd-persistent/init.db deleted file mode 100644 index fe10513887383e8e2132b98465aa2dc75a4b69bb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 147456 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 EpochId? + Existed(i32), + New(String, String), +} + +#[derive(Clone)] pub struct Stat { pub stat_type: i32, pub stat_value: Json, @@ -72,7 +80,11 @@ pub trait CostModelStorageLayer { epoch_id: Self::EpochId, ) -> StorageResult<()>; - async fn update_stats(&self, stat: Stat, epoch_id: Self::EpochId) -> StorageResult<()>; + async fn update_stats( + &mut self, + stat: Stat, + epoch_option: EpochOption, + ) -> StorageResult>; async fn store_cost( &self, diff --git a/optd-persistent/src/cost_model/orm.rs b/optd-persistent/src/cost_model/orm.rs index c43c809..0c47117 100644 --- a/optd-persistent/src/cost_model/orm.rs +++ b/optd-persistent/src/cost_model/orm.rs @@ -13,7 +13,7 @@ use sea_orm::{ }; use super::catalog::mock_catalog::{self, MockCatalog}; -use super::interface::{CatalogSource, Stat}; +use super::interface::{CatalogSource, EpochOption, Stat}; impl BackendManager { fn get_description_from_attr_ids( @@ -154,40 +154,47 @@ impl CostModelStorageLayer for BackendManager { } } - // **IMPORTANT**: It is the caller's responsibility to ensure that the updated stat is not the same with the last stored stat if - // if it is already exists. - async fn update_stats(&self, stat: Stat, epoch_id: Self::EpochId) -> StorageResult<()> { - // let transaction = self.db.begin().await?; + /* Update the statistics in the database. + * The statistic can be newly inserted or updated. If the statistic value + * is the same as the latest existing one, the update will be ignored, and + * the return value will be None. + * If `epoch_option` is `EpochOption::Existed(epoch_id)`, the new statistic + * will be associated with the given epoch_id. If `epoch_option` is + * `EpochOption::New(source, data)`, a new epoch will be created with the + * given source and data, and the new statistic will be associated with the + * new epoch. And return the new epoch_id. + * If the statistic value is the same as the latest existing one, this function + * won't create a new epoch. + * + * For batch updates, if the caller can directly call this function with + * New epoch option at the first time, and if the epoch_id is returned, the + * caller can use the returned epoch_id for the rest of the updates. + * But if the epoch_id is not returned, the caller should continue using + * the New epoch option for the next statistic update. + */ + async fn update_stats( + &mut self, + stat: Stat, + epoch_option: EpochOption, + ) -> StorageResult> { + let transaction = self.db.begin().await?; // 0. Check if the stat already exists. If exists, get stat_id, else insert into statistic table. let stat_id = match stat.table_id { Some(table_id) => { // TODO(lanlou): only select needed fields let res = Statistic::find() .filter(statistic::Column::TableId.eq(table_id)) - .filter(statistic::Column::VariantTag.eq(stat.stat_type)) - /* - TODO(FIX_ME, lanlou): Do we need the following filter? - I am really not sure although I add the top comment... - Since we already increase the epoch, so we should update the stat anyway. - (In theory, we can increase the epoch without updating the stat, but it is not - a straightforward design, and the epoch table will be very large.) - But it will increase the overhead, since the caller will need to make another - query to check if the stat is the same with the last one. We cannot put everything - in one query. - Let us assume we should update the stat anyway for now. - */ - // .inner_join(versioned_statistic::Entity) - // .select_also(versioned_statistic::Entity) - // .order_by_desc(versioned_statistic::Column::EpochId) - .one(&self.db) + .inner_join(versioned_statistic::Entity) + .select_also(versioned_statistic::Entity) + .order_by_desc(versioned_statistic::Column::EpochId) + .one(&transaction) .await?; match res { Some(stat_data) => { - // if stat_data.1.unwrap().statistic_value == stat.stat_value { - // return Ok(()); - // } - // stat_data.0.id - stat_data.id + if stat_data.1.unwrap().statistic_value == stat.stat_value { + return Ok(None); + } + stat_data.0.id } None => { let new_stat = statistic::ActiveModel { @@ -201,7 +208,7 @@ impl CostModelStorageLayer for BackendManager { description: sea_orm::ActiveValue::Set("".to_string()), ..Default::default() }; - let res = Statistic::insert(new_stat).exec(&self.db).await; + let res = Statistic::insert(new_stat).exec(&transaction).await; match res { Ok(insert_res) => insert_res.last_insert_id, Err(_) => { @@ -221,18 +228,17 @@ impl CostModelStorageLayer for BackendManager { .filter(statistic::Column::NumberOfAttributes.eq(stat.attr_ids.len() as i32)) .filter(statistic::Column::Description.eq(description.clone())) .filter(statistic::Column::VariantTag.eq(stat.stat_type)) - // .inner_join(versioned_statistic::Entity) - // .select_also(versioned_statistic::Entity) - // .order_by_desc(versioned_statistic::Column::EpochId) - .one(&self.db) + .inner_join(versioned_statistic::Entity) + .select_also(versioned_statistic::Entity) + .order_by_desc(versioned_statistic::Column::EpochId) + .one(&transaction) .await?; match res { Some(stat_data) => { - // if stat_data.1.unwrap().statistic_value == stat.stat_value { - // return Ok(()); - // } - // stat_data.0.id - stat_data.id + if stat_data.1.unwrap().statistic_value == stat.stat_value { + return Ok(None); + } + stat_data.0.id } None => { let new_stat = statistic::ActiveModel { @@ -246,23 +252,17 @@ impl CostModelStorageLayer for BackendManager { ..Default::default() }; // TODO(lanlou): we should not clone here maybe... - let insert_res = Statistic::insert(new_stat.clone()).exec(&self.db).await?; + let insert_res = Statistic::insert(new_stat.clone()) + .exec(&transaction) + .await?; for attr_id in stat.attr_ids { let new_junction = statistic_to_attribute_junction::ActiveModel { statistic_id: sea_orm::ActiveValue::Set(insert_res.last_insert_id), attribute_id: sea_orm::ActiveValue::Set(attr_id), }; let res = StatisticToAttributeJunction::insert(new_junction) - .exec(&self.db) - .await; - if res.is_err() { - let _ = new_stat.delete(&self.db).await; - return Err(BackendError::Database(DbErr::Exec( - RuntimeErr::Internal( - "Failed to insert into statistic_to_attribute_junction table".to_string(), - ), - ))); - } + .exec(&transaction) + .await?; } insert_res.last_insert_id } @@ -270,13 +270,30 @@ impl CostModelStorageLayer for BackendManager { } }; // 1. Insert into attr_stats and related junction tables. + let mut insert_new_epoch = false; + let epoch_id = match epoch_option { + EpochOption::Existed(e) => e, + EpochOption::New(source, data) => { + insert_new_epoch = true; + let new_event = event::ActiveModel { + source_variant: sea_orm::ActiveValue::Set(source), + timestamp: sea_orm::ActiveValue::Set(Utc::now()), + data: sea_orm::ActiveValue::Set(sea_orm::JsonValue::String(data)), + ..Default::default() + }; + let insert_res = Event::insert(new_event).exec(&transaction).await?; + insert_res.last_insert_id + } + }; let new_stats = versioned_statistic::ActiveModel { epoch_id: sea_orm::ActiveValue::Set(epoch_id), statistic_id: sea_orm::ActiveValue::Set(stat_id), statistic_value: sea_orm::ActiveValue::Set(stat.stat_value), ..Default::default() }; - let _ = VersionedStatistic::insert(new_stats).exec(&self.db).await; + let _ = VersionedStatistic::insert(new_stats) + .exec(&transaction) + .await?; // 2. Invalidate all the related cost. let _ = plan_cost::Entity::update_many() @@ -297,11 +314,17 @@ impl CostModelStorageLayer for BackendManager { .to_owned(), ), ) - .exec(&self.db) - .await; + .exec(&transaction) + .await?; - // transaction.commit().await?; - Ok(()) + // TODO(lanlou): consider the update conflict for latest_epoch_id in multiple threads + if insert_new_epoch { + self.latest_epoch_id + .store(epoch_id as usize, std::sync::atomic::Ordering::Relaxed); + } + + transaction.commit().await?; + Ok(Some(epoch_id)) } async fn store_expr_stats_mappings( @@ -455,10 +478,11 @@ impl CostModelStorageLayer for BackendManager { #[cfg(test)] mod tests { - use crate::cost_model::interface::StatType; + use crate::cost_model::interface::{EpochOption, StatType}; use crate::{cost_model::interface::Stat, migrate, CostModelStorageLayer}; use crate::{get_sqlite_url, TEST_DATABASE_FILE}; use sea_orm::sqlx::database; + use sea_orm::sqlx::types::chrono::Utc; use sea_orm::Statement; use sea_orm::{ ColumnTrait, ConnectionTrait, Database, DbBackend, EntityTrait, ModelTrait, QueryFilter, @@ -497,8 +521,7 @@ mod tests { async fn copy_init_db(db_file: &str) -> String { let _ = std::fs::copy(TEST_DATABASE_FILE.clone(), db_file); - let database_url = get_sqlite_url(db_file); - database_url + get_sqlite_url(db_file) } #[tokio::test] @@ -566,7 +589,9 @@ mod tests { table_id: None, name: "countattr1".to_string(), }; - let res = backend_manager.update_stats(stat, epoch_id1).await; + let res = backend_manager + .update_stats(stat, EpochOption::Existed(epoch_id1)) + .await; assert!(res.is_ok()); let stat_res = Statistic::find() .filter(statistic::Column::Name.eq("countattr1")) @@ -636,7 +661,9 @@ mod tests { table_id: None, name: "countattr1".to_string(), }; - let res = backend_manager.update_stats(stat2, epoch_id2).await; + let res = backend_manager + .update_stats(stat2, EpochOption::Existed(epoch_id2)) + .await; assert!(res.is_ok()); // 2.3 Check statistic table let stat_res = Statistic::find() @@ -645,9 +672,7 @@ mod tests { .await .unwrap(); assert_eq!(stat_res.len(), 1); - assert_eq!(stat_res[0].number_of_attributes, 1); assert_eq!(stat_res[0].description, "1".to_string()); - assert_eq!(stat_res[0].variant_tag, StatType::Count as i32); // 2.4 Check statistic_to_attribute_junction table let stat_attr_res = StatisticToAttributeJunction::find() .filter(statistic_to_attribute_junction::Column::StatisticId.eq(stat_res[0].id)) @@ -681,11 +706,121 @@ mod tests { assert_eq!(cost_res[0].epoch_id, epoch_id1); assert!(!cost_res[0].is_valid); + // 3. Update existed stat with the same value + let epoch_num = Event::find().all(&backend_manager.db).await.unwrap().len(); + let stat3 = Stat { + stat_type: StatType::Count as i32, + stat_value: json!(200), + attr_ids: vec![1], + table_id: None, + name: "CountAttr1".to_string(), + }; + let res = backend_manager + .update_stats( + stat3, + EpochOption::New("source".to_string(), "data".to_string()), + ) + .await; + assert!(res.is_ok()); + assert!(res.unwrap().is_none()); + let epoch_num2 = Event::find().all(&backend_manager.db).await.unwrap().len(); + assert_eq!(epoch_num, epoch_num2); + remove_db_file(DATABASE_FILE); } #[tokio::test] - async fn test_update_table_stats() {} + async fn test_update_table_stats() { + // Simulate batch updates, first insert an existed same stat with none epoch_id, + // then insert some non-existed or different stats with New epoch_option. + const DATABASE_FILE: &str = "test_update_table_stats.db"; + let database_url = copy_init_db(DATABASE_FILE).await; + let mut binding = super::BackendManager::new(Some(&database_url)).await; + let backend_manager = binding.as_mut().unwrap(); + + let table_inserted_res = TableMetadata::insert(table_metadata::ActiveModel { + name: sea_orm::ActiveValue::Set("Table2".to_string()), + namespace_id: sea_orm::ActiveValue::Set(1), + creation_time: sea_orm::ActiveValue::Set(Utc::now()), + ..Default::default() + }) + .exec(&backend_manager.db) + .await + .unwrap(); + + let statistics: Vec = vec![ + Stat { + stat_type: StatType::Count as i32, + stat_value: json!(0), + attr_ids: vec![], + table_id: Some(1), + name: "row_count".to_string(), + }, + Stat { + stat_type: StatType::Count as i32, + stat_value: json!(20), + attr_ids: vec![], + table_id: Some(1), + name: "row_count".to_string(), + }, + Stat { + stat_type: StatType::Count as i32, + stat_value: json!(100), + attr_ids: vec![], + table_id: Some(table_inserted_res.last_insert_id), + name: "Table2Count1".to_string(), + }, + ]; + + let mut epoch_id: Option = None; + for stat in statistics { + match epoch_id { + Some(e) => { + let res = backend_manager + .update_stats(stat.clone(), EpochOption::Existed(e)) + .await; + assert!(res.is_ok()); + assert!(stat.name == "Table2Count1"); + let res = res.unwrap(); + assert!(res.is_some()); + assert!(res.unwrap() == e); + let stat_res = Statistic::find() + .filter(statistic::Column::Name.eq(stat.name.clone())) + .all(&backend_manager.db) + .await + .unwrap(); + assert_eq!(stat_res.len(), 1); + let versioned_stat_res = VersionedStatistic::find() + .filter(versioned_statistic::Column::StatisticId.eq(stat_res[0].id)) + .all(&backend_manager.db) + .await + .unwrap(); + assert_eq!(versioned_stat_res.len(), 1); + assert_eq!(versioned_stat_res[0].statistic_value, stat.stat_value); + assert_eq!(versioned_stat_res[0].epoch_id, e); + } + None => { + let res = backend_manager + .update_stats( + stat.clone(), + EpochOption::New("source".to_string(), "data".to_string()), + ) + .await; + assert!(res.is_ok()); + if stat.stat_value == json!(0) { + assert!(res.unwrap().is_none()); + } else { + assert!(stat.stat_value == json!(20)); + let res = res.unwrap(); + assert!(res.is_some()); + epoch_id = Some(res.unwrap()); + } + } + } + } + + remove_db_file(DATABASE_FILE); + } #[tokio::test] async fn test_store_cost() { @@ -699,7 +834,7 @@ mod tests { .unwrap(); let physical_expression_id = 1; let cost = 42; - let res = backend_manager + backend_manager .store_cost(physical_expression_id, cost, epoch_id) .await .unwrap(); @@ -816,7 +951,10 @@ mod tests { table_id: Some(table_id), name: "row_count".to_string(), }; - backend_manager.update_stats(stat, epoch_id2).await.unwrap(); + backend_manager + .update_stats(stat, EpochOption::Existed(epoch_id2)) + .await + .unwrap(); // Get updated stats let res = backend_manager @@ -873,7 +1011,10 @@ mod tests { table_id: None, name: "cardinality".to_string(), }; - backend_manager.update_stats(stat, epoch_id2).await.unwrap(); + backend_manager + .update_stats(stat, EpochOption::Existed(epoch_id2)) + .await + .unwrap(); // Get updated stats let res = backend_manager @@ -930,7 +1071,10 @@ mod tests { table_id: None, name: "cardinality".to_string(), }; - backend_manager.update_stats(stat, epoch_id2).await.unwrap(); + backend_manager + .update_stats(stat, EpochOption::Existed(epoch_id2)) + .await + .unwrap(); // Get updated stats let res = backend_manager diff --git a/optd-persistent/src/db/init.db b/optd-persistent/src/db/init.db index 1fc7eb9a38c4ba11b8be7fac114e62aecf285964..d88b92d67fbd7019ce2728841a6ce9ee457f8357 100644 GIT binary patch delta 485 zcmZo@;B08%oFFB{oW;PvpaR5Dz!)`A$B2nJYh%JKX=Xj=1(R)MG}wW(wkUJxWJek4 z$+va5*wTRl8X}Vq%E<5(6y+DB7L`UK_Yr!?(sumfprQRdLejxy4d zZ|iWer2_>tL?$1Uk>M#Q$}dPQDyaksP2Mk~&H<#ggqVX_CJX3FPWIR3;snX42r|2~ zOpeo)WdW8F_)6(FX)lr;mpR7p+?O#90zfLx)?$sEcudA6PkMAdvbIk4;j zIaQFXEgN$s%VYB$QE(qK25vNC6aMD)PkQ)B_TZSp@kPKfqKc~!9XmGYV(?V60t zfh?2n=_`R{Hz~Z9=Ql96GPVRFJriSNBSYiOzwAGv3Yr-iZvGYjksn#5iGij0=3n_O z>yZV`O-(GP_sTPBAq$$Bnix+%CeNtKhh&kdx%u{=@{AYaQ3Q>Rw_mSk)I0zHw&#;3