diff options
Diffstat (limited to 'innobase/log/log0recv.c')
-rw-r--r-- | innobase/log/log0recv.c | 249 |
1 files changed, 161 insertions, 88 deletions
diff --git a/innobase/log/log0recv.c b/innobase/log/log0recv.c index 53f75c176ea..1223f9b6041 100644 --- a/innobase/log/log0recv.c +++ b/innobase/log/log0recv.c @@ -58,12 +58,16 @@ yet: the variable name is misleading */ ibool recv_no_ibuf_operations = FALSE; -/* the following counter is used to decide when to print info on +/* The following counter is used to decide when to print info on log scan */ ulint recv_scan_print_counter = 0; ibool recv_is_from_backup = FALSE; +ibool recv_is_making_a_backup = FALSE; +ulint recv_previous_parsed_rec_type = 999999; +ulint recv_previous_parsed_rec_offset = 0; +ulint recv_previous_parsed_rec_is_multi = 0; /************************************************************ Creates the recovery system. */ @@ -124,6 +128,8 @@ recv_sys_init( recv_sys->last_block = ut_align(recv_sys->last_block_buf_start, OS_FILE_LOG_BLOCK_SIZE); + recv_sys->found_corrupt_log = FALSE; + mutex_exit(&(recv_sys->mutex)); } @@ -569,9 +575,9 @@ recv_read_cp_info_for_backup( } /********************************************************** -Checks the 1-byte checksum to the trailer checksum field of a log block. -We also accept a log block in the old format where the checksum field -contained the highest byte of the log block number. */ +Checks the 4-byte checksum to the trailer checksum field of a log block. +We also accept a log block in the old format < InnoDB-3.23.52 where the +checksum field contains the log block number. */ static ibool log_block_checksum_is_ok_or_old_format( @@ -580,29 +586,12 @@ log_block_checksum_is_ok_or_old_format( format of InnoDB version < 3.23.52 */ byte* block) /* in: pointer to a log block */ { - ulint i; - ulint sum; - - sum = 1; - - for (i = 0; i < OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_TRL_SIZE; i++) { - sum += (ulint)(*(block + i)); - } - -/* printf("Checksum %lu, byte %lu\n", 0xFF & sum, - mach_read_from_1(block + OS_FILE_LOG_BLOCK_SIZE - - LOG_BLOCK_TRL_CHECKSUM)); -*/ - if (mach_read_from_1(block + OS_FILE_LOG_BLOCK_SIZE - - LOG_BLOCK_TRL_CHECKSUM) - == (0xFF & sum)) { + if (log_block_calc_checksum(block) == log_block_get_checksum(block)) { return(TRUE); } - if (((0xFF000000 & log_block_get_hdr_no(block)) >> 24) - == mach_read_from_1(block + OS_FILE_LOG_BLOCK_SIZE - - LOG_BLOCK_TRL_CHECKSUM)) { + if (log_block_get_hdr_no(block) == log_block_get_checksum(block)) { /* We assume the log block is in the format of InnoDB version < 3.23.52 and the block is ok */ @@ -649,23 +638,20 @@ recv_scan_log_seg_for_backup( /* fprintf(stderr, "Log block header no %lu\n", no); */ - if ((no & 0xFFFFFF) != log_block_get_trl_no(log_block) - || no != log_block_convert_lsn_to_no(*scanned_lsn) + if (no != log_block_convert_lsn_to_no(*scanned_lsn) || !log_block_checksum_is_ok_or_old_format(log_block)) { /* printf( -"Log block n:o %lu, trailer n:o %lu, scanned lsn n:o %lu\n", - no, log_block_get_trl_no(log_block), - log_block_convert_lsn_to_no(*scanned_lsn)); +"Log block n:o %lu, scanned lsn n:o %lu\n", + no, log_block_convert_lsn_to_no(*scanned_lsn)); */ /* Garbage or an incompletely written log block */ log_block += OS_FILE_LOG_BLOCK_SIZE; /* printf( -"Next log block n:o %lu, trailer n:o %lu\n", - log_block_get_hdr_no(log_block), - log_block_get_trl_no(log_block)); +"Next log block n:o %lu\n", + log_block_get_hdr_no(log_block)); */ break; } @@ -788,14 +774,8 @@ recv_parse_or_apply_log_rec_body( new_ptr = mlog_parse_string(ptr, end_ptr, page); } else { new_ptr = NULL; - - fprintf(stderr, - "InnoDB: WARNING: the log file may have been corrupt and it\n" - "InnoDB: is possible that the log scan did not proceed\n" - "InnoDB: far enough in recovery. Please run CHECK TABLE\n" - "InnoDB: on your InnoDB tables to check that they are ok!\n" - "InnoDB: Corrupt log record type %lu\n", - (ulint)type); + + recv_sys->found_corrupt_log = TRUE; } ut_ad(!page || new_ptr); @@ -1399,18 +1379,30 @@ recv_apply_log_recs_for_backup( OS_FILE_OPEN, OS_FILE_READ_WRITE, &success); - ut_a(success); + if (!success) { + printf( +"InnoDB: Error: cannot open %lu'th data file %s\n", nth_file); + + exit(1); + } } recv_addr = recv_get_fil_addr_struct(0, i); if (recv_addr != NULL) { - os_file_read(data_file, page, + success = os_file_read(data_file, page, (nth_page_in_file << UNIV_PAGE_SIZE_SHIFT) & 0xFFFFFFFF, nth_page_in_file >> (32 - UNIV_PAGE_SIZE_SHIFT), UNIV_PAGE_SIZE); + if (!success) { + printf( +"InnoDB: Error: cannot read page no %lu from %lu'th data file %s\n", + nth_page_in_file, nth_file); + exit(1); + } + /* We simulate a page read made by the buffer pool, to make sure recovery works ok. We must init the block corresponding to buf_pool->frame_zero @@ -1425,12 +1417,19 @@ recv_apply_log_recs_for_backup( mach_read_from_8(page + FIL_PAGE_LSN), 0, i); - os_file_write(data_files[nth_file], + success = os_file_write(data_files[nth_file], data_file, page, (nth_page_in_file << UNIV_PAGE_SIZE_SHIFT) & 0xFFFFFFFF, nth_page_in_file >> (32 - UNIV_PAGE_SIZE_SHIFT), UNIV_PAGE_SIZE); + if (!success) { + printf( +"InnoDB: Error: cannot write page no %lu to %lu'th data file %s\n", + nth_page_in_file, nth_file); + + exit(1); + } } if ((100 * i) / n_pages_total @@ -1679,29 +1678,16 @@ recv_parse_log_rec( new_ptr = mlog_parse_initial_log_record(ptr, end_ptr, type, space, page_no); - - /* If the operating system writes to the log complete 512-byte - blocks, we should not get the warnings below in recovery. - A warning means that the header and the trailer appeared ok - in a 512-byte block, but in the middle there was something wrong. - TODO: (1) add similar warnings in the case there is an incompletely - written log record which does not extend to the boundary of a - 512-byte block. (2) Add a checksum to a log block. */ - if (!new_ptr) { + return(0); } /* Check that space id and page_no are sensible */ if (*space != 0 || *page_no > 0x8FFFFFFF) { - fprintf(stderr, - "InnoDB: WARNING: the log file may have been corrupt and it\n" - "InnoDB: is possible that the log scan did not proceed\n" - "InnoDB: far enough in recovery. Please run CHECK TABLE\n" - "InnoDB: on your InnoDB tables to check that they are ok!\n" - "InnoDB: Corrupt log record type %lu, space id %lu, page no %lu\n", - (ulint)(*type), *space, *page_no); + + recv_sys->found_corrupt_log = TRUE; return(0); } @@ -1766,14 +1752,70 @@ recv_check_incomplete_log_recs( } /*********************************************************** +Prints diagnostic info of corrupt log. */ +static +void +recv_report_corrupt_log( +/*====================*/ + byte* ptr, /* in: pointer to corrupt log record */ + byte type, /* in: type of the record */ + ulint space, /* in: space id, this may also be garbage */ + ulint page_no)/* in: page number, this may also be garbage */ +{ + char* err_buf; + + fprintf(stderr, +"InnoDB: ############### CORRUPT LOG RECORD FOUND\n" +"InnoDB: Log record type %lu, space id %lu, page number %lu\n" +"InnoDB: Log parsing proceeded successfully up to %lu %lu\n", + (ulint)type, space, page_no, + ut_dulint_get_high(recv_sys->recovered_lsn), + ut_dulint_get_low(recv_sys->recovered_lsn)); + + err_buf = ut_malloc(1000000); + + fprintf(stderr, +"InnoDB: Previous log record type %lu, is multi %lu\n" +"InnoDB: Recv offset %lu, prev %lu\n", + recv_previous_parsed_rec_type, + recv_previous_parsed_rec_is_multi, + ptr - recv_sys->buf, + recv_previous_parsed_rec_offset); + + if ((ulint)(ptr - recv_sys->buf + 100) + > recv_previous_parsed_rec_offset + && (ulint)(ptr - recv_sys->buf + 100 + - recv_previous_parsed_rec_offset) + < 200000) { + + ut_sprintf_buf(err_buf, + recv_sys->buf + recv_previous_parsed_rec_offset - 100, + ptr - recv_sys->buf + 200 - + recv_previous_parsed_rec_offset); + fprintf(stderr, +"InnoDB: Hex dump of corrupt log starting 100 bytes before the start\n" +"InnoDB: of the previous log rec,\n" +"InnoDB: and ending 100 bytes after the start of the corrupt rec:\n%s\n", + err_buf); + } + + ut_free(err_buf); + + fprintf(stderr, + "InnoDB: WARNING: the log file may have been corrupt and it\n" + "InnoDB: is possible that the log scan did not proceed\n" + "InnoDB: far enough in recovery! Please run CHECK TABLE\n" + "InnoDB: on your InnoDB tables to check that they are ok!\n"); +} + +/*********************************************************** Parses log records from a buffer and stores them to a hash table to wait merging to file pages. */ static ibool recv_parse_log_recs( /*================*/ - /* out: TRUE if the hash table of parsed log - records became full */ + /* out: currently always returns FALSE */ ibool store_to_hash) /* in: TRUE if the records should be stored to the hash table; this is set to FALSE if just debug checking is needed */ @@ -1812,8 +1854,13 @@ loop: len = recv_parse_log_rec(ptr, end_ptr, &type, &space, &page_no, &body); - if (len == 0) { + if (len == 0 || recv_sys->found_corrupt_log) { + if (recv_sys->found_corrupt_log) { + recv_report_corrupt_log(ptr, + type, space, page_no); + } + return(FALSE); } @@ -1828,6 +1875,10 @@ loop: return(FALSE); } + recv_previous_parsed_rec_type = (ulint)type; + recv_previous_parsed_rec_offset = recv_sys->recovered_offset; + recv_previous_parsed_rec_is_multi = 0; + recv_sys->recovered_offset += len; recv_sys->recovered_lsn = new_recovered_lsn; @@ -1851,9 +1902,10 @@ loop: #ifdef UNIV_LOG_DEBUG recv_check_incomplete_log_recs(ptr, len); #endif - recv_update_replicate(type, space, page_no, body, +/* recv_update_replicate(type, space, page_no, body, ptr + len); recv_compare_replicate(space, page_no); +*/ } } else { /* Check that all the records associated with the single mtr @@ -1865,19 +1917,32 @@ loop: for (;;) { len = recv_parse_log_rec(ptr, end_ptr, &type, &space, &page_no, &body); - if (len == 0) { + if (len == 0 || recv_sys->found_corrupt_log) { - return(FALSE); + if (recv_sys->found_corrupt_log) { + + recv_report_corrupt_log(ptr, + type, space, page_no); + } + + return(FALSE); } + recv_previous_parsed_rec_type = (ulint)type; + recv_previous_parsed_rec_offset + = recv_sys->recovered_offset + total_len; + recv_previous_parsed_rec_is_multi = 1; + if ((!store_to_hash) && (type != MLOG_MULTI_REC_END)) { /* In debug checking, update a replicate page according to the log record */ #ifdef UNIV_LOG_DEBUG recv_check_incomplete_log_recs(ptr, len); #endif +/* recv_update_replicate(type, space, page_no, body, ptr + len); +*/ } if (log_debug_writes) { @@ -1919,6 +1984,12 @@ loop: old_lsn = recv_sys->recovered_lsn; len = recv_parse_log_rec(ptr, end_ptr, &type, &space, &page_no, &body); + if (recv_sys->found_corrupt_log) { + + recv_report_corrupt_log(ptr, + type, space, page_no); + } + ut_a(len != 0); ut_a(0 == ((ulint)*ptr & MLOG_SINGLE_REC_FLAG)); @@ -1941,7 +2012,7 @@ loop: page has become identical with the original page */ - recv_compare_replicate(space, page_no); +/* recv_compare_replicate(space, page_no); */ } ptr += len; @@ -2095,32 +2166,19 @@ recv_scan_log_recs( /* fprintf(stderr, "Log block header no %lu\n", no); */ - if ((no & 0xFFFFFF) != log_block_get_trl_no(log_block) - || no != log_block_convert_lsn_to_no(scanned_lsn) + if (no != log_block_convert_lsn_to_no(scanned_lsn) || !log_block_checksum_is_ok_or_old_format(log_block)) { - if ((no & 0xFFFFFF) == log_block_get_trl_no(log_block) - && no == log_block_convert_lsn_to_no(scanned_lsn) + if (no == log_block_convert_lsn_to_no(scanned_lsn) && !log_block_checksum_is_ok_or_old_format( log_block)) { fprintf(stderr, "InnoDB: Log block no %lu at lsn %lu %lu has\n" -"InnoDB: ok header and trailer, but checksum field contains %lu\n", - no, ut_dulint_get_high(scanned_lsn), - ut_dulint_get_low(scanned_lsn), - mach_read_from_1(log_block - + OS_FILE_LOG_BLOCK_SIZE - - LOG_BLOCK_TRL_CHECKSUM)); - } - - if ((no & 0xFFFFFF) - != log_block_get_trl_no(log_block)) { - fprintf(stderr, -"InnoDB: Log block with header no %lu at lsn %lu %lu has\n" -"InnoDB: trailer no %lu\n", +"InnoDB: ok header, but checksum field contains %lu, should be %lu\n", no, ut_dulint_get_high(scanned_lsn), ut_dulint_get_low(scanned_lsn), - log_block_get_trl_no(log_block)); + log_block_get_checksum(log_block), + log_block_calc_checksum(log_block)); } /* Garbage or an incompletely written log block */ @@ -2192,11 +2250,14 @@ recv_scan_log_recs( >= RECV_PARSING_BUF_SIZE) { fprintf(stderr, "InnoDB: Error: log parsing buffer overflow. Recovery may have failed!\n"); - finished = TRUE; + + recv_sys->found_corrupt_log = TRUE; + + } else if (!recv_sys->found_corrupt_log) { + more_data = recv_sys_add_to_parsing_buf( + log_block, scanned_lsn); } - more_data = recv_sys_add_to_parsing_buf(log_block, - scanned_lsn); recv_sys->scanned_lsn = scanned_lsn; recv_sys->scanned_checkpoint_no = log_block_get_checkpoint_no(log_block); @@ -2213,7 +2274,8 @@ recv_scan_log_recs( *group_scanned_lsn = scanned_lsn; - if (recv_needed_recovery || recv_is_from_backup) { + if (recv_needed_recovery + || (recv_is_from_backup && !recv_is_making_a_backup)) { recv_scan_print_counter++; if (finished || (recv_scan_print_counter % 80 == 0)) { @@ -2225,7 +2287,7 @@ recv_scan_log_recs( } } - if (more_data) { + if (more_data && !recv_sys->found_corrupt_log) { /* Try to parse more log records */ recv_parse_log_recs(store_to_hash); @@ -2602,6 +2664,17 @@ recv_recovery_from_checkpoint_finish(void) trx_sys_print_mysql_binlog_offset(); } + if (recv_sys->found_corrupt_log) { + + fprintf(stderr, + "InnoDB: WARNING: the log file may have been corrupt and it\n" + "InnoDB: is possible that the log scan or parsing did not proceed\n" + "InnoDB: far enough in recovery. Please run CHECK TABLE\n" + "InnoDB: on your InnoDB tables to check that they are ok!\n" + "InnoDB: It may be safest to recover your InnoDB database from\n" + "InnoDB: a backup!\n"); + } + /* Free the resources of the recovery system */ recv_recovery_on = FALSE; |