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:
parent
4250117308
commit
59402fe0a7
6
mysql-test/include/have_innodb_change_buffering.inc
Normal file
6
mysql-test/include/have_innodb_change_buffering.inc
Normal 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
|
||||
}
|
66
mysql-test/include/search_pattern_in_file.inc
Normal file
66
mysql-test/include/search_pattern_in_file.inc
Normal 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
|
@ -31,6 +31,7 @@ Created 10/16/1994 Heikki Tuuri
|
||||
#include "btr0sea.h"
|
||||
#include "row0upd.h"
|
||||
#include "trx0rec.h"
|
||||
#include "trx0undo.h"
|
||||
#include "trx0roll.h" /* trx_roll_crash_recv_trx */
|
||||
#include "que0que.h"
|
||||
#include "row0row.h"
|
||||
@ -1363,18 +1364,31 @@ btr_cur_update_in_place_log(
|
||||
return;
|
||||
}
|
||||
|
||||
/* The code below assumes index is a clustered index: change index to
|
||||
the clustered index if we are updating a secondary index record (or we
|
||||
could as well skip writing the sys col values to the log in this case
|
||||
because they are not needed for a secondary index record update) */
|
||||
|
||||
index = dict_table_get_first_index(index->table);
|
||||
/* For secondary indexes, we could skip writing the dummy system fields
|
||||
to the redo log but we have to change redo log parsing of
|
||||
MLOG_REC_UPDATE_IN_PLACE/MLOG_COMP_REC_UPDATE_IN_PLACE or we have to add
|
||||
new redo log record. For now, just write dummy sys fields to the redo
|
||||
log if we are updating a secondary index record.
|
||||
*/
|
||||
|
||||
mach_write_to_1(log_ptr, flags);
|
||||
log_ptr++;
|
||||
|
||||
log_ptr = row_upd_write_sys_vals_to_log(index, trx, roll_ptr, log_ptr,
|
||||
mtr);
|
||||
if (index->type & DICT_CLUSTERED) {
|
||||
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));
|
||||
log_ptr += 2;
|
||||
|
||||
|
@ -3024,6 +3024,24 @@ updated_in_place:
|
||||
/* This is the easy case. Do something similar
|
||||
to btr_cur_update_in_place(). */
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -558,6 +558,19 @@ btr_push_update_extern_fields(
|
||||
const ulint* offsets,/* in: array returned by rec_get_offsets() */
|
||||
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 */
|
||||
|
||||
/*######################################################################*/
|
||||
|
||||
|
@ -58,6 +58,7 @@ Created 10/16/1994 Heikki Tuuri
|
||||
#include "btr0btr.h"
|
||||
#include "btr0sea.h"
|
||||
#include "trx0rec.h"
|
||||
#include "trx0undo.h"
|
||||
#include "trx0roll.h" /* trx_is_recv() */
|
||||
#include "que0que.h"
|
||||
#include "row0row.h"
|
||||
@ -1557,18 +1558,31 @@ btr_cur_update_in_place_log(
|
||||
return;
|
||||
}
|
||||
|
||||
/* The code below assumes index is a clustered index: change index to
|
||||
the clustered index if we are updating a secondary index record (or we
|
||||
could as well skip writing the sys col values to the log in this case
|
||||
because they are not needed for a secondary index record update) */
|
||||
|
||||
index = dict_table_get_first_index(index->table);
|
||||
/* For secondary indexes, we could skip writing the dummy system fields
|
||||
to the redo log but we have to change redo log parsing of
|
||||
MLOG_REC_UPDATE_IN_PLACE/MLOG_COMP_REC_UPDATE_IN_PLACE or we have to add
|
||||
new redo log record. For now, just write dummy sys fields to the redo
|
||||
log if we are updating a secondary index record.
|
||||
*/
|
||||
|
||||
mach_write_to_1(log_ptr, flags);
|
||||
log_ptr++;
|
||||
|
||||
log_ptr = row_upd_write_sys_vals_to_log(index, trx, roll_ptr, log_ptr,
|
||||
mtr);
|
||||
if (index->type & DICT_CLUSTERED) {
|
||||
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));
|
||||
log_ptr += 2;
|
||||
|
||||
|
@ -3072,6 +3072,24 @@ updated_in_place:
|
||||
to btr_cur_update_in_place(). */
|
||||
row_upd_rec_in_place(rec, index, offsets,
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
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 */
|
||||
__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
|
||||
|
Loading…
x
Reference in New Issue
Block a user