// Check whether need to recovery, if yes, reset fseek to the right place. bool WriteBehindFrontendLogger::NeedRecovery(void) { // Otherwise, read the last transaction record fseek(log_file, -TransactionRecord::GetTransactionRecordSize(), SEEK_END); // Get its type auto log_record_type = GetNextLogRecordType(log_file, log_file_size); // Check if the previous transaction run is broken if (log_record_type == LOGRECORD_TYPE_TRANSACTION_COMMIT) { TransactionRecord txn_record(LOGRECORD_TYPE_TRANSACTION_COMMIT); // read the last written out transaction log record if (ReadTransactionRecordHeader(txn_record, log_file, log_file_size) == false) { return false; } // Peloton log records items have fixed size. // Compute log offset based on txn_id size_t tuple_log_record_count = txn_record.GetTransactionId(); size_t rollback_offset = tuple_log_record_count * TupleRecord::GetTupleRecordSize() + TransactionRecord::GetTransactionRecordSize(); // Rollback to the computed offset fseek(log_file, -rollback_offset, SEEK_END); return true; } else { return false; } }
/** * @brief Add new txn to recovery table */ void AriesFrontendLogger::AddTransactionToRecoveryTable() { // read transaction information from the log file TransactionRecord txn_record(LOGRECORD_TYPE_TRANSACTION_BEGIN); // Check for torn log write if (ReadTransactionRecordHeader(txn_record, log_file, log_file_size) == false) { return; } auto txn_id = txn_record.GetTransactionId(); // create the new txn object and added it into recovery recovery_txn_table concurrency::Transaction *txn = new concurrency::Transaction(txn_id, INVALID_CID); recovery_txn_table.insert(std::make_pair(txn_id, txn)); LOG_TRACE("Added txd id %d object in table", (int)txn_id); }
/** * @brief abort tuple */ void AriesFrontendLogger::AbortTuplesFromRecoveryTable() { // read transaction information from the log file TransactionRecord txn_record(LOGRECORD_TYPE_TRANSACTION_ABORT); // Check for torn log write if (ReadTransactionRecordHeader(txn_record, log_file, log_file_size) == false) { return; } auto txn_id = txn_record.GetTransactionId(); // Get info about the transaction from recovery table if (recovery_txn_table.find(txn_id) != recovery_txn_table.end()) { auto txn = recovery_txn_table.at(txn_id); AbortTuples(txn); LOG_INFO("Abort txd id %d object in table", (int)txn_id); } else { LOG_INFO("Abort txd id %d not found in recovery txn table", (int)txn_id); } }
/** * @brief move tuples from current txn to recovery txn so that we can commit * them later * @param recovery txn */ void AriesFrontendLogger::MoveCommittedTuplesToRecoveryTxn( concurrency::Transaction *recovery_txn) { // read transaction information from the log file TransactionRecord txn_record(LOGRECORD_TYPE_TRANSACTION_COMMIT); // Check for torn log write if (ReadTransactionRecordHeader(txn_record, log_file, log_file_size) == false) { return; } // Get info about the transaction from recovery table auto txn_id = txn_record.GetTransactionId(); if (recovery_txn_table.find(txn_id) != recovery_txn_table.end()) { auto txn = recovery_txn_table.at(txn_id); // Copy inserted/deleted tuples to recovery transaction MoveTuples(recovery_txn, txn); LOG_TRACE("Commit txd id %d object in table", (int)txn_id); } else { LOG_TRACE("Commit txd id %d not found in recovery txn table", (int)txn_id); } }
/** * @brief Remove txn from recovery table */ void AriesFrontendLogger::RemoveTransactionFromRecoveryTable() { // read transaction information from the log file TransactionRecord txn_record(LOGRECORD_TYPE_TRANSACTION_END); // Check for torn log write if (ReadTransactionRecordHeader(txn_record, log_file, log_file_size) == false) { return; } auto txn_id = txn_record.GetTransactionId(); // remove txn from recovery txn table if (recovery_txn_table.find(txn_id) != recovery_txn_table.end()) { auto txn = recovery_txn_table.at(txn_id); recovery_txn_table.erase(txn_id); // drop the txn as well delete txn; LOG_TRACE("Erase txd id %d object in table", (int)txn_id); } else { LOG_TRACE("Erase txd id %d not found in recovery txn table", (int)txn_id); } }
/** * @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); } }