Bug#16752251 - INNODB DOESN'T REDO-LOG INSERT BUFFER MERGE OPERATION IF

IT IS DONE IN-PLACE

With change buffer enabled, InnoDB doesn't write a transaction log
record when it merges a record from the insert buffer to an secondary
index page if the insertion is performed as an update-in-place.

Fixed by logging the 'update-in-place' operation on secondary index
pages.

Approved by Marko. rb#2429
This commit is contained in:
Satya Bodapati 2013-09-11 16:57:02 +05:30
parent 4250117308
commit 59402fe0a7
8 changed files with 181 additions and 17 deletions

View File

@ -0,0 +1,6 @@
if (!`SHOW VARIABLES LIKE 'innodb_change_buffering_debug'`)
{
# innodb_change_buffering_debug is enabled by UNIV_DEBUG or
# UNIV_IBUF_DEBUG
--skip Test requires binary with UNIV_DEBUG enabled
}

View File

@ -0,0 +1,66 @@
# Purpose:
# Simple search with Perl for a pattern in some file.
#
# The advantages compared to thinkable auxiliary constructs using the
# mysqltest language and SQL are:
# 1. We do not need a running MySQL server.
# 2. SQL causes "noise" during debugging and increases the size of logs.
# Perl code does not disturb at all.
#
# The environment variables SEARCH_FILE and SEARCH_PATTERN must be set
# before sourcing this routine.
#
# In case of
# - SEARCH_FILE and/or SEARCH_PATTERN is not set
# - SEARCH_FILE cannot be opened
# - SEARCH_FILE does not contain SEARCH_PATTERN
# the test will abort immediate.
# MTR will report something like
# ....
# worker[1] Using MTR_BUILD_THREAD 300, with reserved ports 13000..13009
# main.1st [ pass ] 3
# innodb.innodb_page_size [ fail ]
# Test ended at 2011-11-11 18:15:58
#
# CURRENT_TEST: innodb.innodb_page_size
# # ERROR: The file '<name>' does not contain the expected pattern <pattern>
# mysqltest: In included file "./include/search_pattern_in_file.inc":
# included from ./include/search_pattern_in_file.inc at line 36:
# At line 25: command "perl" failed with error 255. my_errno=175
#
# The result from queries just before the failure was:
# ...
# - saving '<some path>' to '<some path>'
# main.1st [ pass ] 2
#
# Typical use case (check invalid server startup options):
# let $error_log= $MYSQLTEST_VARDIR/log/my_restart.err;
# --error 0,1
# --remove_file $error_log
# let SEARCH_FILE= $error_log;
# # Stop the server
# let $restart_file= $MYSQLTEST_VARDIR/tmp/mysqld.1.expect;
# --exec echo "wait" > $restart_file
# --shutdown_server 10
# --source include/wait_until_disconnected.inc
#
# --error 1
# --exec $MYSQLD_CMD <whatever wrong setting> > $error_log 2>&1
# # The server restart aborts
# let SEARCH_PATTERN= \[ERROR\] Aborting;
# --source include/search_pattern_in_file.inc
#
# Created: 2011-11-11 mleich
#
perl;
use strict;
my $search_file= $ENV{'SEARCH_FILE'} or die "SEARCH_FILE not set";
my $search_pattern= $ENV{'SEARCH_PATTERN'} or die "SEARCH_PATTERN not set";
open(FILE, "$search_file") or die("Unable to open '$search_file': $!\n");
read(FILE, my $file_content, 50000, 0);
close(FILE);
if ( not $file_content =~ m{$search_pattern} ) {
die("# ERROR: The file '$search_file' does not contain the expected pattern $search_pattern\n->$file_content<-\n");
}
EOF

View File

