From fe15d74407826efa3d72c7364747899abde4d87d Mon Sep 17 00:00:00 2001 From: mbutrovich Date: Mon, 25 Jun 2018 15:25:31 -0400 Subject: [PATCH] Cherry-pick GC fixes from #1349, third part. Compiles. Brought in tests, need to verify. --- test/concurrency/testing_transaction_util.cpp | 96 ++ test/gc/garbage_collection_test.cpp | 10 +- test/gc/transaction_level_gc_manager_test.cpp | 1029 +++++++++++++++-- .../concurrency/testing_transaction_util.h | 17 + 4 files changed, 1054 insertions(+), 98 deletions(-) diff --git a/test/concurrency/testing_transaction_util.cpp b/test/concurrency/testing_transaction_util.cpp index ca119c0b563..75a130bf7db 100644 --- a/test/concurrency/testing_transaction_util.cpp +++ b/test/concurrency/testing_transaction_util.cpp @@ -235,6 +235,24 @@ storage::DataTable *TestingTransactionUtil::CreateTable( return table; } +void TestingTransactionUtil::AddSecondaryIndex(storage::DataTable *table) { + // Create unique index on the value column + std::vector key_attrs = {1}; + auto tuple_schema = table->GetSchema(); + bool unique = false; + auto key_schema = catalog::Schema::CopySchema(tuple_schema, key_attrs); + key_schema->SetIndexedColumns(key_attrs); + auto index_metadata2 = new index::IndexMetadata( + "unique_btree_index", 1235, TEST_TABLE_OID, CATALOG_DATABASE_OID, + IndexType::BWTREE, IndexConstraintType::UNIQUE, tuple_schema, key_schema, + key_attrs, unique); + + std::shared_ptr secondary_key_index( + index::IndexFactory::GetIndex(index_metadata2)); + + table->AddIndex(secondary_key_index); +} + std::unique_ptr TestingTransactionUtil::MakeProjectInfoFromTuple(const storage::Tuple *tuple) { TargetList target_list; @@ -479,5 +497,83 @@ bool TestingTransactionUtil::ExecuteScan( } return true; } + +ResultType TestingTransactionUtil::UpdateTuple(storage::DataTable *table, + const int key) { + srand(15721); + + auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance(); + TransactionScheduler scheduler(1, table, &txn_manager); + scheduler.Txn(0).Update(key, rand() % 15721); + scheduler.Txn(0).Commit(); + scheduler.Run(); + + return scheduler.schedules[0].txn_result; +} + +ResultType TestingTransactionUtil::InsertTuple(storage::DataTable *table, + const int key) { + srand(15721); + + auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance(); + TransactionScheduler scheduler(1, table, &txn_manager); + scheduler.Txn(0).Insert(key, rand() % 15721); + scheduler.Txn(0).Commit(); + scheduler.Run(); + + return scheduler.schedules[0].txn_result; +} + +ResultType TestingTransactionUtil::BulkInsertTuples(storage::DataTable *table, + const size_t num_tuples) { + auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance(); + TransactionScheduler scheduler(1, table, &txn_manager); + for (size_t i = 0; i < num_tuples; i++) { + scheduler.Txn(0).Insert(i, i); + } + scheduler.Txn(0).Commit(); + scheduler.Run(); + + return scheduler.schedules[0].txn_result; +} + +ResultType TestingTransactionUtil::BulkDeleteTuples(storage::DataTable *table, + const size_t num_tuples) { + auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance(); + TransactionScheduler scheduler(1, table, &txn_manager); + for (size_t i = 0; i < num_tuples; i++) { + scheduler.Txn(0).Delete(i, false); + } + scheduler.Txn(0).Commit(); + scheduler.Run(); + + return scheduler.schedules[0].txn_result; +} + +ResultType TestingTransactionUtil::DeleteTuple(storage::DataTable *table, + const int key) { + auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance(); + TransactionScheduler scheduler(1, table, &txn_manager); + scheduler.Txn(0).Delete(key); + scheduler.Txn(0).Commit(); + scheduler.Run(); + + return scheduler.schedules[0].txn_result; } + +ResultType TestingTransactionUtil::SelectTuple(storage::DataTable *table, + const int key, + std::vector &results) { + auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance(); + TransactionScheduler scheduler(1, table, &txn_manager); + scheduler.Txn(0).Read(key); + scheduler.Txn(0).Commit(); + scheduler.Run(); + + results = scheduler.schedules[0].results; + + return scheduler.schedules[0].txn_result; } + +} // namespace test +} // namespace peloton diff --git a/test/gc/garbage_collection_test.cpp b/test/gc/garbage_collection_test.cpp index bc7c6b1061b..95cb292b8e3 100644 --- a/test/gc/garbage_collection_test.cpp +++ b/test/gc/garbage_collection_test.cpp @@ -106,8 +106,8 @@ int GarbageNum(storage::DataTable *table) { // get tuple recycled by GC int RecycledNum(storage::DataTable *table) { int count = 0; - auto table_id = table->GetOid(); - while (!gc::GCManagerFactory::GetInstance().ReturnFreeSlot(table_id).IsNull()) + while ( + !gc::GCManagerFactory::GetInstance().GetRecycledTupleSlot(table).IsNull()) count++; LOG_INFO("recycled version num = %d", count); @@ -289,11 +289,7 @@ TEST_F(GarbageCollectionTests, DeleteTest) { // there should be two versions to be recycled by the GC: // the deleted version and the empty version. - // however, the txn will explicitly pass one version (the deleted - // version) to the GC manager. - // The GC itself should be responsible for recycling the - // empty version. - EXPECT_EQ(1, recycle_num); + EXPECT_EQ(2, recycle_num); gc_manager.StopGC(); gc::GCManagerFactory::Configure(0); diff --git a/test/gc/transaction_level_gc_manager_test.cpp b/test/gc/transaction_level_gc_manager_test.cpp index b53f9084ad2..c0156460939 100644 --- a/test/gc/transaction_level_gc_manager_test.cpp +++ b/test/gc/transaction_level_gc_manager_test.cpp @@ -10,17 +10,18 @@ // //===----------------------------------------------------------------------===// +#include "catalog/catalog.h" +#include "common/harness.h" +#include "concurrency/epoch_manager.h" #include "concurrency/testing_transaction_util.h" #include "executor/testing_executor_util.h" -#include "common/harness.h" #include "gc/transaction_level_gc_manager.h" -#include "concurrency/epoch_manager.h" - -#include "catalog/catalog.h" +#include "sql/testing_sql_util.h" #include "storage/data_table.h" #include "storage/tile_group.h" #include "storage/database.h" #include "storage/storage_manager.h" +#include "type/value_factory.h" namespace peloton { @@ -32,55 +33,936 @@ namespace test { class TransactionLevelGCManagerTests : public PelotonTest {}; -ResultType UpdateTuple(storage::DataTable *table, const int key) { - srand(15721); +int GetNumRecycledTuples(storage::DataTable *table) { + int count = 0; + // auto table_id = table->GetOid(); + while ( + !gc::GCManagerFactory::GetInstance().GetRecycledTupleSlot(table).IsNull()) + count++; + + LOG_INFO("recycled version num = %d", count); + return count; +} + +size_t CountOccurrencesInAllIndexes(storage::DataTable *table, int first_val, + int second_val) { + size_t num_occurrences = 0; + std::unique_ptr tuple( + new storage::Tuple(table->GetSchema(), true)); + auto primary_key = type::ValueFactory::GetIntegerValue(first_val); + auto value = type::ValueFactory::GetIntegerValue(second_val); + + tuple->SetValue(0, primary_key, nullptr); + tuple->SetValue(1, value, nullptr); + + for (size_t idx = 0; idx < table->GetIndexCount(); ++idx) { + auto index = table->GetIndex(idx); + if (index == nullptr) continue; + auto index_schema = index->GetKeySchema(); + auto indexed_columns = index_schema->GetIndexedColumns(); + + // build key. + std::unique_ptr current_key( + new storage::Tuple(index_schema, true)); + current_key->SetFromTuple(tuple.get(), indexed_columns, index->GetPool()); + + std::vector index_entries; + index->ScanKey(current_key.get(), index_entries); + num_occurrences += index_entries.size(); + } + return num_occurrences; +} + +size_t CountOccurrencesInIndex(storage::DataTable *table, int idx, + int first_val, int second_val) { + std::unique_ptr tuple( + new storage::Tuple(table->GetSchema(), true)); + auto primary_key = type::ValueFactory::GetIntegerValue(first_val); + auto value = type::ValueFactory::GetIntegerValue(second_val); + + tuple->SetValue(0, primary_key, nullptr); + tuple->SetValue(1, value, nullptr); + + auto index = table->GetIndex(idx); + if (index == nullptr) return 0; + auto index_schema = index->GetKeySchema(); + auto indexed_columns = index_schema->GetIndexedColumns(); + + // build key. + std::unique_ptr current_key( + new storage::Tuple(index_schema, true)); + current_key->SetFromTuple(tuple.get(), indexed_columns, index->GetPool()); + + std::vector index_entries; + index->ScanKey(current_key.get(), index_entries); + + return index_entries.size(); +} + +//////////////////////////////////////////// +// NEW TESTS +//////////////////////////////////////////// + +// Scenario: Abort Insert (due to other operation) +// Insert tuple +// Some other operation fails +// Abort +// Assert RQ size = 1 +// Assert not present in indexes +TEST_F(TransactionLevelGCManagerTests, AbortInsertTest) { + std::string test_name = "abortinsert"; + uint64_t current_epoch = 0; + auto &epoch_manager = concurrency::EpochManagerFactory::GetInstance(); + epoch_manager.Reset(++current_epoch); + std::vector> gc_threads; + gc::GCManagerFactory::Configure(1); + auto &gc_manager = gc::TransactionLevelGCManager::GetInstance(); + gc_manager.Reset(); + auto storage_manager = storage::StorageManager::GetInstance(); + auto database = TestingExecutorUtil::InitializeDatabase(test_name + "db"); + oid_t db_id = database->GetOid(); + EXPECT_TRUE(storage_manager->HasDatabase(db_id)); + + std::unique_ptr table(TestingTransactionUtil::CreateTable( + 0, test_name + "table", db_id, INVALID_OID, 1234, true)); + TestingTransactionUtil::AddSecondaryIndex(table.get()); + + EXPECT_EQ(0, GetNumRecycledTuples(table.get())); + + epoch_manager.SetCurrentEpochId(++current_epoch); + + // insert, then abort + auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance(); + TransactionScheduler scheduler(1, table.get(), &txn_manager); + scheduler.Txn(0).Insert(0, 1); + scheduler.Txn(0).Abort(); + scheduler.Run(); + + EXPECT_EQ(ResultType::ABORTED, scheduler.schedules[0].txn_result); + + epoch_manager.SetCurrentEpochId(++current_epoch); + gc_manager.ClearGarbage(0); + + EXPECT_EQ(1, GetNumRecycledTuples(table.get())); + EXPECT_EQ(0, CountOccurrencesInAllIndexes(table.get(), 2, 1)); + + table.release(); + TestingExecutorUtil::DeleteDatabase(test_name + "db"); + epoch_manager.SetCurrentEpochId(++current_epoch); + gc_manager.StopGC(); + gc::GCManagerFactory::Configure(0); + EXPECT_FALSE(storage_manager->HasDatabase(db_id)); +} + +// Fail to insert a tuple +// Scenario: Failed Insert (due to insert failure (e.g. index rejects insert or +// FK constraints) violated) +// Abort +// Assert RQ size = 1 +// Assert old copy in 2 indexes +// Assert new copy in 0 indexes +TEST_F(TransactionLevelGCManagerTests, FailedInsertPrimaryKeyTest) { + std::string test_name = "failedinsertprimarykey"; + uint64_t current_epoch = 0; + auto &epoch_manager = concurrency::EpochManagerFactory::GetInstance(); + epoch_manager.Reset(++current_epoch); + std::vector> gc_threads; + gc::GCManagerFactory::Configure(1); + auto &gc_manager = gc::TransactionLevelGCManager::GetInstance(); + gc_manager.Reset(); + auto storage_manager = storage::StorageManager::GetInstance(); + auto database = TestingExecutorUtil::InitializeDatabase(test_name + "db"); + oid_t db_id = database->GetOid(); + EXPECT_TRUE(storage_manager->HasDatabase(db_id)); + + std::unique_ptr table(TestingTransactionUtil::CreateTable( + 0, test_name + "table", db_id, INVALID_OID, 1234, true)); + TestingTransactionUtil::AddSecondaryIndex(table.get()); + + EXPECT_EQ(0, GetNumRecycledTuples(table.get())); + + epoch_manager.SetCurrentEpochId(++current_epoch); + + // insert duplicate key (failure), try to commit + auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance(); + TransactionScheduler scheduler(2, table.get(), &txn_manager); + scheduler.Txn(0).Insert(0, 0); + scheduler.Txn(0).Commit(); + scheduler.Txn(1).Insert(0, 1); // primary key already exists in table + scheduler.Txn(1).Commit(); + scheduler.Run(); + + EXPECT_EQ(ResultType::SUCCESS, scheduler.schedules[0].txn_result); + EXPECT_EQ(ResultType::ABORTED, scheduler.schedules[1].txn_result); + + epoch_manager.SetCurrentEpochId(++current_epoch); + gc_manager.ClearGarbage(0); + + // EXPECT_EQ(1, GetNumRecycledTuples(table.get())); + + EXPECT_EQ(1, CountOccurrencesInIndex(table.get(), 0, 0, 0)); + EXPECT_EQ(1, CountOccurrencesInIndex(table.get(), 1, 0, 0)); + + EXPECT_EQ(0, CountOccurrencesInIndex(table.get(), 1, 0, 1)); + + table.release(); + TestingExecutorUtil::DeleteDatabase(test_name + "db"); + epoch_manager.SetCurrentEpochId(++current_epoch); + gc_manager.StopGC(); + gc::GCManagerFactory::Configure(0); + EXPECT_FALSE(storage_manager->HasDatabase(db_id)); +} + +//// Scenario: Failed Insert (due to insert failure (e.g. index rejects insert +/// or FK constraints) violated) +//// Fail to insert a tuple +//// Abort +//// Assert RQ size = 1 +//// Assert old tuple in 2 indexes +//// Assert new tuple in 0 indexes +TEST_F(TransactionLevelGCManagerTests, FailedInsertSecondaryKeyTest) { + std::string test_name = "failedinsertsecondarykey"; + uint64_t current_epoch = 0; + auto &epoch_manager = concurrency::EpochManagerFactory::GetInstance(); + epoch_manager.Reset(++current_epoch); + std::vector> gc_threads; + gc::GCManagerFactory::Configure(1); + auto &gc_manager = gc::TransactionLevelGCManager::GetInstance(); + gc_manager.Reset(); + auto storage_manager = storage::StorageManager::GetInstance(); + auto database = TestingExecutorUtil::InitializeDatabase(test_name + "db"); + oid_t db_id = database->GetOid(); + EXPECT_TRUE(storage_manager->HasDatabase(db_id)); + + std::unique_ptr table(TestingTransactionUtil::CreateTable( + 0, test_name + "table", db_id, INVALID_OID, 1234, true)); + + TestingTransactionUtil::AddSecondaryIndex(table.get()); + + EXPECT_EQ(0, GetNumRecycledTuples(table.get())); + + epoch_manager.SetCurrentEpochId(++current_epoch); + + // insert duplicate value (secondary index requires uniqueness, so fails) + auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance(); + TransactionScheduler scheduler(2, table.get(), &txn_manager); + scheduler.Txn(0).Insert(0, 1); // succeeds + scheduler.Txn(0).Commit(); + scheduler.Txn(1).Insert(1, 1); // fails, dup value + scheduler.Txn(1).Commit(); + scheduler.Run(); + EXPECT_EQ(ResultType::SUCCESS, scheduler.schedules[0].txn_result); + EXPECT_EQ(ResultType::ABORTED, scheduler.schedules[1].txn_result); + + epoch_manager.SetCurrentEpochId(++current_epoch); + gc_manager.ClearGarbage(0); + + EXPECT_EQ(1, GetNumRecycledTuples(table.get())); + + EXPECT_EQ(1, CountOccurrencesInIndex(table.get(), 0, 0, 1)); + EXPECT_EQ(1, CountOccurrencesInIndex(table.get(), 1, 0, 1)); + + EXPECT_EQ(0, CountOccurrencesInIndex(table.get(), 0, 1, 1)); + + table.release(); + TestingExecutorUtil::DeleteDatabase(test_name + "db"); + epoch_manager.SetCurrentEpochId(++current_epoch); + gc_manager.StopGC(); + gc::GCManagerFactory::Configure(0); + EXPECT_FALSE(storage_manager->HasDatabase(db_id)); +} + +//// Scenario: COMMIT_UPDATE +//// Insert tuple +//// Commit +//// Update tuple +//// Commit +//// Assert RQ size = 1 +//// Assert old version in 1 index (primary key) +//// Assert new version in 2 indexes +TEST_F(TransactionLevelGCManagerTests, CommitUpdateSecondaryKeyTest) { + std::string test_name = "commitupdatesecondarykey"; + uint64_t current_epoch = 0; + auto &epoch_manager = concurrency::EpochManagerFactory::GetInstance(); + epoch_manager.Reset(++current_epoch); + std::vector> gc_threads; + gc::GCManagerFactory::Configure(1); + auto &gc_manager = gc::TransactionLevelGCManager::GetInstance(); + gc_manager.Reset(); + auto storage_manager = storage::StorageManager::GetInstance(); + auto database = TestingExecutorUtil::InitializeDatabase(test_name + "db"); + oid_t db_id = database->GetOid(); + EXPECT_TRUE(storage_manager->HasDatabase(db_id)); + + std::unique_ptr table(TestingTransactionUtil::CreateTable( + 0, test_name + "table", db_id, INVALID_OID, 1234, true)); + + TestingTransactionUtil::AddSecondaryIndex(table.get()); + + EXPECT_EQ(0, GetNumRecycledTuples(table.get())); + + epoch_manager.SetCurrentEpochId(++current_epoch); + + // insert, commit. update, commit. + auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance(); + TransactionScheduler scheduler(2, table.get(), &txn_manager); + scheduler.Txn(0).Insert(5, 1); + scheduler.Txn(0).Commit(); + scheduler.Txn(1).Update(5, 2); + scheduler.Txn(1).Commit(); + scheduler.Run(); + EXPECT_EQ(ResultType::SUCCESS, scheduler.schedules[0].txn_result); + EXPECT_EQ(ResultType::SUCCESS, scheduler.schedules[1].txn_result); + + epoch_manager.SetCurrentEpochId(++current_epoch); + gc_manager.ClearGarbage(0); + + EXPECT_EQ(1, GetNumRecycledTuples(table.get())); + + EXPECT_EQ(0, CountOccurrencesInIndex(table.get(), 1, 5, 1)); + + EXPECT_EQ(1, CountOccurrencesInIndex(table.get(), 0, 5, 2)); + EXPECT_EQ(1, CountOccurrencesInIndex(table.get(), 1, 5, 2)); + + table.release(); + TestingExecutorUtil::DeleteDatabase(test_name + "db"); + epoch_manager.SetCurrentEpochId(++current_epoch); + gc_manager.StopGC(); + gc::GCManagerFactory::Configure(0); + EXPECT_FALSE(storage_manager->HasDatabase(db_id)); +} + +// Scenario: ABORT_UPDATE +// Insert tuple +// Commit +// Update tuple +// Abort +// Assert RQ size = 1 +// Assert old version is in 2 indexes +// Assert new version is in 1 index (primary key) +TEST_F(TransactionLevelGCManagerTests, AbortUpdateSecondaryKeyTest) { + std::string test_name = "abortupdatesecondarykey"; + uint64_t current_epoch = 0; + auto &epoch_manager = concurrency::EpochManagerFactory::GetInstance(); + epoch_manager.Reset(++current_epoch); + std::vector> gc_threads; + gc::GCManagerFactory::Configure(1); + auto &gc_manager = gc::TransactionLevelGCManager::GetInstance(); + gc_manager.Reset(); + auto storage_manager = storage::StorageManager::GetInstance(); + auto database = TestingExecutorUtil::InitializeDatabase(test_name + "db"); + oid_t db_id = database->GetOid(); + EXPECT_TRUE(storage_manager->HasDatabase(db_id)); + + std::unique_ptr table(TestingTransactionUtil::CreateTable( + 0, test_name + "table", db_id, INVALID_OID, 1234, true)); + + TestingTransactionUtil::AddSecondaryIndex(table.get()); + + EXPECT_EQ(0, GetNumRecycledTuples(table.get())); + + epoch_manager.SetCurrentEpochId(++current_epoch); + + // update, abort + auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance(); + TransactionScheduler scheduler(2, table.get(), &txn_manager); + scheduler.Txn(0).Insert(0, 1); // succeeds + scheduler.Txn(0).Commit(); + scheduler.Txn(1).Update(0, 2); + scheduler.Txn(1).Abort(); + scheduler.Run(); + EXPECT_EQ(ResultType::SUCCESS, scheduler.schedules[0].txn_result); + EXPECT_EQ(ResultType::ABORTED, scheduler.schedules[1].txn_result); + + epoch_manager.SetCurrentEpochId(++current_epoch); + gc_manager.ClearGarbage(0); + + EXPECT_EQ(1, GetNumRecycledTuples(table.get())); + + EXPECT_EQ(1, CountOccurrencesInIndex(table.get(), 0, 0, 1)); + EXPECT_EQ(1, CountOccurrencesInIndex(table.get(), 1, 0, 1)); + + EXPECT_EQ(0, CountOccurrencesInIndex(table.get(), 1, 0, 2)); + + table.release(); + TestingExecutorUtil::DeleteDatabase(test_name + "db"); + epoch_manager.SetCurrentEpochId(++current_epoch); + gc_manager.StopGC(); + gc::GCManagerFactory::Configure(0); +} + +// Scenario: COMMIT_INS_UPDATE (not a GC type) +// Insert tuple +// Update tuple +// Commit +// Assert RQ.size = 0 +// Assert old tuple in 1 index (primary key) +// Assert new tuple in 2 indexes +// Test is disabled until the reuse of owned tuple slots optimization is removed. +TEST_F(TransactionLevelGCManagerTests, DISABLED_CommitInsertUpdateTest) { + std::string test_name = "commitinsertupdate"; + uint64_t current_epoch = 0; + auto &epoch_manager = concurrency::EpochManagerFactory::GetInstance(); + epoch_manager.Reset(++current_epoch); + std::vector> gc_threads; + gc::GCManagerFactory::Configure(1); + auto &gc_manager = gc::TransactionLevelGCManager::GetInstance(); + gc_manager.Reset(); + auto storage_manager = storage::StorageManager::GetInstance(); + auto database = TestingExecutorUtil::InitializeDatabase(test_name + "db"); + oid_t db_id = database->GetOid(); + EXPECT_TRUE(storage_manager->HasDatabase(db_id)); + + std::unique_ptr table(TestingTransactionUtil::CreateTable( + 0, test_name + "table", db_id, INVALID_OID, 1234, true)); + TestingTransactionUtil::AddSecondaryIndex(table.get()); + + EXPECT_EQ(0, GetNumRecycledTuples(table.get())); + + epoch_manager.SetCurrentEpochId(++current_epoch); + + // insert, update, commit + auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance(); + TransactionScheduler scheduler(1, table.get(), &txn_manager); + scheduler.Txn(0).Insert(0, 1); + scheduler.Txn(0).Update(0, 2); + scheduler.Txn(0).Commit(); + scheduler.Run(); + + EXPECT_EQ(ResultType::SUCCESS, scheduler.schedules[0].txn_result); + + epoch_manager.SetCurrentEpochId(++current_epoch); + gc_manager.ClearGarbage(0); + + EXPECT_EQ(0, GetNumRecycledTuples(table.get())); + + EXPECT_EQ(0, CountOccurrencesInIndex(table.get(), 1, 0, 1)); + + EXPECT_EQ(1, CountOccurrencesInIndex(table.get(), 0, 0, 2)); + EXPECT_EQ(1, CountOccurrencesInIndex(table.get(), 1, 0, 2)); + + table.release(); + TestingExecutorUtil::DeleteDatabase(test_name + "db"); + epoch_manager.SetCurrentEpochId(++current_epoch); + gc_manager.StopGC(); + gc::GCManagerFactory::Configure(0); + EXPECT_FALSE(storage_manager->HasDatabase(db_id)); +} + +// Scenario: ABORT_INS_UPDATE +// Insert tuple +// Update tuple +// Abort +// Assert RQ.size = 1 or 2? +// Assert inserted tuple in 0 indexes +// Assert updated tuple in 0 indexes +// Test is disabled until the reuse of owned tuple slots optimization is removed. +TEST_F(TransactionLevelGCManagerTests, DISABLED_AbortInsertUpdateTest) { + std::string test_name = "abortinsertupdate"; + uint64_t current_epoch = 0; + auto &epoch_manager = concurrency::EpochManagerFactory::GetInstance(); + epoch_manager.Reset(++current_epoch); + std::vector> gc_threads; + gc::GCManagerFactory::Configure(1); + auto &gc_manager = gc::TransactionLevelGCManager::GetInstance(); + gc_manager.Reset(); + auto storage_manager = storage::StorageManager::GetInstance(); + auto database = TestingExecutorUtil::InitializeDatabase(test_name + "db"); + oid_t db_id = database->GetOid(); + EXPECT_TRUE(storage_manager->HasDatabase(db_id)); + + std::unique_ptr table(TestingTransactionUtil::CreateTable( + 0, test_name + "table", db_id, INVALID_OID, 1234, true)); + TestingTransactionUtil::AddSecondaryIndex(table.get()); + + EXPECT_EQ(0, GetNumRecycledTuples(table.get())); + + epoch_manager.SetCurrentEpochId(++current_epoch); + + // insert, update, abort + auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance(); + TransactionScheduler scheduler(1, table.get(), &txn_manager); + scheduler.Txn(0).Insert(0, 1); + scheduler.Txn(0).Update(0, 2); + scheduler.Txn(0).Abort(); + scheduler.Run(); + EXPECT_EQ(ResultType::ABORTED, scheduler.schedules[0].txn_result); + + epoch_manager.SetCurrentEpochId(++current_epoch); + gc_manager.ClearGarbage(0); + + EXPECT_EQ(1, GetNumRecycledTuples(table.get())); + EXPECT_EQ(0, CountOccurrencesInAllIndexes(table.get(), 0, 1)); + EXPECT_EQ(0, CountOccurrencesInAllIndexes(table.get(), 0, 2)); + + table.release(); + TestingExecutorUtil::DeleteDatabase(test_name + "db"); + epoch_manager.SetCurrentEpochId(++current_epoch); + gc_manager.StopGC(); + gc::GCManagerFactory::Configure(0); + EXPECT_FALSE(storage_manager->HasDatabase(db_id)); +} + +// Scenario: COMMIT_DELETE +// Insert tuple +// Commit +// Delete tuple +// Commit +// Assert RQ size = 2 +// Assert deleted tuple appears in 0 indexes +TEST_F(TransactionLevelGCManagerTests, CommitDeleteTest) { + std::string test_name = "commitdelete"; + uint64_t current_epoch = 0; + auto &epoch_manager = concurrency::EpochManagerFactory::GetInstance(); + epoch_manager.Reset(++current_epoch); + std::vector> gc_threads; + gc::GCManagerFactory::Configure(1); + auto &gc_manager = gc::TransactionLevelGCManager::GetInstance(); + gc_manager.Reset(); + auto storage_manager = storage::StorageManager::GetInstance(); + auto database = TestingExecutorUtil::InitializeDatabase(test_name + "db"); + oid_t db_id = database->GetOid(); + EXPECT_TRUE(storage_manager->HasDatabase(db_id)); + + std::unique_ptr table(TestingTransactionUtil::CreateTable( + 0, test_name + "table", db_id, INVALID_OID, 1234, true)); + TestingTransactionUtil::AddSecondaryIndex(table.get()); + + EXPECT_EQ(0, GetNumRecycledTuples(table.get())); + + epoch_manager.SetCurrentEpochId(++current_epoch); + + // insert, commit, delete, commit + auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance(); + TransactionScheduler scheduler(2, table.get(), &txn_manager); + scheduler.Txn(0).Insert(0, 1); + scheduler.Txn(0).Commit(); + scheduler.Txn(1).Delete(0); + scheduler.Txn(1).Commit(); + scheduler.Run(); + + EXPECT_EQ(ResultType::SUCCESS, scheduler.schedules[0].txn_result); + EXPECT_EQ(ResultType::SUCCESS, scheduler.schedules[1].txn_result); + epoch_manager.SetCurrentEpochId(++current_epoch); + gc_manager.ClearGarbage(0); + + EXPECT_EQ(2, GetNumRecycledTuples(table.get())); + EXPECT_EQ(0, CountOccurrencesInAllIndexes(table.get(), 0, 1)); + + table.release(); + TestingExecutorUtil::DeleteDatabase(test_name + "db"); + epoch_manager.SetCurrentEpochId(++current_epoch); + gc_manager.StopGC(); + gc::GCManagerFactory::Configure(0); + EXPECT_FALSE(storage_manager->HasDatabase(db_id)); +} + +// Scenario: ABORT_DELETE +// Insert tuple +// Commit +// Delete tuple +// Abort +// Assert RQ size = 1 +// Assert tuple found in 2 indexes +TEST_F(TransactionLevelGCManagerTests, AbortDeleteTest) { + std::string test_name = "abortdelete"; + uint64_t current_epoch = 0; + auto &epoch_manager = concurrency::EpochManagerFactory::GetInstance(); + epoch_manager.Reset(++current_epoch); + std::vector> gc_threads; + gc::GCManagerFactory::Configure(1); + auto &gc_manager = gc::TransactionLevelGCManager::GetInstance(); + gc_manager.Reset(); + auto storage_manager = storage::StorageManager::GetInstance(); + auto database = TestingExecutorUtil::InitializeDatabase(test_name + "db"); + oid_t db_id = database->GetOid(); + EXPECT_TRUE(storage_manager->HasDatabase(db_id)); + + std::unique_ptr table(TestingTransactionUtil::CreateTable( + 0, test_name + "table", db_id, INVALID_OID, 1234, true)); + TestingTransactionUtil::AddSecondaryIndex(table.get()); + EXPECT_EQ(0, GetNumRecycledTuples(table.get())); + + epoch_manager.SetCurrentEpochId(++current_epoch); + + // delete, abort auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance(); - TransactionScheduler scheduler(1, table, &txn_manager); - scheduler.Txn(0).Update(key, rand() % 15721); + TransactionScheduler scheduler(2, table.get(), &txn_manager); + scheduler.Txn(0).Insert(0, 1); scheduler.Txn(0).Commit(); + scheduler.Txn(1).Delete(0); + scheduler.Txn(1).Abort(); scheduler.Run(); + EXPECT_EQ(ResultType::SUCCESS, scheduler.schedules[0].txn_result); + EXPECT_EQ(ResultType::ABORTED, scheduler.schedules[1].txn_result); + + epoch_manager.SetCurrentEpochId(++current_epoch); + gc_manager.ClearGarbage(0); - return scheduler.schedules[0].txn_result; + EXPECT_EQ(1, GetNumRecycledTuples(table.get())); + EXPECT_EQ(2, CountOccurrencesInAllIndexes(table.get(), 0, 1)); + + table.release(); + TestingExecutorUtil::DeleteDatabase(test_name + "db"); + epoch_manager.SetCurrentEpochId(++current_epoch); + gc_manager.StopGC(); + gc::GCManagerFactory::Configure(0); + EXPECT_FALSE(storage_manager->HasDatabase(db_id)); } -ResultType InsertTuple(storage::DataTable *table, const int key) { - srand(15721); +// Scenario: COMMIT_INS_DEL +// Insert tuple +// Delete tuple +// Commit +// Assert RQ.size = 1 +// Assert tuple found in 0 indexes +TEST_F(TransactionLevelGCManagerTests, CommitInsertDeleteTest) { + std::string test_name = "commitinsertdelete"; + uint64_t current_epoch = 0; + auto &epoch_manager = concurrency::EpochManagerFactory::GetInstance(); + epoch_manager.Reset(++current_epoch); + std::vector> gc_threads; + gc::GCManagerFactory::Configure(1); + auto &gc_manager = gc::TransactionLevelGCManager::GetInstance(); + gc_manager.Reset(); + auto storage_manager = storage::StorageManager::GetInstance(); + auto database = TestingExecutorUtil::InitializeDatabase(test_name + "db"); + oid_t db_id = database->GetOid(); + EXPECT_TRUE(storage_manager->HasDatabase(db_id)); + + std::unique_ptr table(TestingTransactionUtil::CreateTable( + 0, test_name + "table", db_id, INVALID_OID, 1234, true)); + TestingTransactionUtil::AddSecondaryIndex(table.get()); + + EXPECT_EQ(0, GetNumRecycledTuples(table.get())); + + epoch_manager.SetCurrentEpochId(++current_epoch); + // insert, delete, commit auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance(); - TransactionScheduler scheduler(1, table, &txn_manager); - scheduler.Txn(0).Insert(key, rand() % 15721); + TransactionScheduler scheduler(1, table.get(), &txn_manager); + scheduler.Txn(0).Insert(0, 1); + scheduler.Txn(0).Delete(0); scheduler.Txn(0).Commit(); scheduler.Run(); + EXPECT_EQ(ResultType::SUCCESS, scheduler.schedules[0].txn_result); + + epoch_manager.SetCurrentEpochId(++current_epoch); + gc_manager.ClearGarbage(0); + + EXPECT_EQ(1, GetNumRecycledTuples(table.get())); + EXPECT_EQ(0, CountOccurrencesInAllIndexes(table.get(), 0, 1)); + + table.release(); + TestingExecutorUtil::DeleteDatabase(test_name + "db"); + epoch_manager.SetCurrentEpochId(++current_epoch); + gc_manager.StopGC(); + gc::GCManagerFactory::Configure(0); + EXPECT_FALSE(storage_manager->HasDatabase(db_id)); +} + +// Scenario: ABORT_INS_DEL +// Insert tuple +// Delete tuple +// Abort +// Assert RQ size = 1 +// Assert tuple found in 0 indexes +TEST_F(TransactionLevelGCManagerTests, AbortInsertDeleteTest) { + std::string test_name = "abortinsertdelete"; + uint64_t current_epoch = 0; + auto &epoch_manager = concurrency::EpochManagerFactory::GetInstance(); + epoch_manager.Reset(++current_epoch); + std::vector> gc_threads; + gc::GCManagerFactory::Configure(1); + auto &gc_manager = gc::TransactionLevelGCManager::GetInstance(); + gc_manager.Reset(); + auto storage_manager = storage::StorageManager::GetInstance(); + auto database = TestingExecutorUtil::InitializeDatabase(test_name + "db"); + oid_t db_id = database->GetOid(); + EXPECT_TRUE(storage_manager->HasDatabase(db_id)); + + std::unique_ptr table(TestingTransactionUtil::CreateTable( + 0, test_name + "table", db_id, INVALID_OID, 1234, true)); + TestingTransactionUtil::AddSecondaryIndex(table.get()); + + EXPECT_EQ(0, GetNumRecycledTuples(table.get())); + + epoch_manager.SetCurrentEpochId(++current_epoch); + + // insert, delete, abort + auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance(); + TransactionScheduler scheduler(1, table.get(), &txn_manager); + scheduler.Txn(0).Insert(0, 1); + scheduler.Txn(0).Delete(0); + scheduler.Txn(0).Abort(); + scheduler.Run(); + EXPECT_EQ(ResultType::ABORTED, scheduler.schedules[0].txn_result); + + epoch_manager.SetCurrentEpochId(++current_epoch); + gc_manager.ClearGarbage(0); + + EXPECT_EQ(1, GetNumRecycledTuples(table.get())); + EXPECT_EQ(0, CountOccurrencesInAllIndexes(table.get(), 0, 1)); - return scheduler.schedules[0].txn_result; + table.release(); + TestingExecutorUtil::DeleteDatabase(test_name + "db"); + epoch_manager.SetCurrentEpochId(++current_epoch); + gc_manager.StopGC(); + gc::GCManagerFactory::Configure(0); + EXPECT_FALSE(storage_manager->HasDatabase(db_id)); } -ResultType DeleteTuple(storage::DataTable *table, const int key) { - srand(15721); +// Scenario: COMMIT_UPDATE_DEL +// Insert tuple +// Commit +// Update tuple +// Delete tuple +// Commit +// Assert RQ.size = 2 +// Assert old tuple in 0 indexes +// Assert new tuple in 0 indexes +// Test is disabled until the reuse of owned tuple slots optimization is removed. +TEST_F(TransactionLevelGCManagerTests, DISABLED_CommitUpdateDeleteTest) { + std::string test_name = "commitupdatedelete"; + uint64_t current_epoch = 0; + auto &epoch_manager = concurrency::EpochManagerFactory::GetInstance(); + epoch_manager.Reset(++current_epoch); + std::vector> gc_threads; + gc::GCManagerFactory::Configure(1); + auto &gc_manager = gc::TransactionLevelGCManager::GetInstance(); + gc_manager.Reset(); + auto storage_manager = storage::StorageManager::GetInstance(); + auto database = TestingExecutorUtil::InitializeDatabase(test_name + "db"); + oid_t db_id = database->GetOid(); + EXPECT_TRUE(storage_manager->HasDatabase(db_id)); + + std::unique_ptr table(TestingTransactionUtil::CreateTable( + 0, test_name + "table", db_id, INVALID_OID, 1234, true)); + TestingTransactionUtil::AddSecondaryIndex(table.get()); + + EXPECT_EQ(0, GetNumRecycledTuples(table.get())); + + epoch_manager.SetCurrentEpochId(++current_epoch); + // update, delete, commit auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance(); - TransactionScheduler scheduler(1, table, &txn_manager); - scheduler.Txn(0).Delete(key); + TransactionScheduler scheduler(2, table.get(), &txn_manager); + scheduler.Txn(0).Insert(0, 1); scheduler.Txn(0).Commit(); + scheduler.Txn(1).Update(0, 2); + scheduler.Txn(1).Delete(0); + scheduler.Txn(1).Commit(); scheduler.Run(); + EXPECT_EQ(ResultType::SUCCESS, scheduler.schedules[0].txn_result); - return scheduler.schedules[0].txn_result; + epoch_manager.SetCurrentEpochId(++current_epoch); + gc_manager.ClearGarbage(0); + + EXPECT_EQ(2, GetNumRecycledTuples(table.get())); + EXPECT_EQ(0, CountOccurrencesInAllIndexes(table.get(), 0, 1)); + EXPECT_EQ(0, CountOccurrencesInAllIndexes(table.get(), 0, 2)); + + table.release(); + TestingExecutorUtil::DeleteDatabase(test_name + "db"); + epoch_manager.SetCurrentEpochId(++current_epoch); + gc_manager.StopGC(); + gc::GCManagerFactory::Configure(0); + EXPECT_FALSE(storage_manager->HasDatabase(db_id)); } -ResultType SelectTuple(storage::DataTable *table, const int key, - std::vector &results) { - srand(15721); +// Scenario: ABORT_UPDATE_DEL +// Insert tuple +// Commit +// Update tuple +// Delete tuple +// Abort +// Assert RQ size = 2 +// Assert old tuple in 2 indexes +// Assert new tuple in 1 index (primary key) +// Test is disabled until the reuse of owned tuple slots optimization is removed. +TEST_F(TransactionLevelGCManagerTests, DISABLED_AbortUpdateDeleteTest) { + std::string test_name = "abortupdatedelete"; + uint64_t current_epoch = 0; + auto &epoch_manager = concurrency::EpochManagerFactory::GetInstance(); + epoch_manager.Reset(++current_epoch); + std::vector> gc_threads; + gc::GCManagerFactory::Configure(1); + auto &gc_manager = gc::TransactionLevelGCManager::GetInstance(); + gc_manager.Reset(); + auto storage_manager = storage::StorageManager::GetInstance(); + auto database = TestingExecutorUtil::InitializeDatabase(test_name + "db"); + oid_t db_id = database->GetOid(); + EXPECT_TRUE(storage_manager->HasDatabase(db_id)); + + std::unique_ptr table(TestingTransactionUtil::CreateTable( + 0, test_name + "table", db_id, INVALID_OID, 1234, true)); + TestingTransactionUtil::AddSecondaryIndex(table.get()); + + EXPECT_EQ(0, GetNumRecycledTuples(table.get())); + epoch_manager.SetCurrentEpochId(++current_epoch); + + // update, delete, then abort auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance(); - TransactionScheduler scheduler(1, table, &txn_manager); - scheduler.Txn(0).Read(key); + TransactionScheduler scheduler(2, table.get(), &txn_manager); + scheduler.Txn(0).Insert(0, 1); scheduler.Txn(0).Commit(); + scheduler.Txn(1).Update(0, 2); + scheduler.Txn(1).Delete(0); + scheduler.Txn(1).Abort(); scheduler.Run(); + EXPECT_EQ(ResultType::SUCCESS, scheduler.schedules[0].txn_result); + EXPECT_EQ(ResultType::ABORTED, scheduler.schedules[1].txn_result); + + epoch_manager.SetCurrentEpochId(++current_epoch); + gc_manager.ClearGarbage(0); + + EXPECT_EQ(1, GetNumRecycledTuples(table.get())); + + EXPECT_EQ(2, CountOccurrencesInAllIndexes(table.get(), 0, 1)); + EXPECT_EQ(0, CountOccurrencesInIndex(table.get(), 1, 0, 2)); + + table.release(); + TestingExecutorUtil::DeleteDatabase(test_name + "db"); + epoch_manager.SetCurrentEpochId(++current_epoch); + gc_manager.StopGC(); + gc::GCManagerFactory::Configure(0); + EXPECT_FALSE(storage_manager->HasDatabase(db_id)); +} + +// Scenario: Update Primary Key Test +// Insert tuple +// Commit +// Update primary key and value +// Commit +// Assert RQ.size = 2 (primary key update causes delete and insert) +// Assert old tuple in 0 indexes +// Assert new tuple in 2 indexes +TEST_F(TransactionLevelGCManagerTests, CommitUpdatePrimaryKeyTest) { + uint64_t current_epoch = 0; + auto &epoch_manager = concurrency::EpochManagerFactory::GetInstance(); + epoch_manager.Reset(++current_epoch); + std::vector> gc_threads; + gc::GCManagerFactory::Configure(1); + auto &gc_manager = gc::TransactionLevelGCManager::GetInstance(); + gc_manager.Reset(); + auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance(); + auto txn = txn_manager.BeginTransaction(); + auto catalog = catalog::Catalog::GetInstance(); + catalog->CreateDatabase(DEFAULT_DB_NAME, txn); + txn_manager.CommitTransaction(txn); + auto database = catalog->GetDatabaseWithName(DEFAULT_DB_NAME, txn); + + TestingSQLUtil::ExecuteSQLQuery( + "CREATE TABLE test(a INT PRIMARY KEY, b INT);"); + auto table = database->GetTable(database->GetTableCount() - 1); + TestingTransactionUtil::AddSecondaryIndex(table); + + EXPECT_EQ(0, GetNumRecycledTuples(table)); + + epoch_manager.SetCurrentEpochId(++current_epoch); + + TestingSQLUtil::ExecuteSQLQuery("INSERT INTO test VALUES (3, 30);"); + + std::vector result; + std::vector tuple_descriptor; + std::string error_message; + int rows_affected; + + // confirm setup + TestingSQLUtil::ExecuteSQLQuery("SELECT * from test WHERE b=30", result, + tuple_descriptor, rows_affected, + error_message); + EXPECT_EQ('3', result[0][0]); + EXPECT_EQ(2, CountOccurrencesInAllIndexes(table, 3, 30)); + + // Perform primary key and value update + TestingSQLUtil::ExecuteSQLQuery("UPDATE test SET a=5, b=40", result, + tuple_descriptor, rows_affected, + error_message); + + epoch_manager.SetCurrentEpochId(++current_epoch); + gc_manager.ClearGarbage(0); + + // confirm update + TestingSQLUtil::ExecuteSQLQuery("SELECT * from test WHERE b=40", result, + tuple_descriptor, rows_affected, + error_message); + EXPECT_EQ('5', result[0][0]); + + EXPECT_EQ(2, GetNumRecycledTuples(table)); + EXPECT_EQ(0, CountOccurrencesInAllIndexes(table, 3, 30)); + EXPECT_EQ(2, CountOccurrencesInAllIndexes(table, 5, 40)); + + txn = txn_manager.BeginTransaction(); + catalog::Catalog::GetInstance()->DropDatabaseWithName(DEFAULT_DB_NAME, txn); + txn_manager.CommitTransaction(txn); + epoch_manager.SetCurrentEpochId(++current_epoch); + gc_manager.StopGC(); + gc::GCManagerFactory::Configure(0); +} + +// Scenario: Insert then Update Primary Key Test +// Insert tuple +// Update primary key and value +// Commit +// Assert RQ.size = 2 (primary key update causes delete and insert) +// Assert old tuple in 0 indexes +// Assert new tuple in 2 indexes +TEST_F(TransactionLevelGCManagerTests, DISABLED_CommitInsertUpdatePrimaryKeyTest) { + uint64_t current_epoch = 0; + auto &epoch_manager = concurrency::EpochManagerFactory::GetInstance(); + epoch_manager.Reset(++current_epoch); + std::vector> gc_threads; + gc::GCManagerFactory::Configure(1); + auto &gc_manager = gc::TransactionLevelGCManager::GetInstance(); + gc_manager.Reset(); + auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance(); + auto txn = txn_manager.BeginTransaction(); + auto catalog = catalog::Catalog::GetInstance(); + catalog->CreateDatabase(DEFAULT_DB_NAME, txn); + txn_manager.CommitTransaction(txn); + auto database = catalog->GetDatabaseWithName(DEFAULT_DB_NAME, txn); + + TestingSQLUtil::ExecuteSQLQuery( + "CREATE TABLE test(a INT PRIMARY KEY, b INT);"); + auto table = database->GetTable(database->GetTableCount() - 1); + TestingTransactionUtil::AddSecondaryIndex(table); + + EXPECT_EQ(0, GetNumRecycledTuples(table)); + + epoch_manager.SetCurrentEpochId(++current_epoch); + + TestingSQLUtil::ExecuteSQLQuery("BEGIN;"); + TestingSQLUtil::ExecuteSQLQuery("INSERT INTO test VALUES (3, 30);"); + TestingSQLUtil::ExecuteSQLQuery("UPDATE test SET a=5, b=40;"); + TestingSQLUtil::ExecuteSQLQuery("COMMIT;"); + + std::vector result; + std::vector tuple_descriptor; + std::string error_message; + int rows_affected; - results = scheduler.schedules[0].results; + epoch_manager.SetCurrentEpochId(++current_epoch); + gc_manager.ClearGarbage(0); - return scheduler.schedules[0].txn_result; + // confirm update + TestingSQLUtil::ExecuteSQLQuery("SELECT * from test WHERE b=40", result, + tuple_descriptor, rows_affected, + error_message); + EXPECT_EQ('5', result[0][0]); + + EXPECT_EQ(2, GetNumRecycledTuples(table)); + EXPECT_EQ(0, CountOccurrencesInAllIndexes(table, 3, 30)); + EXPECT_EQ(2, CountOccurrencesInAllIndexes(table, 5, 40)); + + txn = txn_manager.BeginTransaction(); + catalog::Catalog::GetInstance()->DropDatabaseWithName(DEFAULT_DB_NAME, txn); + txn_manager.CommitTransaction(txn); + epoch_manager.SetCurrentEpochId(++current_epoch); + gc_manager.StopGC(); + gc::GCManagerFactory::Configure(0); } // update -> delete @@ -93,7 +975,7 @@ TEST_F(TransactionLevelGCManagerTests, UpdateDeleteTest) { auto &gc_manager = gc::TransactionLevelGCManager::GetInstance(); auto storage_manager = storage::StorageManager::GetInstance(); // create database - auto database = TestingExecutorUtil::InitializeDatabase("database0"); + auto database = TestingExecutorUtil::InitializeDatabase("updatedeletedb"); oid_t db_id = database->GetOid(); EXPECT_TRUE(storage_manager->HasDatabase(db_id)); @@ -102,14 +984,14 @@ TEST_F(TransactionLevelGCManagerTests, UpdateDeleteTest) { // create a table with only one key const int num_key = 1; std::unique_ptr table(TestingTransactionUtil::CreateTable( - num_key, "TABLE0", db_id, 12345, 1234, true)); + num_key, "updatedeletetable", db_id, 12345, 1234, true)); EXPECT_EQ(1, gc_manager.GetTableCount() - prev_tc); //=========================== // update a version here. //=========================== - auto ret = UpdateTuple(table.get(), 0); + auto ret = TestingTransactionUtil::UpdateTuple(table.get(), 0); EXPECT_TRUE(ret == ResultType::SUCCESS); epoch_manager.SetCurrentEpochId(2); @@ -154,7 +1036,7 @@ TEST_F(TransactionLevelGCManagerTests, UpdateDeleteTest) { //=========================== // delete a version here. //=========================== - ret = DeleteTuple(table.get(), 0); + ret = TestingTransactionUtil::DeleteTuple(table.get(), 0); EXPECT_TRUE(ret == ResultType::SUCCESS); epoch_manager.SetCurrentEpochId(4); @@ -202,12 +1084,12 @@ TEST_F(TransactionLevelGCManagerTests, UpdateDeleteTest) { table.release(); // DROP! - TestingExecutorUtil::DeleteDatabase("database0"); + TestingExecutorUtil::DeleteDatabase("updatedeletedb"); auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance(); auto txn = txn_manager.BeginTransaction(); EXPECT_THROW( - catalog::Catalog::GetInstance()->GetDatabaseCatalogEntry(txn, "database0"), + catalog::Catalog::GetInstance()->GetDatabaseObject("updatedeletedb", txn), CatalogException); txn_manager.CommitTransaction(txn); // EXPECT_FALSE(storage_manager->HasDatabase(db_id)); @@ -226,7 +1108,7 @@ TEST_F(TransactionLevelGCManagerTests, ReInsertTest) { auto storage_manager = storage::StorageManager::GetInstance(); // create database - auto database = TestingExecutorUtil::InitializeDatabase("database1"); + auto database = TestingExecutorUtil::InitializeDatabase("reinsertdb"); oid_t db_id = database->GetOid(); EXPECT_TRUE(storage_manager->HasDatabase(db_id)); @@ -235,14 +1117,14 @@ TEST_F(TransactionLevelGCManagerTests, ReInsertTest) { // create a table with only one key const int num_key = 1; std::unique_ptr table(TestingTransactionUtil::CreateTable( - num_key, "TABLE1", db_id, 12346, 1234, true)); + num_key, "reinserttable", db_id, 12346, 1234, true)); EXPECT_EQ(1, gc_manager.GetTableCount() - prev_tc); //=========================== // insert a tuple here. //=========================== - auto ret = InsertTuple(table.get(), 100); + auto ret = TestingTransactionUtil::InsertTuple(table.get(), 100); EXPECT_TRUE(ret == ResultType::SUCCESS); epoch_manager.SetCurrentEpochId(2); @@ -290,14 +1172,14 @@ TEST_F(TransactionLevelGCManagerTests, ReInsertTest) { std::vector results; results.clear(); - ret = SelectTuple(table.get(), 100, results); + ret = TestingTransactionUtil::SelectTuple(table.get(), 100, results); EXPECT_TRUE(ret == ResultType::SUCCESS); EXPECT_TRUE(results[0] != -1); //=========================== // delete the tuple. //=========================== - ret = DeleteTuple(table.get(), 100); + ret = TestingTransactionUtil::DeleteTuple(table.get(), 100); EXPECT_TRUE(ret == ResultType::SUCCESS); epoch_manager.SetCurrentEpochId(4); @@ -343,21 +1225,21 @@ TEST_F(TransactionLevelGCManagerTests, ReInsertTest) { // select the tuple. //=========================== results.clear(); - ret = SelectTuple(table.get(), 100, results); + ret = TestingTransactionUtil::SelectTuple(table.get(), 100, results); EXPECT_TRUE(ret == ResultType::SUCCESS); EXPECT_TRUE(results[0] == -1); //=========================== // insert the tuple again. //=========================== - ret = InsertTuple(table.get(), 100); + ret = TestingTransactionUtil::InsertTuple(table.get(), 100); EXPECT_TRUE(ret == ResultType::SUCCESS); //=========================== // select the tuple. //=========================== results.clear(); - ret = SelectTuple(table.get(), 100, results); + ret = TestingTransactionUtil::SelectTuple(table.get(), 100, results); EXPECT_TRUE(ret == ResultType::SUCCESS); EXPECT_TRUE(results[0] != -1); @@ -367,12 +1249,12 @@ TEST_F(TransactionLevelGCManagerTests, ReInsertTest) { table.release(); // DROP! - TestingExecutorUtil::DeleteDatabase("database1"); + TestingExecutorUtil::DeleteDatabase("reinsertdb"); auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance(); auto txn = txn_manager.BeginTransaction(); EXPECT_THROW( - catalog::Catalog::GetInstance()->GetDatabaseCatalogEntry(txn, "database0"), + catalog::Catalog::GetInstance()->GetDatabaseObject("reinsertdb", txn), CatalogException); txn_manager.CommitTransaction(txn); // EXPECT_FALSE(storage_manager->HasDatabase(db_id)); @@ -407,7 +1289,8 @@ TEST_F(TransactionLevelGCManagerTests, ImmutabilityTest) { const int num_key = 25; const size_t tuples_per_tilegroup = 5; std::unique_ptr table(TestingTransactionUtil::CreateTable( - num_key, "TABLE1", db_id, 12347, 1234, true, tuples_per_tilegroup)); + num_key, "immutabilitytable", db_id, 12347, 1234, true, + tuples_per_tilegroup)); EXPECT_EQ(1, gc_manager.GetTableCount() - prev_tc); @@ -421,60 +1304,25 @@ TEST_F(TransactionLevelGCManagerTests, ImmutabilityTest) { tile_group_header->SetImmutability(); // Deleting a tuple from the 1st tilegroup - auto ret = DeleteTuple(table.get(), 2); - EXPECT_TRUE(ret == ResultType::SUCCESS); - epoch_manager.SetCurrentEpochId(2); - auto expired_eid = epoch_manager.GetExpiredEpochId(); - EXPECT_EQ(1, expired_eid); - auto current_eid = epoch_manager.GetCurrentEpochId(); - EXPECT_EQ(2, current_eid); - auto reclaimed_count = gc_manager.Reclaim(0, expired_eid); - auto unlinked_count = gc_manager.Unlink(0, expired_eid); - EXPECT_EQ(0, reclaimed_count); - EXPECT_EQ(1, unlinked_count); - - epoch_manager.SetCurrentEpochId(3); - expired_eid = epoch_manager.GetExpiredEpochId(); - EXPECT_EQ(2, expired_eid); - current_eid = epoch_manager.GetCurrentEpochId(); - EXPECT_EQ(3, current_eid); - reclaimed_count = gc_manager.Reclaim(0, expired_eid); - unlinked_count = gc_manager.Unlink(0, expired_eid); - EXPECT_EQ(1, reclaimed_count); - EXPECT_EQ(0, unlinked_count); + auto ret = TestingTransactionUtil::DeleteTuple(table.get(), 2); + gc_manager.ClearGarbage(0); - // ReturnFreeSlot() should return null because deleted tuple was from - // immutable tilegroup. - auto location = gc_manager.ReturnFreeSlot((table.get())->GetOid()); - EXPECT_EQ(location.IsNull(), true); + // ReturnFreeSlot() should not return a tuple slot from the immutable tile + // group + // should be from where ever the tombstone was inserted + auto location = gc_manager.GetRecycledTupleSlot(table.get()); + EXPECT_NE(tile_group->GetTileGroupId(), location.block); // Deleting a tuple from the 2nd tilegroup which is mutable. - ret = DeleteTuple(table.get(), 6); + ret = TestingTransactionUtil::DeleteTuple(table.get(), 6); EXPECT_TRUE(ret == ResultType::SUCCESS); epoch_manager.SetCurrentEpochId(4); - expired_eid = epoch_manager.GetExpiredEpochId(); - EXPECT_EQ(3, expired_eid); - current_eid = epoch_manager.GetCurrentEpochId(); - EXPECT_EQ(4, current_eid); - reclaimed_count = gc_manager.Reclaim(0, expired_eid); - unlinked_count = gc_manager.Unlink(0, expired_eid); - EXPECT_EQ(0, reclaimed_count); - EXPECT_EQ(1, unlinked_count); - - epoch_manager.SetCurrentEpochId(5); - expired_eid = epoch_manager.GetExpiredEpochId(); - EXPECT_EQ(4, expired_eid); - current_eid = epoch_manager.GetCurrentEpochId(); - EXPECT_EQ(5, current_eid); - reclaimed_count = gc_manager.Reclaim(0, expired_eid); - unlinked_count = gc_manager.Unlink(0, expired_eid); - EXPECT_EQ(1, reclaimed_count); - EXPECT_EQ(0, unlinked_count); + gc_manager.ClearGarbage(0); // ReturnFreeSlot() should not return null because deleted tuple was from - // mutable tilegroup. - location = gc_manager.ReturnFreeSlot((table.get())->GetOid()); + // mutable tilegroup + location = gc_manager.GetRecycledTupleSlot(table.get()); EXPECT_EQ(location.IsNull(), false); gc_manager.StopGC(); @@ -487,8 +1335,7 @@ TEST_F(TransactionLevelGCManagerTests, ImmutabilityTest) { auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance(); auto txn = txn_manager.BeginTransaction(); EXPECT_THROW( - catalog::Catalog::GetInstance()->GetDatabaseCatalogEntry(txn, - "immutabilitydb"), + catalog::Catalog::GetInstance()->GetDatabaseObject("immutabilitydb", txn), CatalogException); txn_manager.CommitTransaction(txn); } diff --git a/test/include/concurrency/testing_transaction_util.h b/test/include/concurrency/testing_transaction_util.h index d2387f9ee98..45231382d6a 100644 --- a/test/include/concurrency/testing_transaction_util.h +++ b/test/include/concurrency/testing_transaction_util.h @@ -153,6 +153,23 @@ class TestingTransactionUtil { static std::unique_ptr MakeProjectInfoFromTuple( const storage::Tuple *tuple); static expression::ComparisonExpression *MakePredicate(int id); + + static void AddSecondaryIndex(storage::DataTable *table); + + static ResultType UpdateTuple(storage::DataTable *table, const int key); + + static ResultType InsertTuple(storage::DataTable *table, const int key); + + static ResultType BulkInsertTuples(storage::DataTable *table, + const size_t num_tuples); + + static ResultType BulkDeleteTuples(storage::DataTable *table, + const size_t num_tuples); + + static ResultType DeleteTuple(storage::DataTable *table, const int key); + + static ResultType SelectTuple(storage::DataTable *table, const int key, + std::vector &results); }; struct TransactionOperation {