-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
MDEV-39468 Mariabackup: redo log copier stalls and fails with misleading error message #5375
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 10.11
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -205,7 +205,7 @@ struct xb_filter_entry_t{ | |
| /** whether log_copying_thread() is active; protected by recv_sys.mutex */ | ||
| static bool log_copying_running; | ||
| /** the log parsing function for --backup */ | ||
| static recv_sys_t::parser backup_log_parse; | ||
| static recv_sys_t::parser backup_log_parse_low; | ||
| /** for --backup, target LSN to copy the log to; protected by recv_sys.mutex */ | ||
| lsn_t metadata_to_lsn; | ||
|
|
||
|
|
@@ -3443,6 +3443,39 @@ static my_bool xtrabackup_copy_datafile(ds_ctxt *ds_data, | |
| return(FALSE); | ||
| } | ||
|
|
||
| /* Maximum LSN the copier may parse up to, derived from the | ||
| server's durably-flushed redo LSN. The copier refuses to | ||
| accept a mini-transaction whose end exceeds this, so it never | ||
| parses the volatile tail block the server is still rewriting | ||
| (which mmap can read torn, leading to a mis-computed mtr | ||
|
Thirunarayanan marked this conversation as resolved.
|
||
| length and permanent mid-mtr drift). A pread() of the same | ||
| still-being-written tail block can likewise return a torn read | ||
| which can be observed on ext4, though not on XFS). | ||
| Read/written only under recv_sys.mutex. */ | ||
|
Comment on lines
+3446
to
+3454
|
||
| static lsn_t max_parse_lsn; | ||
|
|
||
| /* Set by backup_log_parse() when the last parse was | ||
| rejected because the mtr crossed max_parse_lsn. It means | ||
| "caught up, wait" condition, not a stall. */ | ||
| static bool reached_parse_limit; | ||
|
|
||
| static recv_sys_t::parse_mtr_result backup_log_parse() | ||
| { | ||
| const lsn_t limit_lsn= max_parse_lsn; | ||
| const lsn_t prev_lsn= recv_sys.lsn; | ||
| const size_t prev_offset= recv_sys.offset; | ||
| reached_parse_limit= false; | ||
| recv_sys_t::parse_mtr_result r= backup_log_parse_low(false); | ||
| if (r == recv_sys_t::OK && limit_lsn && recv_sys.lsn > limit_lsn) | ||
| { | ||
| recv_sys.lsn= prev_lsn; | ||
| recv_sys.offset= prev_offset; | ||
| reached_parse_limit= true; | ||
| return recv_sys_t::GOT_EOF; | ||
| } | ||
| return r; | ||
| } | ||
|
|
||
| static int | ||
| xtrabackup_copy_mmap_snippet(ds_file_t *ds, const byte *start, const byte *end) | ||
| { | ||
|
|
@@ -3468,7 +3501,7 @@ static bool xtrabackup_copy_mmap_logfile() | |
| const byte *start= &log_sys.buf[recv_sys.offset]; | ||
| ut_d(recv_sys_t::parse_mtr_result r); | ||
|
|
||
| if ((ut_d(r=) backup_log_parse(false)) == recv_sys_t::OK) | ||
| if ((ut_d(r=) backup_log_parse()) == recv_sys_t::OK) | ||
| { | ||
| do | ||
| { | ||
|
|
@@ -3486,7 +3519,7 @@ static bool xtrabackup_copy_mmap_logfile() | |
| start = seq + 1; | ||
| } | ||
| } | ||
| while ((ut_d(r=) backup_log_parse(false)) == recv_sys_t::OK); | ||
| while ((ut_d(r=) backup_log_parse()) == recv_sys_t::OK); | ||
|
|
||
| if (xtrabackup_copy_mmap_snippet(dst_log_file, start, | ||
| &log_sys.buf[recv_sys.offset])) | ||
|
|
@@ -3559,7 +3592,7 @@ static bool xtrabackup_copy_logfile(bool early_exit) | |
| if (log_sys.buf[recv_sys.offset] <= 1) | ||
| break; | ||
|
|
||
| if (backup_log_parse(false) == recv_sys_t::OK) | ||
| if (backup_log_parse() == recv_sys_t::OK) | ||
| { | ||
| do | ||
| { | ||
|
|
@@ -3569,7 +3602,7 @@ static bool xtrabackup_copy_logfile(bool early_exit) | |
| sequence_offset)); | ||
| *seq= 1; | ||
| } | ||
| while ((r= backup_log_parse(false)) == recv_sys_t::OK); | ||
| while ((r= backup_log_parse()) == recv_sys_t::OK); | ||
|
|
||
| if (ds_write(dst_log_file, log_sys.buf + start_offset, | ||
| recv_sys.offset - start_offset)) | ||
|
|
@@ -3599,6 +3632,9 @@ static bool xtrabackup_copy_logfile(bool early_exit) | |
| if (retry_count == 100) | ||
| break; | ||
|
|
||
| if (reached_parse_limit) | ||
| break; | ||
|
|
||
| mysql_mutex_unlock(&recv_sys.mutex); | ||
| if (!retry_count++) | ||
| msg("Retrying read of log at LSN=" LSN_PF, recv_sys.lsn); | ||
|
|
@@ -3617,9 +3653,20 @@ static bool backup_wait_timeout(lsn_t lsn, lsn_t last_lsn) | |
| { | ||
| if (last_lsn >= lsn) | ||
| return true; | ||
|
|
||
| const lsn_t checkpoint_lsn= log_sys.last_checkpoint_lsn.load(); | ||
| const lsn_t capacity= log_sys.file_size - log_sys.START_OFFSET; | ||
| const lsn_t needed= lsn - checkpoint_lsn; | ||
|
|
||
| msg("Was only able to copy log from " LSN_PF " to " LSN_PF | ||
| ", not " LSN_PF "; try increasing innodb_log_file_size", | ||
| log_sys.last_checkpoint_lsn.load(), last_lsn, lsn); | ||
| ", not " LSN_PF, checkpoint_lsn, last_lsn, lsn); | ||
|
|
||
| if (needed <= capacity) | ||
| msg("mariabackup: The required redo still fits within the log " | ||
| "capacity, so it has not been overwritten; check whether the " | ||
| "server and backup configuration are the same."); | ||
| else | ||
| msg("mariabackup: Try increasing the innodb_log_file_size."); | ||
| return false; | ||
| } | ||
|
|
||
|
|
@@ -3674,16 +3721,56 @@ static bool backup_wait_for_lsn(lsn_t lsn) | |
| static void log_copying_thread() | ||
| { | ||
| my_thread_init(); | ||
| MYSQL *limit_con= xb_mysql_connect(); | ||
|
Thirunarayanan marked this conversation as resolved.
|
||
| if (!limit_con) | ||
| { | ||
| /* Without this connection we cannot poll the durably-flushed | ||
| LSN, so max_parse_lsn could never advance and the copier | ||
| would either stall or parse the volatile tail block. | ||
| Fail the copy gracefully and let the main thread report | ||
| it via backup_wait_timeout(). */ | ||
| msg("mariabackup: Error: cannot open a server connection for " | ||
| "the log copying thread; the backup will fail."); | ||
| mysql_mutex_lock(&recv_sys.mutex); | ||
| log_copying_running= false; | ||
| pthread_cond_broadcast(&scanned_lsn_cond); | ||
| mysql_mutex_unlock(&recv_sys.mutex); | ||
| my_thread_end(); | ||
| return; | ||
| } | ||
|
|
||
| mysql_mutex_lock(&recv_sys.mutex); | ||
|
iMineLink marked this conversation as resolved.
|
||
| while (!xtrabackup_copy_logfile(false) && | ||
| (!metadata_last_lsn || metadata_last_lsn > recv_sys.lsn)) | ||
| for (;;) | ||
| { | ||
| if (metadata_last_lsn == 1) | ||
| break; | ||
| /* Refresh the max_parse_lsn before each copy pass */ | ||
| const lsn_t final_target= metadata_last_lsn > metadata_to_lsn | ||
| ? metadata_last_lsn : metadata_to_lsn; | ||
| if (final_target) | ||
| max_parse_lsn= final_target; | ||
| else | ||
| { | ||
| mysql_mutex_unlock(&recv_sys.mutex); | ||
| lsn_t flushed= get_log_flushed_lsn(limit_con); | ||
| mysql_mutex_lock(&recv_sys.mutex); | ||
| if (flushed > log_sys.get_first_lsn()) | ||
| max_parse_lsn= flushed; | ||
| } | ||
|
iMineLink marked this conversation as resolved.
|
||
|
|
||
| if (xtrabackup_copy_logfile(false)) | ||
| break; | ||
| if (final_target && final_target <= recv_sys.lsn) | ||
| break; | ||
|
Comment on lines
+3763
to
+3764
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If To avoid this unnecessary 1-second delay during backup completion, we should check if (final_target && final_target <= recv_sys.lsn)
break;
if (metadata_last_lsn == 1)
break;
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not a correctness issue anyway, and I understand |
||
|
|
||
| timespec abstime; | ||
| set_timespec_nsec(abstime, 1000000ULL * xtrabackup_log_copy_interval); | ||
| mysql_cond_timedwait(&log_copying_stop, &recv_sys.mutex, &abstime); | ||
| } | ||
| log_copying_running= false; | ||
| mysql_mutex_unlock(&recv_sys.mutex); | ||
| if (limit_con) | ||
| mysql_close(limit_con); | ||
| my_thread_end(); | ||
| } | ||
|
iMineLink marked this conversation as resolved.
|
||
|
|
||
|
|
@@ -5614,9 +5701,15 @@ static bool xtrabackup_backup_func() | |
| /* copy log file by current position */ | ||
|
|
||
| mysql_mutex_lock(&recv_sys.mutex); | ||
| backup_log_parse = recv_sys.get_backup_parser(); | ||
| backup_log_parse_low = recv_sys.get_backup_parser(); | ||
| recv_sys.lsn = log_sys.last_checkpoint_lsn; | ||
|
|
||
| if (lsn_t flushed= get_log_flushed_lsn(mysql_connection)) | ||
| { | ||
| if (flushed > log_sys.get_first_lsn()) | ||
| max_parse_lsn= flushed; | ||
| } | ||
|
|
||
| const bool log_copy_failed = xtrabackup_copy_logfile(true); | ||
|
|
||
| mysql_mutex_unlock(&recv_sys.mutex); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| CREATE TABLE t ENGINE=INNODB SELECT seq%10 i FROM seq_0_to_204796; | ||
| # xtrabackup backup | ||
| FOUND 1 /Was only able to copy log from \d+ to \d+, not \d+; try increasing innodb_log_file_size\b/ in backup.log | ||
| FOUND 1 /mariabackup: Try increasing the innodb_log_file_size./ in backup.log | ||
|
iMineLink marked this conversation as resolved.
|
||
| NOT FOUND /failed: redo log block checksum does not match/ in backup.log | ||
| DROP TABLE t; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is relevant