@ -31,6 +31,7 @@ Created 10/16/1994 Heikki Tuuri
#include "btr0sea.h" #include "btr0sea.h"
#include "row0upd.h" #include "row0upd.h"
#include "trx0rec.h" #include "trx0rec.h"
#include "trx0undo.h"
#include "trx0roll.h" /* trx_roll_crash_recv_trx */ #include "trx0roll.h" /* trx_roll_crash_recv_trx */
#include "que0que.h" #include "que0que.h"
#include "row0row.h" #include "row0row.h"
@ -1363,18 +1364,31 @@ btr_cur_update_in_place_log(
return; return;
} }
/* The code below assumes index is a clustered index: change index to /* For secondary indexes, we could skip writing the dummy system fields
the clustered index if we are updating a secondary index record (or we to the redo log but we have to change redo log parsing of
could as well skip writing the sys col values to the log in this case MLOG_REC_UPDATE_IN_PLACE/MLOG_COMP_REC_UPDATE_IN_PLACE or we have to add
because they are not needed for a secondary index record update) */ new redo log record. For now, just write dummy sys fields to the redo
log if we are updating a secondary index record.
index = dict_table_get_first_index(index->table); */
mach_write_to_1(log_ptr, flags); mach_write_to_1(log_ptr, flags);
log_ptr++; log_ptr++;
log_ptr = row_upd_write_sys_vals_to_log(index, trx, roll_ptr, log_ptr, if (index->type & DICT_CLUSTERED) {
mtr); log_ptr = row_upd_write_sys_vals_to_log(
index, trx, roll_ptr, log_ptr, mtr);
} else {
/* Dummy system fields for a secondary index */
/* TRX_ID Position */
log_ptr += mach_write_compressed(log_ptr, 0);
/* ROLL_PTR */
trx_write_roll_ptr(log_ptr, ut_dulint_zero);
log_ptr += DATA_ROLL_PTR_LEN;
/* TRX_ID */
log_ptr += mach_dulint_write_compressed(log_ptr,
ut_dulint_zero);
}
mach_write_to_2(log_ptr, page_offset(rec)); mach_write_to_2(log_ptr, page_offset(rec));
log_ptr += 2; log_ptr += 2;

View File

@ -3024,6 +3024,24 @@ updated_in_place:
/* This is the easy case. Do something similar /* This is the easy case. Do something similar
to btr_cur_update_in_place(). */ to btr_cur_update_in_place(). */
row_upd_rec_in_place(rec, offsets, update); row_upd_rec_in_place(rec, offsets, update);
/* Log the update in place operation. During recovery
MLOG_COMP_REC_UPDATE_IN_PLACE/MLOG_REC_UPDATE_IN_PLACE
expects trx_id, roll_ptr for secondary indexes. So we
just write dummy trx_id(0), roll_ptr(0) */
btr_cur_update_in_place_log(BTR_KEEP_SYS_FLAG, rec,
index, update,
NULL,
ut_dulint_zero, mtr);
DBUG_EXECUTE_IF(
"crash_after_log_ibuf_upd_inplace",
log_buffer_flush_to_disk();
fprintf(stderr,
"InnoDB: Wrote log record for ibuf "
"update in place operation\n");
DBUG_SUICIDE();
);
goto updated_in_place; goto updated_in_place;
} }

View File

@ -558,6 +558,19 @@ btr_push_update_extern_fields(
const ulint* offsets,/* in: array returned by rec_get_offsets() */ const ulint* offsets,/* in: array returned by rec_get_offsets() */
upd_t* update);/* in: update vector or NULL */ upd_t* update);/* in: update vector or NULL */
/***************************************************************
Writes a redo log record of updating a record in-place. */
UNIV_INLINE
void
btr_cur_update_in_place_log(
/*========================*/
ulint flags, /* in: flags */
rec_t* rec, /* in: record */
dict_index_t* index, /* in: index where cursor positioned */
upd_t* update, /* in: update vector */
trx_t* trx, /* in: transaction */
dulint roll_ptr, /* in: roll ptr */
mtr_t* mtr); /* in: mtr */
/*######################################################################*/ /*######################################################################*/

View File

