/** * @brief Recovery system based on log file */ void AriesFrontendLogger::DoRecovery() { // Set log file size log_file_size = GetLogFileSize(log_file_fd); // Go over the log size if needed if (log_file_size > 0) { bool reached_end_of_file = false; // Start the recovery transaction auto &txn_manager = concurrency::TransactionManager::GetInstance(); // Although we call BeginTransaction here, recovery txn will not be // recoreded in log file since we are in recovery mode auto recovery_txn = txn_manager.BeginTransaction(); // Go over each log record in the log file while (reached_end_of_file == false) { // Read the first byte to identify log record type // If that is not possible, then wrap up recovery auto record_type = GetNextLogRecordType(log_file, log_file_size); switch (record_type) { case LOGRECORD_TYPE_TRANSACTION_BEGIN: AddTransactionToRecoveryTable(); break; case LOGRECORD_TYPE_TRANSACTION_END: RemoveTransactionFromRecoveryTable(); break; case LOGRECORD_TYPE_TRANSACTION_COMMIT: MoveCommittedTuplesToRecoveryTxn(recovery_txn); break; case LOGRECORD_TYPE_TRANSACTION_ABORT: AbortTuplesFromRecoveryTable(); break; case LOGRECORD_TYPE_ARIES_TUPLE_INSERT: InsertTuple(recovery_txn); break; case LOGRECORD_TYPE_ARIES_TUPLE_DELETE: DeleteTuple(recovery_txn); break; case LOGRECORD_TYPE_ARIES_TUPLE_UPDATE: UpdateTuple(recovery_txn); break; default: reached_end_of_file = true; break; } } // Commit the recovery transaction txn_manager.CommitTransaction(); // Finally, abort ACTIVE transactions in recovery_txn_table AbortActiveTransactions(); // After finishing recovery, set the next oid with maximum oid // observed during the recovery auto &manager = catalog::Manager::GetInstance(); manager.SetNextOid(max_oid); } }
TEST_F(GarbageCollectionTests, UpdateTest) { auto &epoch_manager = concurrency::EpochManagerFactory::GetInstance(); epoch_manager.Reset(1); std::vector<std::unique_ptr<std::thread>> gc_threads; gc::GCManagerFactory::Configure(1); auto &gc_manager = gc::GCManagerFactory::GetInstance(); auto storage_manager = storage::StorageManager::GetInstance(); // create database auto database = TestingExecutorUtil::InitializeDatabase("UPDATE_DB"); oid_t db_id = database->GetOid(); EXPECT_TRUE(storage_manager->HasDatabase(db_id)); // create a table with only one key const int num_key = 1; std::unique_ptr<storage::DataTable> table(TestingTransactionUtil::CreateTable( num_key, "UPDATE_TABLE", db_id, INVALID_OID, 1234, true)); EXPECT_TRUE(gc_manager.GetTableCount() == 1); gc_manager.StartGC(gc_threads); const int update_num = 1; UpdateTuple(table.get(), update_num, num_key); // count garbage num auto old_num = GarbageNum(table.get()); auto recycle_num = RecycledNum(table.get()); // there should be only one garbage // generated by the last update EXPECT_EQ(1, old_num); // nothing is recycled yet. EXPECT_EQ(0, recycle_num); epoch_manager.SetCurrentEpochId(2); // get expired epoch id. // as the current epoch id is set to 2, // the expected expired epoch id should be 1. auto expired_eid = epoch_manager.GetExpiredEpochId(); EXPECT_EQ(1, expired_eid); auto current_eid = epoch_manager.GetCurrentEpochId(); EXPECT_EQ(2, current_eid); // sleep a while for gc to unlink expired version. std::this_thread::sleep_for(std::chrono::seconds(1)); old_num = GarbageNum(table.get()); recycle_num = RecycledNum(table.get()); EXPECT_EQ(1, old_num); EXPECT_EQ(0, recycle_num); epoch_manager.SetCurrentEpochId(3); // sleep a while for gc to recycle expired version. std::this_thread::sleep_for(std::chrono::seconds(1)); // there should be no garbage old_num = GarbageNum(table.get()); recycle_num = RecycledNum(table.get()); EXPECT_EQ(0, old_num); // there should be 1 tuple recycled EXPECT_EQ(1, recycle_num); gc_manager.StopGC(); gc::GCManagerFactory::Configure(0); table.release(); // DROP! TestingExecutorUtil::DeleteDatabase("UPDATE_DB"); auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance(); auto txn = txn_manager.BeginTransaction(); EXPECT_THROW(catalog::Catalog::GetInstance()->GetDatabaseObject(db_id, txn), CatalogException); txn_manager.CommitTransaction(txn); // EXPECT_FALSE(storage_manager->HasDatabase(db_id)); for (auto &gc_thread : gc_threads) { gc_thread->join(); } }