Commit c3fec5d2 authored by Timothy Smith's avatar Timothy Smith

Applying InnoDB snashot 5.0-ss4007, part 2. Fixes

Bug #18828: If InnoDB runs out of undo slots, it returns misleading 'table is full'

This is a backport of code already in 5.1+.  The error message change referred
to in the detailed revision comments is still pending.

Detailed revision comments:

r3937 | calvin | 2009-01-15 03:11:56 +0200 (Thu, 15 Jan 2009) | 17 lines
branches/5.0:

Backport the fix for Bug#18828. Return DB_TOO_MANY_CONCURRENT_TRXS
when we run out of UNDO slots in the rollback segment. The backport
is requested by MySQL under bug#41529 - Safe handling of InnoDB running
out of undo log slots.

This is a partial fix since the MySQL error code requested to properly
report the error condition back to the client has not yet materialized.
Currently we have #ifdef'd the error code translation in ha_innodb.cc.
This will have to be changed as and when MySQl add the new requested
code or an equivalent code that we can then use.

Given the above, currently we will get the old behavior, not the
"fixed" and intended behavior.

Approved by:	Heikki (on IM)
parent 901b2986
...@@ -1249,7 +1249,8 @@ dict_create_or_check_foreign_constraint_tables(void) ...@@ -1249,7 +1249,8 @@ dict_create_or_check_foreign_constraint_tables(void)
fprintf(stderr, "InnoDB: error %lu in creation\n", fprintf(stderr, "InnoDB: error %lu in creation\n",
(ulong) error); (ulong) error);
ut_a(error == DB_OUT_OF_FILE_SPACE); ut_a(error == DB_OUT_OF_FILE_SPACE
|| error == DB_TOO_MANY_CONCURRENT_TRXS);
fprintf(stderr, "InnoDB: creation failed\n"); fprintf(stderr, "InnoDB: creation failed\n");
fprintf(stderr, "InnoDB: tablespace is full\n"); fprintf(stderr, "InnoDB: tablespace is full\n");
......
...@@ -70,6 +70,11 @@ Created 5/24/1996 Heikki Tuuri ...@@ -70,6 +70,11 @@ Created 5/24/1996 Heikki Tuuri
work with e.g., FT indexes created by work with e.g., FT indexes created by
a later version of the engine. */ a later version of the engine. */
#define DB_TOO_MANY_CONCURRENT_TRXS 47 /* when InnoDB runs out of the
preconfigured undo slots, this can
only happen when there are too many
concurrent transactions */
/* The following are partial failure codes */ /* The following are partial failure codes */
#define DB_FAIL 1000 #define DB_FAIL 1000
#define DB_OVERFLOW 1001 #define DB_OVERFLOW 1001
......
...@@ -222,13 +222,16 @@ trx_undo_lists_init( ...@@ -222,13 +222,16 @@ trx_undo_lists_init(
Assigns an undo log for a transaction. A new undo log is created or a cached Assigns an undo log for a transaction. A new undo log is created or a cached
undo log reused. */ undo log reused. */
trx_undo_t* ulint
trx_undo_assign_undo( trx_undo_assign_undo(
/*=================*/ /*=================*/
/* out: the undo log, NULL if did not succeed: out of /* out: DB_SUCCESS if undo log assign
space */ * successful, possible error codes are:
trx_t* trx, /* in: transaction */ * ER_TOO_MANY_CONCURRENT_TRXS
ulint type); /* in: TRX_UNDO_INSERT or TRX_UNDO_UPDATE */ * DB_OUT_OF_FILE_SPAC
* DB_OUT_OF_MEMORY */
trx_t* trx, /* in: transaction */
ulint type); /* in: TRX_UNDO_INSERT or TRX_UNDO_UPDATE */
/********************************************************************** /**********************************************************************
Sets the state of the undo log segment at a transaction finish. */ Sets the state of the undo log segment at a transaction finish. */
......
...@@ -494,7 +494,8 @@ handle_new_error: ...@@ -494,7 +494,8 @@ handle_new_error:
/* MySQL will roll back the latest SQL statement */ /* MySQL will roll back the latest SQL statement */
} else if (err == DB_ROW_IS_REFERENCED } else if (err == DB_ROW_IS_REFERENCED
|| err == DB_NO_REFERENCED_ROW || err == DB_NO_REFERENCED_ROW
|| err == DB_CANNOT_ADD_CONSTRAINT) { || err == DB_CANNOT_ADD_CONSTRAINT
|| err == DB_TOO_MANY_CONCURRENT_TRXS) {
if (savept) { if (savept) {
/* Roll back the latest, possibly incomplete /* Roll back the latest, possibly incomplete
insertion or update */ insertion or update */
......
...@@ -1013,6 +1013,7 @@ trx_undo_report_row_operation( ...@@ -1013,6 +1013,7 @@ trx_undo_report_row_operation(
ibool is_insert; ibool is_insert;
trx_rseg_t* rseg; trx_rseg_t* rseg;
mtr_t mtr; mtr_t mtr;
ulint err = DB_SUCCESS;
mem_heap_t* heap = NULL; mem_heap_t* heap = NULL;
ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint offsets_[REC_OFFS_NORMAL_SIZE];
ulint* offsets = offsets_; ulint* offsets = offsets_;
...@@ -1024,7 +1025,7 @@ trx_undo_report_row_operation( ...@@ -1024,7 +1025,7 @@ trx_undo_report_row_operation(
*roll_ptr = ut_dulint_zero; *roll_ptr = ut_dulint_zero;
return(DB_SUCCESS); return(err);
} }
ut_ad(thr); ut_ad(thr);
...@@ -1042,7 +1043,7 @@ trx_undo_report_row_operation( ...@@ -1042,7 +1043,7 @@ trx_undo_report_row_operation(
if (trx->insert_undo == NULL) { if (trx->insert_undo == NULL) {
trx_undo_assign_undo(trx, TRX_UNDO_INSERT); err = trx_undo_assign_undo(trx, TRX_UNDO_INSERT);
} }
undo = trx->insert_undo; undo = trx->insert_undo;
...@@ -1052,7 +1053,7 @@ trx_undo_report_row_operation( ...@@ -1052,7 +1053,7 @@ trx_undo_report_row_operation(
if (trx->update_undo == NULL) { if (trx->update_undo == NULL) {
trx_undo_assign_undo(trx, TRX_UNDO_UPDATE); err = trx_undo_assign_undo(trx, TRX_UNDO_UPDATE);
} }
...@@ -1060,11 +1061,11 @@ trx_undo_report_row_operation( ...@@ -1060,11 +1061,11 @@ trx_undo_report_row_operation(
is_insert = FALSE; is_insert = FALSE;
} }
if (undo == NULL) { if (err != DB_SUCCESS) {
/* Did not succeed: out of space */ /* Did not succeed: return the error encountered */
mutex_exit(&(trx->undo_mutex)); mutex_exit(&(trx->undo_mutex));
return(DB_OUT_OF_FILE_SPACE); return(err);
} }
page_no = undo->last_page_no; page_no = undo->last_page_no;
...@@ -1154,7 +1155,7 @@ trx_undo_report_row_operation( ...@@ -1154,7 +1155,7 @@ trx_undo_report_row_operation(
if (UNIV_LIKELY_NULL(heap)) { if (UNIV_LIKELY_NULL(heap)) {
mem_heap_free(heap); mem_heap_free(heap);
} }
return(DB_SUCCESS); return(err);
} }
/*============== BUILDING PREVIOUS VERSION OF A RECORD ===============*/ /*============== BUILDING PREVIOUS VERSION OF A RECORD ===============*/
......
...@@ -374,27 +374,32 @@ trx_undo_page_init( ...@@ -374,27 +374,32 @@ trx_undo_page_init(
/******************************************************************* /*******************************************************************
Creates a new undo log segment in file. */ Creates a new undo log segment in file. */
static static
page_t* ulint
trx_undo_seg_create( trx_undo_seg_create(
/*================*/ /*================*/
/* out: segment header page x-latched, NULL /* out: DB_SUCCESS if page creation OK
if no space left */ possible error codes are:
DB_TOO_MANY_CONCURRENT_TRXS
DB_OUT_OF_FILE_SPACE */
trx_rseg_t* rseg __attribute__((unused)),/* in: rollback segment */ trx_rseg_t* rseg __attribute__((unused)),/* in: rollback segment */
trx_rsegf_t* rseg_hdr,/* in: rollback segment header, page trx_rsegf_t* rseg_hdr,/* in: rollback segment header, page
x-latched */ x-latched */
ulint type, /* in: type of the segment: TRX_UNDO_INSERT or ulint type, /* in: type of the segment: TRX_UNDO_INSERT or
TRX_UNDO_UPDATE */ TRX_UNDO_UPDATE */
ulint* id, /* out: slot index within rseg header */ ulint* id, /* out: slot index within rseg header */
page_t** undo_page,
/* out: segment header page x-latched, NULL
if there was an error */
mtr_t* mtr) /* in: mtr */ mtr_t* mtr) /* in: mtr */
{ {
ulint slot_no; ulint slot_no;
ulint space; ulint space;
page_t* undo_page;
trx_upagef_t* page_hdr; trx_upagef_t* page_hdr;
trx_usegf_t* seg_hdr; trx_usegf_t* seg_hdr;
ulint n_reserved; ulint n_reserved;
ibool success; ibool success;
ulint err = DB_SUCCESS;
ut_ad(mtr && id && rseg_hdr); ut_ad(mtr && id && rseg_hdr);
#ifdef UNIV_SYNC_DEBUG #ifdef UNIV_SYNC_DEBUG
ut_ad(mutex_own(&(rseg->mutex))); ut_ad(mutex_own(&(rseg->mutex)));
...@@ -411,7 +416,7 @@ trx_undo_seg_create( ...@@ -411,7 +416,7 @@ trx_undo_seg_create(
"InnoDB: Warning: cannot find a free slot for an undo log. Do you have too\n" "InnoDB: Warning: cannot find a free slot for an undo log. Do you have too\n"
"InnoDB: many active transactions running concurrently?\n"); "InnoDB: many active transactions running concurrently?\n");
return(NULL); return(DB_TOO_MANY_CONCURRENT_TRXS);
} }
space = buf_frame_get_space_id(rseg_hdr); space = buf_frame_get_space_id(rseg_hdr);
...@@ -420,29 +425,29 @@ trx_undo_seg_create( ...@@ -420,29 +425,29 @@ trx_undo_seg_create(
mtr); mtr);
if (!success) { if (!success) {
return(NULL); return(DB_OUT_OF_FILE_SPACE);
} }
/* Allocate a new file segment for the undo log */ /* Allocate a new file segment for the undo log */
undo_page = fseg_create_general(space, 0, *undo_page = fseg_create_general(space, 0,
TRX_UNDO_SEG_HDR + TRX_UNDO_FSEG_HEADER, TRUE, mtr); TRX_UNDO_SEG_HDR + TRX_UNDO_FSEG_HEADER, TRUE, mtr);
fil_space_release_free_extents(space, n_reserved); fil_space_release_free_extents(space, n_reserved);
if (undo_page == NULL) { if (*undo_page == NULL) {
/* No space left */ /* No space left */
return(NULL); return(DB_OUT_OF_FILE_SPACE);
} }
#ifdef UNIV_SYNC_DEBUG #ifdef UNIV_SYNC_DEBUG
buf_page_dbg_add_level(undo_page, SYNC_TRX_UNDO_PAGE); buf_page_dbg_add_level(*undo_page, SYNC_TRX_UNDO_PAGE);
#endif /* UNIV_SYNC_DEBUG */ #endif /* UNIV_SYNC_DEBUG */
page_hdr = undo_page + TRX_UNDO_PAGE_HDR; page_hdr = *undo_page + TRX_UNDO_PAGE_HDR;
seg_hdr = undo_page + TRX_UNDO_SEG_HDR; seg_hdr = *undo_page + TRX_UNDO_SEG_HDR;
trx_undo_page_init(undo_page, type, mtr); trx_undo_page_init(*undo_page, type, mtr);
mlog_write_ulint(page_hdr + TRX_UNDO_PAGE_FREE, mlog_write_ulint(page_hdr + TRX_UNDO_PAGE_FREE,
TRX_UNDO_SEG_HDR + TRX_UNDO_SEG_HDR_SIZE, TRX_UNDO_SEG_HDR + TRX_UNDO_SEG_HDR_SIZE,
...@@ -456,10 +461,11 @@ trx_undo_seg_create( ...@@ -456,10 +461,11 @@ trx_undo_seg_create(
page_hdr + TRX_UNDO_PAGE_NODE, mtr); page_hdr + TRX_UNDO_PAGE_NODE, mtr);
trx_rsegf_set_nth_undo(rseg_hdr, slot_no, trx_rsegf_set_nth_undo(rseg_hdr, slot_no,
buf_frame_get_page_no(undo_page), mtr); buf_frame_get_page_no(*undo_page), mtr);
*id = slot_no; *id = slot_no;
return(undo_page); return(err);
} }
/************************************************************************** /**************************************************************************
...@@ -1400,6 +1406,11 @@ trx_undo_mem_create( ...@@ -1400,6 +1406,11 @@ trx_undo_mem_create(
undo = mem_alloc(sizeof(trx_undo_t)); undo = mem_alloc(sizeof(trx_undo_t));
if (undo == NULL) {
return NULL;
}
undo->id = id; undo->id = id;
undo->type = type; undo->type = type;
undo->state = TRX_UNDO_ACTIVE; undo->state = TRX_UNDO_ACTIVE;
...@@ -1479,11 +1490,15 @@ trx_undo_mem_free( ...@@ -1479,11 +1490,15 @@ trx_undo_mem_free(
/************************************************************************** /**************************************************************************
Creates a new undo log. */ Creates a new undo log. */
static static
trx_undo_t* ulint
trx_undo_create( trx_undo_create(
/*============*/ /*============*/
/* out: undo log object, NULL if did not /* out: DB_SUCCESS if successful in creating
succeed: out of space */ the new undo lob object, possible error
codes are:
DB_TOO_MANY_CONCURRENT_TRXS
DB_OUT_OF_FILE_SPACE
DB_OUT_OF_MEMORY*/
trx_t* trx, /* in: transaction */ trx_t* trx, /* in: transaction */
trx_rseg_t* rseg, /* in: rollback segment memory copy */ trx_rseg_t* rseg, /* in: rollback segment memory copy */
ulint type, /* in: type of the log: TRX_UNDO_INSERT or ulint type, /* in: type of the log: TRX_UNDO_INSERT or
...@@ -1491,36 +1506,39 @@ trx_undo_create( ...@@ -1491,36 +1506,39 @@ trx_undo_create(
dulint trx_id, /* in: id of the trx for which the undo log dulint trx_id, /* in: id of the trx for which the undo log
is created */ is created */
XID* xid, /* in: X/Open transaction identification*/ XID* xid, /* in: X/Open transaction identification*/
trx_undo_t** undo, /* out: the new undo log object, undefined
* if did not succeed */
mtr_t* mtr) /* in: mtr */ mtr_t* mtr) /* in: mtr */
{ {
trx_rsegf_t* rseg_header; trx_rsegf_t* rseg_header;
ulint page_no; ulint page_no;
ulint offset; ulint offset;
ulint id; ulint id;
trx_undo_t* undo;
page_t* undo_page; page_t* undo_page;
ulint err;
#ifdef UNIV_SYNC_DEBUG #ifdef UNIV_SYNC_DEBUG
ut_ad(mutex_own(&(rseg->mutex))); ut_ad(mutex_own(&(rseg->mutex)));
#endif /* UNIV_SYNC_DEBUG */ #endif /* UNIV_SYNC_DEBUG */
if (rseg->curr_size == rseg->max_size) { if (rseg->curr_size == rseg->max_size) {
return(NULL); return(DB_OUT_OF_FILE_SPACE);
} }
rseg->curr_size++; rseg->curr_size++;
rseg_header = trx_rsegf_get(rseg->space, rseg->page_no, mtr); rseg_header = trx_rsegf_get(rseg->space, rseg->page_no, mtr);
undo_page = trx_undo_seg_create(rseg, rseg_header, type, &id, mtr); err = trx_undo_seg_create(rseg, rseg_header, type, &id,
&undo_page, mtr);
if (undo_page == NULL) { if (err != DB_SUCCESS) {
/* Did not succeed */ /* Did not succeed */
rseg->curr_size--; rseg->curr_size--;
return(NULL); return(err);
} }
page_no = buf_frame_get_page_no(undo_page); page_no = buf_frame_get_page_no(undo_page);
...@@ -1532,9 +1550,14 @@ trx_undo_create( ...@@ -1532,9 +1550,14 @@ trx_undo_create(
undo_page + offset, mtr); undo_page + offset, mtr);
} }
undo = trx_undo_mem_create(rseg, id, type, trx_id, xid, *undo = trx_undo_mem_create(rseg, id, type, trx_id, xid,
page_no, offset); page_no, offset);
return(undo); if (*undo == NULL) {
err = DB_OUT_OF_MEMORY;
}
return(err);
} }
/*================ UNDO LOG ASSIGNMENT AND CLEANUP =====================*/ /*================ UNDO LOG ASSIGNMENT AND CLEANUP =====================*/
...@@ -1653,17 +1676,20 @@ trx_undo_mark_as_dict_operation( ...@@ -1653,17 +1676,20 @@ trx_undo_mark_as_dict_operation(
Assigns an undo log for a transaction. A new undo log is created or a cached Assigns an undo log for a transaction. A new undo log is created or a cached
undo log reused. */ undo log reused. */
trx_undo_t* ulint
trx_undo_assign_undo( trx_undo_assign_undo(
/*=================*/ /*=================*/
/* out: the undo log, NULL if did not succeed: out of /* out: DB_SUCCESS if undo log assign
space */ successful, possible error codes are:
trx_t* trx, /* in: transaction */ DD_TOO_MANY_CONCURRENT_TRXS
ulint type) /* in: TRX_UNDO_INSERT or TRX_UNDO_UPDATE */ DB_OUT_OF_FILE_SPACE DB_OUT_OF_MEMORY*/
trx_t* trx, /* in: transaction */
ulint type) /* in: TRX_UNDO_INSERT or TRX_UNDO_UPDATE */
{ {
trx_rseg_t* rseg; trx_rseg_t* rseg;
trx_undo_t* undo; trx_undo_t* undo;
mtr_t mtr; mtr_t mtr;
ulint err = DB_SUCCESS;
ut_ad(trx); ut_ad(trx);
ut_ad(trx->rseg); ut_ad(trx->rseg);
...@@ -1684,15 +1710,11 @@ trx_undo_assign_undo( ...@@ -1684,15 +1710,11 @@ trx_undo_assign_undo(
undo = trx_undo_reuse_cached(trx, rseg, type, trx->id, &trx->xid, undo = trx_undo_reuse_cached(trx, rseg, type, trx->id, &trx->xid,
&mtr); &mtr);
if (undo == NULL) { if (undo == NULL) {
undo = trx_undo_create(trx, rseg, type, trx->id, &trx->xid, err = trx_undo_create(trx, rseg, type, trx->id, &trx->xid,
&mtr); &undo, &mtr);
if (undo == NULL) { if (err != DB_SUCCESS) {
/* Did not succeed */
mutex_exit(&(rseg->mutex)); goto func_exit;
mtr_commit(&mtr);
return(NULL);
} }
} }
...@@ -1710,10 +1732,11 @@ trx_undo_assign_undo( ...@@ -1710,10 +1732,11 @@ trx_undo_assign_undo(
trx_undo_mark_as_dict_operation(trx, undo, &mtr); trx_undo_mark_as_dict_operation(trx, undo, &mtr);
} }
func_exit:
mutex_exit(&(rseg->mutex)); mutex_exit(&(rseg->mutex));
mtr_commit(&mtr); mtr_commit(&mtr);
return(undo); return err;
} }
/********************************************************************** /**********************************************************************
......
...@@ -524,6 +524,20 @@ convert_error_code_to_mysql( ...@@ -524,6 +524,20 @@ convert_error_code_to_mysql(
mark_transaction_to_rollback(thd, TRUE); mark_transaction_to_rollback(thd, TRUE);
return(HA_ERR_LOCK_TABLE_FULL); return(HA_ERR_LOCK_TABLE_FULL);
} else if (error == DB_TOO_MANY_CONCURRENT_TRXS) {
/* Once MySQL add the appropriate code to errmsg.txt then
we can get rid of this #ifdef. NOTE: The code checked by
the #ifdef is the suggested name for the error condition
and the actual error code name could very well be different.
This will require some monitoring, ie. the status
of this request on our part.*/
#ifdef ER_TOO_MANY_CONCURRENT_TRXS
return(ER_TOO_MANY_CONCURRENT_TRXS);
#else
return(HA_ERR_RECORD_FILE_FULL);
#endif
} else if (error == DB_UNSUPPORTED) { } else if (error == DB_UNSUPPORTED) {
return(HA_ERR_UNSUPPORTED); return(HA_ERR_UNSUPPORTED);
......
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