Commit f8f424da authored by sbains's avatar sbains

branches/innodb+: Create additional rollback segments on startup. Reduce

the upper limit of total rollback segments from 256 to 128. This is because
we can't use the sign bit. It has not caused problems in the past because we
only created one segment. InnoDB has always had the capability to use the
additional rollback segments therefore this patch is backwards compatible.
The only requirement to maintain backward compatibility has been to ensure
that the additional segments are created after the double write buffer.
This is to avoid breaking assumptions in the existing code.

Fix Bug#26590 MySQL does not allow more than 1023 open transactions
parent f29b6ef0
......@@ -103,7 +103,7 @@ trx_rseg_header_create(
ulint zip_size, /*!< in: compressed page size in bytes
or 0 for uncompressed pages */
ulint max_size, /*!< in: max size in pages */
ulint* slot_no, /*!< out: rseg id == slot number in trx sys */
ulint rseg_slot_no, /*!< in: rseg id == slot number in trx sys */
mtr_t* mtr); /*!< in: mtr */
/*********************************************************************//**
Creates the memory copies for rollback segments and initializes the
......@@ -122,6 +122,12 @@ trx_rseg_mem_free(
/*==============*/
trx_rseg_t* rseg); /* in, own: instance to free */
/*********************************************************************
Creates a rollback segment. */
UNIV_INTERN
trx_rseg_t*
trx_rseg_create(void);
/*==================*/
/* Number of undo log slots in a rollback segment file copy */
#define TRX_RSEG_N_SLOTS (UNIV_PAGE_SIZE / 16)
......
......@@ -431,6 +431,14 @@ trx_sys_file_format_id_to_name(
const ulint id); /*!< in: id of the file format */
#endif /* !UNIV_HOTBACKUP */
/*********************************************************************
Creates the rollback segments */
UNIV_INTERN
void
trx_sys_create_rsegs(
/*=================*/
ulint n_rsegs); /*!< number of rollback segments to create */
/* The automatically created system rollback segment has this id */
#define TRX_SYS_SYSTEM_RSEG_ID 0
......@@ -465,11 +473,16 @@ trx_sys_file_format_id_to_name(
slots */
/*------------------------------------------------------------- @} */
/** Maximum number of rollback segments: the number of segment
specification slots in the transaction system array; rollback segment
id must fit in one byte, therefore 256; each slot is currently 8 bytes
in size */
#define TRX_SYS_N_RSEGS 256
/* Max number of rollback segments: the number of segment specification slots
in the transaction system array; rollback segment id must fit in one (signed)
byte, therefore 128; each slot is currently 8 bytes in size. If you want
to raise the level to 256 then you will need to fix some assertions that
impose the 7 bit restriction. e.g., mach_write_to_3() */
#define TRX_SYS_N_RSEGS 128
/* Originally, InnoDB defined TRX_SYS_N_RSEGS as 256 but created only one
rollback segment. It initialized some arrays with this number of entries.
We must remember this limit in order to keep file compatibility. */
#define TRX_SYS_OLD_N_RSEGS 256
/** Maximum length of MySQL binlog file name, in bytes.
@see trx_sys_mysql_master_log_name
......
......@@ -42,7 +42,7 @@ trx_undo_build_roll_ptr(
#if DATA_ROLL_PTR_LEN != 7
# error "DATA_ROLL_PTR_LEN != 7"
#endif
ut_ad(rseg_id < 128);
ut_ad(rseg_id < TRX_SYS_N_RSEGS);
return(ut_dulint_create(is_insert * 128 * 256 * 256
+ rseg_id * 256 * 256
......
......@@ -1533,12 +1533,19 @@ innobase_start_or_create_for_mysql(void)
if (create_new_db) {
mtr_start(&mtr);
fsp_header_init(0, sum_of_new_sizes, &mtr);
mtr_commit(&mtr);
/* To maintain backward compatibility we create only
the first rollback segment before the double write buffer.
All the remaining rollback segments will be created later,
after the double write buffer has been created. */
trx_sys_create();
dict_create();
srv_startup_is_before_trx_rollback_phase = FALSE;
#ifdef UNIV_LOG_ARCHIVE
......@@ -1557,7 +1564,9 @@ innobase_start_or_create_for_mysql(void)
in any disk i/o, first call dict_boot */
dict_boot();
trx_sys_init_at_db_start();
srv_startup_is_before_trx_rollback_phase = FALSE;
/* Initialize the fsp free limit global variable in the log
......@@ -1714,6 +1723,14 @@ innobase_start_or_create_for_mysql(void)
trx_sys_create_doublewrite_buf();
}
/* Here the double write buffer has already been created and so
any new rollback segments will be allocated after the double
write buffer. The default segment should already exist.
We create the new segments only if it's a new database or
the database was shutdown cleanly. */
trx_sys_create_rsegs(TRX_SYS_N_RSEGS - 1);
err = dict_create_or_check_foreign_constraint_tables();
if (err != DB_SUCCESS) {
......
......@@ -51,11 +51,9 @@ trx_rseg_get_on_id(
trx_rseg_t* rseg;
rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list);
ut_ad(rseg);
while (rseg->id != id) {
while (rseg && rseg->id != id) {
rseg = UT_LIST_GET_NEXT(rseg_list, rseg);
ut_ad(rseg);
}
return(rseg);
......@@ -73,7 +71,7 @@ trx_rseg_header_create(
ulint zip_size, /*!< in: compressed page size in bytes
or 0 for uncompressed pages */
ulint max_size, /*!< in: max size in pages */
ulint* slot_no, /*!< out: rseg id == slot number in trx sys */
ulint rseg_slot_no, /*!< in: rseg id == slot number in trx sys */
mtr_t* mtr) /*!< in: mtr */
{
ulint page_no;
......@@ -86,14 +84,6 @@ trx_rseg_header_create(
ut_ad(mutex_own(&kernel_mutex));
ut_ad(mtr_memo_contains(mtr, fil_space_get_latch(space, NULL),
MTR_MEMO_X_LOCK));
sys_header = trx_sysf_get(mtr);
*slot_no = trx_sysf_rseg_find_free(mtr);
if (*slot_no == ULINT_UNDEFINED) {
return(FIL_NULL);
}
/* Allocate a new file segment for the rollback segment */
block = fseg_create(space, 0,
......@@ -127,11 +117,13 @@ trx_rseg_header_create(
trx_rsegf_set_nth_undo(rsegf, i, FIL_NULL, mtr);
}
/* Add the rollback segment info to the free slot in the trx system
header */
/* Add the rollback segment info to the free slot in
the trx system header */
sys_header = trx_sysf_get(mtr);
trx_sysf_rseg_set_space(sys_header, *slot_no, space, mtr);
trx_sysf_rseg_set_page_no(sys_header, *slot_no, page_no, mtr);
trx_sysf_rseg_set_space(sys_header, rseg_slot_no, space, mtr);
trx_sysf_rseg_set_page_no(sys_header, rseg_slot_no, page_no, mtr);
return(page_no);
}
......@@ -196,16 +188,16 @@ trx_rseg_mem_create(
ulint page_no, /*!< in: page number of the segment header */
mtr_t* mtr) /*!< in: mtr */
{
trx_rsegf_t* rseg_header;
ulint len;
trx_rseg_t* rseg;
trx_ulogf_t* undo_log_hdr;
fil_addr_t node_addr;
trx_rsegf_t* rseg_header;
trx_ulogf_t* undo_log_hdr;
ulint sum_of_undo_sizes;
ulint len;
ut_ad(mutex_own(&kernel_mutex));
rseg = mem_alloc(sizeof(trx_rseg_t));
rseg = mem_zalloc(sizeof(trx_rseg_t));
rseg->id = id;
rseg->space = space;
......@@ -255,39 +247,108 @@ trx_rseg_mem_create(
return(rseg);
}
/*********************************************************************//**
Creates the memory copies for rollback segments and initializes the
/********************************************************************
Creates the memory copies for the rollback segments and initializes the
rseg list and array in trx_sys at a database startup. */
UNIV_INTERN
static
void
trx_rseg_list_and_array_init(
/*=========================*/
trx_rseg_create_instance(
/*=====================*/
trx_sysf_t* sys_header, /*!< in: trx system header */
mtr_t* mtr) /*!< in: mtr */
{
ulint i;
ulint page_no;
ulint space;
UT_LIST_INIT(trx_sys->rseg_list);
trx_sys->rseg_history_len = 0;
ulint i;
for (i = 0; i < TRX_SYS_N_RSEGS; i++) {
ulint page_no;
page_no = trx_sysf_rseg_get_page_no(sys_header, i, mtr);
if (page_no == FIL_NULL) {
trx_sys_set_nth_rseg(trx_sys, i, NULL);
} else {
ulint zip_size;
ulint space;
ulint zip_size;
trx_rseg_t* rseg = NULL;
ut_a(!trx_rseg_get_on_id(i));
space = trx_sysf_rseg_get_space(sys_header, i, mtr);
zip_size = space ? fil_space_get_zip_size(space) : 0;
trx_rseg_mem_create(i, space, zip_size, page_no, mtr);
rseg = trx_rseg_mem_create(
i, space, zip_size, page_no, mtr);
ut_a(rseg->id == i);
}
}
}
/*********************************************************************
Creates a rollback segment.
@return pointer to new rollback segment if create successful */
UNIV_INTERN
trx_rseg_t*
trx_rseg_create(void)
/*=================*/
{
mtr_t mtr;
ulint slot_no;
trx_rseg_t* rseg = NULL;
mtr_start(&mtr);
/* To obey the latching order, acquire the file space
x-latch before the kernel mutex. */
mtr_x_lock(fil_space_get_latch(TRX_SYS_SPACE, NULL), &mtr);
mutex_enter(&kernel_mutex);
slot_no = trx_sysf_rseg_find_free(&mtr);
if (slot_no != ULINT_UNDEFINED) {
ulint space;
ulint page_no;
ulint zip_size;
trx_sysf_t* sys_header;
page_no = trx_rseg_header_create(
TRX_SYS_SPACE, 0, ULINT_MAX, slot_no, &mtr);
ut_a(page_no != FIL_NULL);
ut_ad(!trx_rseg_get_on_id(slot_no));
sys_header = trx_sysf_get(&mtr);
space = trx_sysf_rseg_get_space(sys_header, slot_no, &mtr);
zip_size = space ? fil_space_get_zip_size(space) : 0;
rseg = trx_rseg_mem_create(
slot_no, space, zip_size, page_no, &mtr);
}
mutex_exit(&kernel_mutex);
mtr_commit(&mtr);
return(rseg);
}
/********************************************************************
Initialize the rollback instance list. */
UNIV_INTERN
void
trx_rseg_list_and_array_init(
/*=========================*/
trx_sysf_t* sys_header, /* in: trx system header */
mtr_t* mtr) /* in: mtr */
{
UT_LIST_INIT(trx_sys->rseg_list);
trx_sys->rseg_history_len = 0;
trx_rseg_create_instance(sys_header, mtr);
}
......@@ -39,6 +39,7 @@ Created 3/26/1996 Heikki Tuuri
#include "srv0srv.h"
#include "trx0purge.h"
#include "log0log.h"
#include "log0recv.h"
#include "os0file.h"
#include "read0read.h"
......@@ -877,7 +878,8 @@ trx_sysf_create(
buf_block_t* block;
page_t* page;
ulint page_no;
ulint i;
byte* ptr;
ulint len;
ut_ad(mtr);
......@@ -910,32 +912,31 @@ trx_sysf_create(
sys_header = trx_sysf_get(mtr);
/* Start counting transaction ids from number 1 up */
mlog_write_dulint(sys_header + TRX_SYS_TRX_ID_STORE,
ut_dulint_create(0, 1), mtr);
mach_write_to_8(sys_header + TRX_SYS_TRX_ID_STORE,
ut_dulint_create(0, 1));
/* Reset the rollback segment slots */
for (i = 0; i < TRX_SYS_N_RSEGS; i++) {
/* Reset the rollback segment slots. Old versions of InnoDB
define TRX_SYS_N_RSEGS as 256 (TRX_SYS_OLD_N_RSEGS) and expect
that the whole array is initialized. */
ptr = TRX_SYS_RSEGS + sys_header;
len = ut_max(TRX_SYS_OLD_N_RSEGS, TRX_SYS_N_RSEGS)
* TRX_SYS_RSEG_SLOT_SIZE;
memset(ptr, 0xff, len);
ptr += len;
ut_a(ptr <= page + (UNIV_PAGE_SIZE - FIL_PAGE_DATA_END));
trx_sysf_rseg_set_space(sys_header, i, ULINT_UNDEFINED, mtr);
trx_sysf_rseg_set_page_no(sys_header, i, FIL_NULL, mtr);
}
/* Initialize all of the page. This part used to be uninitialized. */
memset(ptr, 0, UNIV_PAGE_SIZE - FIL_PAGE_DATA_END + page - ptr);
/* The remaining area (up to the page trailer) is uninitialized.
Silence Valgrind warnings about it. */
UNIV_MEM_VALID(sys_header + (TRX_SYS_RSEGS
+ TRX_SYS_N_RSEGS * TRX_SYS_RSEG_SLOT_SIZE
+ TRX_SYS_RSEG_SPACE),
(UNIV_PAGE_SIZE - FIL_PAGE_DATA_END
- (TRX_SYS_RSEGS
+ TRX_SYS_N_RSEGS * TRX_SYS_RSEG_SLOT_SIZE
+ TRX_SYS_RSEG_SPACE))
+ page - sys_header);
mlog_log_string(sys_header, UNIV_PAGE_SIZE - FIL_PAGE_DATA_END
+ page - sys_header, mtr);
/* Create the first rollback segment in the SYSTEM tablespace */
page_no = trx_rseg_header_create(TRX_SYS_SPACE, 0, ULINT_MAX, &slot_no,
slot_no = trx_sysf_rseg_find_free(mtr);
page_no = trx_rseg_header_create(TRX_SYS_SPACE, 0, ULINT_MAX, slot_no,
mtr);
ut_a(slot_no == TRX_SYS_SYSTEM_RSEG_ID);
ut_a(page_no != FIL_NULL);
ut_a(page_no == FSP_FIRST_RSEG_PAGE_NO);
mutex_exit(&kernel_mutex);
}
......@@ -1310,6 +1311,40 @@ trx_sys_file_format_close(void)
{
/* Does nothing at the moment */
}
/*********************************************************************
Creates the rollback segments */
UNIV_INTERN
void
trx_sys_create_rsegs(
/*=================*/
ulint n_rsegs) /*!< number of rollback segments to create */
{
ulint new_rsegs = 0;
/* Do not create additional rollback segments if
innodb_force_recovery has been set and the database
was not shutdown cleanly. */
if (!srv_force_recovery && !recv_needed_recovery) {
ulint i;
for (i = 0; i < n_rsegs; ++i) {
if (trx_rseg_create() != NULL) {
++new_rsegs;
} else {
break;
}
}
}
if (new_rsegs > 0) {
fprintf(stderr,
"InnoDB: %lu rollback segment(s) active.\n",
new_rsegs);
}
}
#else /* !UNIV_HOTBACKUP */
/*****************************************************************//**
Prints to stderr the MySQL binlog info in the system header if the
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment