mariadb/extra/mariabackup/write_filt.cc
Thirunarayanan Balathandayuthapani baf276e6d4 MDEV-19229 Allow innodb_undo_tablespaces to be changed after database creation
trx_sys_t::undo_log_nonempty: Set to true if there are undo logs
to rollback and purge.

The algorithm for re-creating the undo tablespace when
trx_sys_t::undo_log_nonempty is disabled:

1) trx_sys_t::reset_page(): Reset the TRX_SYS page and assign all
rollback segment slots from 1..127 to FIL_NULL

2) Free the rollback segment header page of system tablespace
for the slots 1..127

3) Update the binlog and WSREP information in system tablespace
rollback segment header
Step (1), (2) and Step (3) should happen atomically within a
single mini-transaction.

4) srv_undo_delete_old_tablespaces(): Delete the old undo tablespaces
present in the undo log directory

5) Make checkpoint to get rid of old undo log tablespaces redo logs

6) Assign new start space id for the undo log tablespaces

7) Re-create the specified undo log tablespaces. InnoDB uses same
mtr for this one and step (6)

8) Make checkpoint again, so that server or mariabackup
can read the undo log tablespace page0 before applying
the redo logs

srv_undo_tablespaces_reinit(): Recreate the undo log tablespaces.
It does reset trx_sys page, delete the old undo tablespaces,
update the binlog offset, write set replication checkpoint
in system rollback segment page

trx_rseg_update_binlog_offset(): Added 2 new parameters to pass
binlog file name and binlog offset

trx_rseg_array_init(): Return error if the rollback segment
slot points to non-existent tablespace

srv_undo_tablespaces_init(): Added new parameter mtr
to initialize all undo tablespaces

trx_assign_rseg_low(): Allow the transaction to use the rollback
segment slots(1..127) even if InnoDB failed to change to the
requested innodb_undo_tablespaces=0

srv_start(): Override the user specified value of
innodb_undo_tablespaces variable with already existing actual
undo tablespaces

wf_incremental_process(): Detects whether TRX_SYS page has been
modified since last backup. If it is then incremental backup
fails and throws the information about taking full backup again

xb_assign_undo_space_start(): Removed the function. Because
undo001 has first undo space id value in page0

Added test case to test the scenario during startup and mariabackup
incremental process too.

Reviewed-by : Marko Mäkelä
Tested-by : Matthias Leich
2022-10-25 11:19:36 +05:30

232 lines
6.9 KiB
C++

