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 "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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 */
|
||||||
|
|
||||||
/*######################################################################*/
|
/*######################################################################*/
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user