Exemplo n.º 1
0
/**
 * @brief read tuple record from log file and add them tuples to recovery txn
 * @param recovery txn
 */
void AriesFrontendLogger::DeleteTuple(concurrency::Transaction *recovery_txn) {
  TupleRecord tuple_record(LOGRECORD_TYPE_ARIES_TUPLE_DELETE);

  // Check for torn log write
  if (ReadTupleRecordHeader(tuple_record, log_file, log_file_size) == false) {
    return;
  }

  auto txn_id = tuple_record.GetTransactionId();
  if (recovery_txn_table.find(txn_id) == recovery_txn_table.end()) {
    LOG_TRACE("Delete txd id %d not found in recovery txn table", (int)txn_id);
    return;
  }

  auto table = GetTable(tuple_record);

  ItemPointer delete_location = tuple_record.GetDeleteLocation();

  // Try to delete the tuple
  bool status = table->DeleteTuple(recovery_txn, delete_location);
  if (status == false) {
    // TODO: We need to abort on failure !
    recovery_txn->SetResult(Result::RESULT_FAILURE);
    return;
  }

  auto txn = recovery_txn_table.at(txn_id);
  txn->RecordDelete(delete_location);
}
Exemplo n.º 2
0
/**
 * @brief read tuple record from log file and add them tuples to recovery txn
 * @param recovery txn
 */
void AriesFrontendLogger::UpdateTuple(concurrency::Transaction *recovery_txn) {
  TupleRecord tuple_record(LOGRECORD_TYPE_ARIES_TUPLE_UPDATE);

  // Check for torn log write
  if (ReadTupleRecordHeader(tuple_record, log_file, log_file_size) == false) {
    return;
  }

  auto txn_id = tuple_record.GetTransactionId();
  if (recovery_txn_table.find(txn_id) == recovery_txn_table.end()) {
    LOG_TRACE("Update txd id %d not found in recovery txn table", (int)txn_id);
    return;
  }

  auto txn = recovery_txn_table.at(txn_id);

  auto table = GetTable(tuple_record);

  auto tuple = ReadTupleRecordBody(table->GetSchema(), recovery_pool, log_file,
                                   log_file_size);

  // Check for torn log write
  if (tuple == nullptr) {
    return;
  }

  // First, redo the delete
  ItemPointer delete_location = tuple_record.GetDeleteLocation();

  bool status = table->DeleteTuple(recovery_txn, delete_location);
  if (status == false) {
    recovery_txn->SetResult(Result::RESULT_FAILURE);
  } else {
    txn->RecordDelete(delete_location);

    auto target_location = tuple_record.GetInsertLocation();
    auto tile_group_id = target_location.block;
    auto tuple_slot = target_location.offset;
    auto &manager = catalog::Manager::GetInstance();
    auto tile_group = manager.GetTileGroup(tile_group_id);

    // Create new tile group if table doesn't already have that tile group
    if (tile_group == nullptr) {
      table->AddTileGroupWithOid(tile_group_id);
      tile_group = manager.GetTileGroup(tile_group_id);
      if (max_oid < tile_group_id) {
        max_oid = tile_group_id;
      }
    }

    // Do the insert !
    auto inserted_tuple_slot = tile_group->InsertTuple(
        recovery_txn->GetTransactionId(), tuple_slot, tuple);
    if (inserted_tuple_slot == INVALID_OID) {
      recovery_txn->SetResult(Result::RESULT_FAILURE);
    } else {
      txn->RecordInsert(target_location);
    }
  }

  delete tuple;
}
Exemplo n.º 3
0
/**
 * @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);
  }
}
Exemplo n.º 4
0
TEST_F(GarbageCollectionTests, DeleteTest) {
  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("DELETE_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, "DELETE_TABLE", db_id, INVALID_OID, 1234, true));

  EXPECT_TRUE(gc_manager.GetTableCount() == 1);

  gc_manager.StartGC(gc_threads);

  const int delete_num = 1;
  DeleteTuple(table.get(), delete_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 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);

  gc_manager.StopGC();
  gc::GCManagerFactory::Configure(0);

  table.release();

  // DROP!
  TestingExecutorUtil::DeleteDatabase("DELETE_DB");
  auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance();
  auto txn = txn_manager.BeginTransaction();
  EXPECT_THROW(
      catalog::Catalog::GetInstance()->GetDatabaseObject("DATABASE0", txn),
      CatalogException);
  txn_manager.CommitTransaction(txn);
  // EXPECT_FALSE(storage_manager->HasDatabase(db_id));

  for (auto &gc_thread : gc_threads) {
    gc_thread->join();
  }
}