/******************************************************
MariaBackup: hot backup tool for InnoDB
(c) 2009-2013 Percona LLC and/or its affiliates.
Originally Created 3/3/2009 Yasufumi Kinoshita
Written by Alexey Kopytov, Aleksandr Kuzminsky, Stewart Smith, Vadim Tkachenko,
Yasufumi Kinoshita, Ignacio Nin and Baron Schwartz.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
*******************************************************/
/* Page write filters implementation */
#include <my_global.h>
#include <my_base.h>
#include "common.h"
#include "write_filt.h"
#include "fil_cur.h"
#include "xtrabackup.h"
/************************************************************************
Write-through page write filter. */
static my_bool wf_wt_init(xb_write_filt_ctxt_t *ctxt, char *dst_name,
xb_fil_cur_t *cursor, CorruptedPages *corrupted_pages);
static my_bool wf_wt_process(xb_write_filt_ctxt_t *ctxt, ds_file_t *dstfile);
xb_write_filt_t wf_write_through = {
&wf_wt_init,
&wf_wt_process,
NULL,
NULL
};
/************************************************************************
Incremental page write filter. */
static my_bool wf_incremental_init(xb_write_filt_ctxt_t *ctxt, char *dst_name,
xb_fil_cur_t *cursor, CorruptedPages *corrupted_pages);
static my_bool wf_incremental_process(xb_write_filt_ctxt_t *ctxt,
ds_file_t *dstfile);
static my_bool wf_incremental_finalize(xb_write_filt_ctxt_t *ctxt,
ds_file_t *dstfile);
static void wf_incremental_deinit(xb_write_filt_ctxt_t *ctxt);
xb_write_filt_t wf_incremental = {
&wf_incremental_init,
&wf_incremental_process,
&wf_incremental_finalize,
&wf_incremental_deinit
};
/************************************************************************
Initialize incremental page write filter.
@return TRUE on success, FALSE on error. */
static my_bool
wf_incremental_init(xb_write_filt_ctxt_t *ctxt, char *dst_name,
xb_fil_cur_t *cursor, CorruptedPages *corrupted_pages)
{
char meta_name[FN_REFLEN];
xb_wf_incremental_ctxt_t *cp =
&(ctxt->wf_incremental_ctxt);
ctxt->cursor = cursor;
/* allocate buffer for incremental backup (4096 pages) */
cp->delta_buf_size = (cursor->page_size / 4) * cursor->page_size;
cp->delta_buf = (unsigned char *)my_large_malloc(&cp->delta_buf_size, MYF(0));
if (!cp->delta_buf) {
msg(cursor->thread_n,"Can't allocate %zu bytes",
(size_t) cp->delta_buf_size);
return (FALSE);
}
/* write delta meta info */
snprintf(meta_name, sizeof(meta_name), "%s%s", dst_name,
XB_DELTA_INFO_SUFFIX);
const xb_delta_info_t info(cursor->page_size, cursor->zip_size,
cursor->space_id);
if (!xb_write_delta_metadata(meta_name, &info)) {
msg(cursor->thread_n,"Error: "
"failed to write meta info for %s",
cursor->rel_path);
return(FALSE);
}
/* change the target file name, since we are only going to write
delta pages */
strcat(dst_name, ".delta");
mach_write_to_4(cp->delta_buf, 0x78747261UL); /*"xtra"*/
cp->npages = 1;
cp->corrupted_pages = corrupted_pages;
return(TRUE);
}
/************************************************************************
Run the next batch of pages through incremental page write filter.
@return TRUE on success, FALSE on error. */
static my_bool
wf_incremental_process(xb_write_filt_ctxt_t *ctxt, ds_file_t *dstfile)
{
unsigned i;
xb_fil_cur_t *cursor = ctxt->cursor;
byte *page;
const ulint page_size = cursor->page_size;
xb_wf_incremental_ctxt_t *cp = &(ctxt->wf_incremental_ctxt);
for (i = 0, page = cursor->buf; i < cursor->buf_npages;
i++, page += page_size) {
if ((!cp->corrupted_pages ||
!cp->corrupted_pages->contains({cursor->node->space->id,
cursor->buf_page_no + i})) &&
incremental_lsn >= mach_read_from_8(page + FIL_PAGE_LSN))
continue;
/* Check whether TRX_SYS page has been changed */
if (mach_read_from_4(page + FIL_PAGE_SPACE_ID)
== TRX_SYS_SPACE
&& mach_read_from_4(page + FIL_PAGE_OFFSET)
== TRX_SYS_PAGE_NO) {
msg(cursor->thread_n,
"--incremental backup is impossible if "
"the server had been restarted with "
"different innodb_undo_tablespaces.");
return false;
}
/* updated page */
if (cp->npages == page_size / 4) {
/* flush buffer */
if (ds_write(dstfile, cp->delta_buf,
cp->npages * page_size)) {
return(FALSE);
}
/* clear buffer */
memset(cp->delta_buf, 0, page_size / 4 * page_size);
/*"xtra"*/
mach_write_to_4(cp->delta_buf, 0x78747261UL);
cp->npages = 1;
}
mach_write_to_4(cp->delta_buf + cp->npages * 4,
cursor->buf_page_no + i);
memcpy(cp->delta_buf + cp->npages * page_size, page,
page_size);
cp->npages++;
}
return(TRUE);
}
/************************************************************************
Flush the incremental page write filter's buffer.
@return TRUE on success, FALSE on error. */
static my_bool
wf_incremental_finalize(xb_write_filt_ctxt_t *ctxt, ds_file_t *dstfile)
{
xb_fil_cur_t *cursor = ctxt->cursor;
const ulint page_size = cursor->page_size;
xb_wf_incremental_ctxt_t *cp = &(ctxt->wf_incremental_ctxt);
if (cp->npages != page_size / 4) {
mach_write_to_4(cp->delta_buf + cp->npages * 4, 0xFFFFFFFFUL);
}
/* Mark the final block */
mach_write_to_4(cp->delta_buf, 0x58545241UL); /*"XTRA"*/
/* flush buffer */
if (ds_write(dstfile, cp->delta_buf, cp->npages * page_size)) {
return(FALSE);
}
return(TRUE);
}
/************************************************************************
Free the incremental page write filter's buffer. */
static void
wf_incremental_deinit(xb_write_filt_ctxt_t *ctxt)
{
xb_wf_incremental_ctxt_t *cp = &(ctxt->wf_incremental_ctxt);
my_large_free(cp->delta_buf, cp->delta_buf_size);
}
/************************************************************************
Initialize the write-through page write filter.
@return TRUE on success, FALSE on error. */
static my_bool
wf_wt_init(xb_write_filt_ctxt_t *ctxt, char *dst_name __attribute__((unused)),
xb_fil_cur_t *cursor, CorruptedPages *)
{
ctxt->cursor = cursor;
return(TRUE);
}
/************************************************************************
Write the next batch of pages to the destination datasink.
@return TRUE on success, FALSE on error. */
static my_bool
wf_wt_process(xb_write_filt_ctxt_t *ctxt, ds_file_t *dstfile)
{
xb_fil_cur_t *cursor = ctxt->cursor;
if (ds_write(dstfile, cursor->buf, cursor->buf_read)) {
return(FALSE);
}
return(TRUE);
}