/** * @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); }
/** * @brief read tuple record from log file and add them tuples to recovery txn * @param recovery txn */ void AriesFrontendLogger::InsertTuple(concurrency::Transaction *recovery_txn) { TupleRecord tuple_record(LOGRECORD_TYPE_ARIES_TUPLE_INSERT); // Check for torn log write if (ReadTupleRecordHeader(tuple_record, log_file, log_file_size) == false) { LOG_ERROR("Could not read tuple record header."); return; } auto txn_id = tuple_record.GetTransactionId(); if (recovery_txn_table.find(txn_id) == recovery_txn_table.end()) { LOG_ERROR("Insert txd id %d not found in recovery txn table", (int)txn_id); return; } auto table = GetTable(tuple_record); // Read off the tuple record body from the log auto tuple = ReadTupleRecordBody(table->GetSchema(), recovery_pool, log_file, log_file_size); // Check for torn log write if (tuple == nullptr) { return; } 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); auto txn = recovery_txn_table.at(txn_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) { // TODO: We need to abort on failure ! recovery_txn->SetResult(Result::RESULT_FAILURE); } else { txn->RecordInsert(target_location); table->IncreaseNumberOfTuplesBy(1); } delete tuple; }
/** * @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; }
/** * @brief Recovery system based on log file */ void WriteBehindFrontendLogger::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; // check whether first item is LOGRECORD_TYPE_TRANSACTION_COMMIT // if not, no need to do recovery. // if yes, need to replay all log records before we hit // LOGRECORD_TYPE_TRANSACTION_DONE bool need_recovery = NeedRecovery(); if (need_recovery == true) { TransactionRecord dummy_transaction_record(LOGRECORD_TYPE_INVALID); cid_t current_commit_id = INVALID_CID; // 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 LogRecordType log_type = GetNextLogRecordType(log_file, log_file_size); switch (log_type) { case LOGRECORD_TYPE_TRANSACTION_DONE: case LOGRECORD_TYPE_TRANSACTION_COMMIT: { // read but do nothing ReadTransactionRecordHeader(dummy_transaction_record, log_file, log_file_size); } break; case LOGRECORD_TYPE_WBL_TUPLE_INSERT: { TupleRecord insert_record(LOGRECORD_TYPE_WBL_TUPLE_INSERT); ReadTupleRecordHeader(insert_record, log_file, log_file_size); auto insert_location = insert_record.GetInsertLocation(); auto info = SetInsertCommitMark(insert_location); current_commit_id = info.first; } break; case LOGRECORD_TYPE_WBL_TUPLE_DELETE: { TupleRecord delete_record(LOGRECORD_TYPE_WBL_TUPLE_DELETE); ReadTupleRecordHeader(delete_record, log_file, log_file_size); auto delete_location = delete_record.GetDeleteLocation(); auto info = SetDeleteCommitMark(delete_location); current_commit_id = info.first; } break; case LOGRECORD_TYPE_WBL_TUPLE_UPDATE: { TupleRecord update_record(LOGRECORD_TYPE_WBL_TUPLE_UPDATE); ReadTupleRecordHeader(update_record, log_file, log_file_size); auto delete_location = update_record.GetDeleteLocation(); SetDeleteCommitMark(delete_location); auto insert_location = update_record.GetInsertLocation(); auto info = SetInsertCommitMark(insert_location); current_commit_id = info.first; } break; default: reached_end_of_file = true; break; } } // Update latest commit id if (latest_commit_id < current_commit_id) { latest_commit_id = current_commit_id; } // write out a trasaction done log record to file // to avoid redo next time during recovery WriteTransactionLogRecord( TransactionRecord(LOGRECORD_TYPE_TRANSACTION_DONE)); } // After finishing recovery, set the next oid with maximum oid // observed during the recovery auto &manager = catalog::Manager::GetInstance(); manager.SetNextOid(max_oid); } }