Skip to content

Commit fe5658e

Browse files
nipunn1313Convex, Inc.
authored and
Convex, Inc.
committed
Fix delete_component to work with hidden tables (#35246)
Adds a test that makes sure it deletes hidden tables. confirmed that the test previously failed, but now passes. GitOrigin-RevId: 85707863a1c27b6523c669d14f86d2abf5a4598d
1 parent 8adc16a commit fe5658e

File tree

10 files changed

+82
-24
lines changed

10 files changed

+82
-24
lines changed

crates/application/src/exports/tests.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -567,7 +567,7 @@ async fn test_export_with_table_delete(rt: TestRuntime) -> anyhow::Result<()> {
567567
db.commit(tx).await?;
568568
let mut tx = db.begin(Identity::system()).await?;
569569
TableModel::new(&mut tx)
570-
.delete_table(TableNamespace::test_user(), "table_0".parse()?)
570+
.delete_active_table(TableNamespace::test_user(), "table_0".parse()?)
571571
.await?;
572572
db.commit(tx).await?;
573573

crates/application/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2518,7 +2518,7 @@ impl<RT: Runtime> Application<RT> {
25182518
let mut table_model = TableModel::new(&mut tx);
25192519
count += table_model.must_count(table_namespace, &table_name).await?;
25202520
table_model
2521-
.delete_table(table_namespace, table_name)
2521+
.delete_active_table(table_namespace, table_name)
25222522
.await?;
25232523
}
25242524
self.commit(tx, "delete_tables").await?;

crates/application/src/snapshot_import/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -895,7 +895,9 @@ async fn finalize_import<RT: Runtime>(
895895
.count(namespace, &table_name)
896896
.await?
897897
.unwrap_or(0);
898-
table_model.delete_table(namespace, table_name).await?;
898+
table_model
899+
.delete_active_table(namespace, table_name)
900+
.await?;
899901
}
900902
schema_constraints.validate(tx).await?;
901903
let mut table_model = TableModel::new(tx);

crates/application/src/system_table_cleanup/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ impl<RT: Runtime> SystemTableCleanupWorker<RT> {
237237
{component_id:?}"
238238
);
239239
table_model
240-
.delete_table(*namespace, table_name.clone())
240+
.delete_active_table(*namespace, table_name.clone())
241241
.await?;
242242
}
243243
}

crates/application/src/tests/components.rs