@ -58,6 +58,7 @@ Created 10/16/1994 Heikki Tuuri
#include "btr0btr.h" #include "btr0btr.h"
#include "btr0sea.h" #include "btr0sea.h"
#include "trx0rec.h" #include "trx0rec.h"
#include "trx0undo.h"
#include "trx0roll.h" /* trx_is_recv() */ #include "trx0roll.h" /* trx_is_recv() */
#include "que0que.h" #include "que0que.h"
#include "row0row.h" #include "row0row.h"
@ -1557,18 +1558,31 @@ btr_cur_update_in_place_log(
return; return;
} }
/* The code below assumes index is a clustered index: change index to /* For secondary indexes, we could skip writing the dummy system fields
the clustered index if we are updating a secondary index record (or we to the redo log but we have to change redo log parsing of
could as well skip writing the sys col values to the log in this case MLOG_REC_UPDATE_IN_PLACE/MLOG_COMP_REC_UPDATE_IN_PLACE or we have to add
because they are not needed for a secondary index record update) */ new redo log record. For now, just write dummy sys fields to the redo
log if we are updating a secondary index record.
index = dict_table_get_first_index(index->table); */
mach_write_to_1(log_ptr, flags); mach_write_to_1(log_ptr, flags);
log_ptr++; log_ptr++;
log_ptr = row_upd_write_sys_vals_to_log(index, trx, roll_ptr, log_ptr, if (index->type & DICT_CLUSTERED) {
mtr); log_ptr = row_upd_write_sys_vals_to_log(
index, trx, roll_ptr, log_ptr, mtr);
} else {
/* Dummy system fields for a secondary index */
/* TRX_ID Position */
log_ptr += mach_write_compressed(log_ptr, 0);
/* ROLL_PTR */
trx_write_roll_ptr(log_ptr, ut_dulint_zero);
log_ptr += DATA_ROLL_PTR_LEN;
/* TRX_ID */
log_ptr += mach_dulint_write_compressed(log_ptr,
ut_dulint_zero);
}
mach_write_to_2(log_ptr, page_offset(rec)); mach_write_to_2(log_ptr, page_offset(rec));
log_ptr += 2; log_ptr += 2;

View File

@ -3072,6 +3072,24 @@ updated_in_place:
to btr_cur_update_in_place(). */ to btr_cur_update_in_place(). */
row_upd_rec_in_place(rec, index, offsets, row_upd_rec_in_place(rec, index, offsets,
update, page_zip); update, page_zip);
/* Log the update in place operation. During recovery
MLOG_COMP_REC_UPDATE_IN_PLACE/MLOG_REC_UPDATE_IN_PLACE
expects trx_id, roll_ptr for secondary indexes. So we
just write dummy trx_id(0), roll_ptr(0) */
btr_cur_update_in_place_log(BTR_KEEP_SYS_FLAG, rec,
index, update,
NULL,
ut_dulint_zero, mtr);
DBUG_EXECUTE_IF(
"crash_after_log_ibuf_upd_inplace",
log_buffer_flush_to_disk();
fprintf(stderr,
"InnoDB: Wrote log record for ibuf "
"update in place operation\n");
DBUG_SUICIDE();
);
goto updated_in_place; goto updated_in_place;
} }

View File

@ -1,6 +1,6 @@
/***************************************************************************** /*****************************************************************************
Copyright (c) 1994, 2012, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 1994, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under 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 the terms of the GNU General Public License as published by the Free Software
@ -636,6 +636,21 @@ btr_push_update_extern_fields(
mem_heap_t* heap) /*!< in: memory heap */ mem_heap_t* heap) /*!< in: memory heap */
__attribute__((nonnull)); __attribute__((nonnull));
/***********************************************************//**
Writes a redo log record of updating a record in-place. */
UNIV_INLINE
void
btr_cur_update_in_place_log(
/*========================*/
ulint flags, /*!< in: flags */
rec_t* rec, /*!< in: record */
dict_index_t* index, /*!< in: index where cursor
positioned */
const upd_t* update, /*!< in: update vector */
trx_t* trx, /*!< in: transaction */
roll_ptr_t roll_ptr, /*!< in: roll ptr */
mtr_t* mtr); /*!< in: mtr */
/*######################################################################*/ /*######################################################################*/
/** In the pessimistic delete, if the page data size drops below this /** In the pessimistic delete, if the page data size drops below this