+54-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1+
use std::collections::BTreeSet;
2+
13
use common::{
2-
bootstrap_model::components::ComponentState,
4+
bootstrap_model::{
5+
components::ComponentState,
6+
tables::TableState,
7+
},
38
components::{
49
CanonicalizedComponentFunctionPath,
510
ComponentId,
@@ -489,3 +494,51 @@ async fn test_infinite_loop_in_component(rt: TestRuntime) -> anyhow::Result<()>
489494
assert_contains(&err.error, "Cross component call depth limit exceeded");
490495
Ok(())
491496
}
497+
498+
#[convex_macro::test_runtime]
499+
async fn test_delete_component_with_hidden_tables(rt: TestRuntime) -> anyhow::Result<()> {
500+
let application = Application::new_for_tests(&rt).await?;
501+
application.load_component_tests_modules("mounted").await?;
502+
503+
// insert table for import
504+
let mut tx = application.begin(Identity::system()).await?;
505+
let (_, component_id) =
506+
BootstrapComponentsModel::new(&mut tx).must_component_path_to_ids(&component_path())?;
507+
let mut table_model = TableModel::new(&mut tx);
508+
let hidden_table_name = "hiddentable".parse()?;
509+
let tablet_id_and_table_number = table_model
510+
.insert_table_for_import(
511+
TableNamespace::from(component_id),
512+
&hidden_table_name,
513+
None,
514+
&BTreeSet::new(),
515+
)
516+
.await?;
517+
application.commit_test(tx).await?;
518+
519+
// Ensure the hidden table exists
520+
let mut tx = application.begin(Identity::system()).await?;
521+
let mut table_model = TableModel::new(&mut tx);
522+
let table_metadata = table_model
523+
.get_table_metadata(tablet_id_and_table_number.tablet_id)
524+
.await?
525+
.into_value();
526+
assert_eq!(table_metadata.namespace, TableNamespace::from(component_id));
527+
assert_eq!(table_metadata.state, TableState::Hidden);
528+
529+
// Delete the component
530+
let component_id = unmount_component(&application).await?;
531+
application
532+
.delete_component(&Identity::system(), component_id)
533+
.await?;
534+
535+
// Ensure the hidden table is also deleted
536+
let mut tx = application.begin(Identity::system()).await?;
537+
let mut table_model = TableModel::new(&mut tx);
538+
let table_metadata = table_model
539+
.get_table_metadata(tablet_id_and_table_number.tablet_id)
540+
.await?
541+
.into_value();
542+
assert_eq!(table_metadata.state, TableState::Deleting);
543+
Ok(())
544+
}

crates/database/src/bootstrap_model/table.rs

+14-9
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ impl<'a, RT: Runtime> TableModel<'a, RT> {
194194
self.tx.table_mapping().iter_active_user_tables().count()
195195
}
196196

197-
pub async fn delete_table(
197+
pub async fn delete_active_table(
198198
&mut self,
199199
namespace: TableNamespace,
200200
table_name: TableName,
@@ -223,6 +223,11 @@ impl<'a, RT: Runtime> TableModel<'a, RT> {
223223
.await
224224
}
225225

226+
pub async fn delete_table(&mut self, tablet_id: TabletId) -> anyhow::Result<()> {
227+
self.delete_table_by_id_bypassing_schema_enforcement(tablet_id)
228+
.await
229+
}
230+
226231
async fn delete_table_by_id_bypassing_schema_enforcement(
227232
&mut self,
228233
tablet_id: TabletId,
@@ -384,7 +389,7 @@ impl<'a, RT: Runtime> TableModel<'a, RT> {
384389
anyhow::bail!(ErrorMetadata::bad_request(
385390
"TableConflict",
386391
format!(
387-
"New table {table} has IDs that conflict with existing internal table. \
392+
"New table `{table}` has IDs that conflict with existing internal table. \
388393
Consider importing this table without `_id` fields or import into a new \
389394
deployment."
390395
)
@@ -610,7 +615,7 @@ mod tests {
610615
let mut tx = new_tx(rt).await?;
611616
let mut model = TableModel::new(&mut tx);
612617
model
613-
.delete_table(TableNamespace::test_user(), TableName::from_str("missing")?)
618+
.delete_active_table(TableNamespace::test_user(), TableName::from_str("missing")?)
614619
.await
615620
}
616621

@@ -635,7 +640,7 @@ mod tests {
635640
.insert_table_metadata(TableNamespace::test_user(), &table_name)
636641
.await?;
637642
model
638-
.delete_table(TableNamespace::test_user(), table_name.clone())
643+
.delete_active_table(TableNamespace::test_user(), table_name.clone())
639644
.await?;
640645
assert!(!model.table_exists(TableNamespace::test_user(), &table_name));
641646
Ok(())
@@ -655,7 +660,7 @@ mod tests {
655660
.insert_table_metadata(TableNamespace::test_user(), &table_name)
656661
.await?;
657662
model
658-
.delete_table(TableNamespace::test_user(), table_name.clone())
663+
.delete_active_table(TableNamespace::test_user(), table_name.clone())
659664
.await?;
660665
assert!(!model.table_exists(TableNamespace::test_user(), &table_name));
661666
Ok(())
@@ -675,7 +680,7 @@ mod tests {
675680
.insert_table_metadata(TableNamespace::test_user(), &table_name)
676681
.await?;
677682
model
678-
.delete_table(TableNamespace::test_user(), table_name.clone())
683+
.delete_active_table(TableNamespace::test_user(), table_name.clone())
679684
.await?;
680685
assert!(!model.table_exists(TableNamespace::test_user(), &table_name));
681686
Ok(())
@@ -693,7 +698,7 @@ mod tests {
693698
.insert_table_metadata(TableNamespace::test_user(), &table_name)
694699
.await?;
695700
let result = model
696-
.delete_table(TableNamespace::test_user(), table_name.clone())
701+
.delete_active_table(TableNamespace::test_user(), table_name.clone())
697702
.await;
698703
let error = result.unwrap_err();
699704
assert!(
@@ -723,7 +728,7 @@ mod tests {
723728
.insert_table_metadata(TableNamespace::test_user(), &table_name)
724729
.await?;
725730
let result = model
726-
.delete_table(TableNamespace::test_user(), table_name.clone())
731+
.delete_active_table(TableNamespace::test_user(), table_name.clone())
727732
.await;
728733
let error = result.unwrap_err();
729734
assert!(
@@ -754,7 +759,7 @@ mod tests {
754759
.insert_table_metadata(TableNamespace::test_user(), &table_name)
755760
.await?;
756761
model
757-
.delete_table(TableNamespace::test_user(), table_name.clone())
762+
.delete_active_table(TableNamespace::test_user(), table_name.clone())
758763
.await?;
759764

760765
let mut schema_model = SchemaModel::new_root_for_test(&mut tx);

crates/database/src/tests/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1569,7 +1569,7 @@ async fn test_interrupted_import_then_delete_table(rt: TestRuntime) -> anyhow::R
15691569
.is_none());
15701570
// Delete the active table.
15711571
TableModel::new(&mut tx)
1572-
.delete_table(TableNamespace::test_user(), table_name.clone())
1572+
.delete_active_table(TableNamespace::test_user(), table_name.clone())
15731573
.await?;
15741574
database.commit(tx).await?;
15751575

crates/database/src/tests/streaming_export_tests.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ async fn document_deltas_should_ignore_rows_from_deleted_tables(
202202
let mut tx = db.begin(Identity::system()).await?;
203203
let mut model = TableModel::new(&mut tx);
204204
model
205-
.delete_table(TableNamespace::test_user(), "table".parse()?)
205+
.delete_active_table(TableNamespace::test_user(), "table".parse()?)
206206
.await?;
207207
db.commit(tx).await?;
208208

@@ -241,7 +241,7 @@ async fn document_deltas_should_not_ignore_rows_from_tables_that_were_not_delete
241241
let mut tx = db.begin(Identity::system()).await?;
242242
let mut model = TableModel::new(&mut tx);
243243
model
244-
.delete_table(TableNamespace::test_user(), "table2".parse()?)
244+
.delete_active_table(TableNamespace::test_user(), "table2".parse()?)
245245
.await?;
246246
let table_mapping = tx.table_mapping().clone();
247247
let ts_latest = db.commit(tx).await?;

crates/model/src/components/config.rs

+4-6
Original file line numberDiff line numberDiff line change
@@ -724,15 +724,13 @@ impl<'a, RT: Runtime> ComponentConfigModel<'a, RT> {
724724
// tables defined in the schema cannot be deleted, so we delete the _schemas
725725
// table first to remove that restriction
726726
TableModel::new(self.tx)
727-
.delete_table(namespace, SCHEMAS_TABLE.clone())
727+
.delete_active_table(namespace, SCHEMAS_TABLE.clone())
728728
.await?;
729729

730-
// then delete all tables, including system tables
730+
// then delete all tables, including system tables and hidden tables
731731
let namespaced_table_mapping = self.tx.table_mapping().namespace(namespace);
732-
for (_, _, table_name) in namespaced_table_mapping.iter() {
733-
TableModel::new(self.tx)
734-
.delete_table(namespace, table_name.clone())
735-
.await?;
732+
for (tablet_id, ..) in namespaced_table_mapping.iter() {
733+
TableModel::new(self.tx).delete_table(tablet_id).await?;
736734
}
737735
}
738736

crates/model/src/migrations.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ impl<RT: Runtime> MigrationWorker<RT> {
282282
.expect("Invalid built-in virtual_tables table");
283283
let mut tx = self.db.begin_system().await?;
284284
TableModel::new(&mut tx)
285-
.delete_table(TableNamespace::Global, virtual_tables_table)
285+
.delete_active_table(TableNamespace::Global, virtual_tables_table)
286286
.await?;
287287
self.db
288288
.commit_with_write_source(tx, "migration_111")

0 commit comments

Comments
 (0)