diff --git a/dict/dict0crea.c b/dict/dict0crea.c index e060d45768e..51146c3c24c 100644 --- a/dict/dict0crea.c +++ b/dict/dict0crea.c @@ -1195,7 +1195,8 @@ dict_create_or_check_foreign_constraint_tables(void) fprintf(stderr, "InnoDB: error %lu in creation\n", (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" diff --git a/handler/ha_innodb.cc b/handler/ha_innodb.cc index 8629c21bcfc..8a89cfd5fff 100644 --- a/handler/ha_innodb.cc +++ b/handler/ha_innodb.cc @@ -627,6 +627,20 @@ convert_error_code_to_mysql( } 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 { return(-1); // Unknown error } diff --git a/include/db0err.h b/include/db0err.h index 843c70af577..0aa1b87e470 100644 --- a/include/db0err.h +++ b/include/db0err.h @@ -62,6 +62,11 @@ Created 5/24/1996 Heikki Tuuri lead to a duplicate key in some table */ +#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 */ #define DB_FAIL 1000 #define DB_OVERFLOW 1001 diff --git a/include/trx0undo.h b/include/trx0undo.h index 87849ab42c3..f53c6b01be4 100644 --- a/include/trx0undo.h +++ b/include/trx0undo.h @@ -222,13 +222,16 @@ trx_undo_lists_init( Assigns an undo log for a transaction. A new undo log is created or a cached undo log reused. */ -trx_undo_t* +ulint trx_undo_assign_undo( /*=================*/ - /* out: the undo log, NULL if did not succeed: out of - space */ - trx_t* trx, /* in: transaction */ - ulint type); /* in: TRX_UNDO_INSERT or TRX_UNDO_UPDATE */ + /* out: DB_SUCCESS if undo log assign + * successful, possible error codes are: + * ER_TOO_MANY_CONCURRENT_TRXS + * 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. */ diff --git a/row/row0mysql.c b/row/row0mysql.c index 8e0e356e3f1..9f88fd8040b 100644 --- a/row/row0mysql.c +++ b/row/row0mysql.c @@ -476,7 +476,8 @@ handle_new_error: /* MySQL will roll back the latest SQL statement */ } else if (err == DB_ROW_IS_REFERENCED || err == DB_NO_REFERENCED_ROW - || err == DB_CANNOT_ADD_CONSTRAINT) { + || err == DB_CANNOT_ADD_CONSTRAINT + || err == DB_TOO_MANY_CONCURRENT_TRXS) { if (savept) { /* Roll back the latest, possibly incomplete insertion or update */ diff --git a/trx/trx0rec.c b/trx/trx0rec.c index 69e858fe71d..50f8b011463 100644 --- a/trx/trx0rec.c +++ b/trx/trx0rec.c @@ -1024,6 +1024,7 @@ trx_undo_report_row_operation( ibool is_insert; trx_rseg_t* rseg; mtr_t mtr; + ulint err = DB_SUCCESS; mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; @@ -1035,7 +1036,7 @@ trx_undo_report_row_operation( *roll_ptr = ut_dulint_zero; - return(DB_SUCCESS); + return(err); } ut_ad(thr); @@ -1053,7 +1054,7 @@ trx_undo_report_row_operation( 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; @@ -1063,7 +1064,7 @@ trx_undo_report_row_operation( if (trx->update_undo == NULL) { - trx_undo_assign_undo(trx, TRX_UNDO_UPDATE); + err = trx_undo_assign_undo(trx, TRX_UNDO_UPDATE); } @@ -1071,11 +1072,11 @@ trx_undo_report_row_operation( is_insert = FALSE; } - if (undo == NULL) { - /* Did not succeed: out of space */ + if (err != DB_SUCCESS) { + /* Did not succeed: return the error encountered */ mutex_exit(&(trx->undo_mutex)); - return(DB_OUT_OF_FILE_SPACE); + return(err); } page_no = undo->last_page_no; @@ -1107,7 +1108,9 @@ trx_undo_report_row_operation( if (offset == 0) { /* The record did not fit on the page. We erase the end segment of the undo log page and write a log - record of it to to ensure deterministic contents. */ + record of it: this is to ensure that in the debug + version the replicate page constructed using the log + records stays identical to the original page */ trx_undo_erase_page_end(undo_page, &mtr); } @@ -1163,7 +1166,7 @@ trx_undo_report_row_operation( if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } - return(DB_SUCCESS); + return(err); } /*============== BUILDING PREVIOUS VERSION OF A RECORD ===============*/ diff --git a/trx/trx0undo.c b/trx/trx0undo.c index 831e337f513..64e5ad3c9a8 100644 --- a/trx/trx0undo.c +++ b/trx/trx0undo.c @@ -373,26 +373,31 @@ trx_undo_page_init( /******************************************************************* Creates a new undo log segment in file. */ static -page_t* +ulint trx_undo_seg_create( /*================*/ - /* out: segment header page x-latched, NULL - if no space left */ + /* out: DB_SUCCESS if page creation OK + possible error codes are: + DB_TOO_MANY_CONCURRENT_TRXS + DB_OUT_OF_FILE_SPACE */ trx_rseg_t* rseg __attribute__((unused)),/* in: rollback segment */ trx_rsegf_t* rseg_hdr,/* in: rollback segment header, page x-latched */ ulint type, /* in: type of the segment: TRX_UNDO_INSERT or TRX_UNDO_UPDATE */ 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 */ { ulint slot_no; ulint space; - page_t* undo_page; trx_upagef_t* page_hdr; trx_usegf_t* seg_hdr; ulint n_reserved; ibool success; + ulint err = DB_SUCCESS; ut_ad(mtr && id && rseg_hdr); ut_ad(mutex_own(&(rseg->mutex))); @@ -410,7 +415,7 @@ trx_undo_seg_create( "InnoDB: many active transactions" " running concurrently?\n"); - return(NULL); + return(DB_TOO_MANY_CONCURRENT_TRXS); } space = buf_frame_get_space_id(rseg_hdr); @@ -419,30 +424,30 @@ trx_undo_seg_create( mtr); if (!success) { - return(NULL); + return(DB_OUT_OF_FILE_SPACE); } /* 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); fil_space_release_free_extents(space, n_reserved); - if (undo_page == NULL) { + if (*undo_page == NULL) { /* No space left */ - return(NULL); + return(DB_OUT_OF_FILE_SPACE); } #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 */ - page_hdr = undo_page + TRX_UNDO_PAGE_HDR; - seg_hdr = undo_page + TRX_UNDO_SEG_HDR; + page_hdr = *undo_page + TRX_UNDO_PAGE_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, TRX_UNDO_SEG_HDR + TRX_UNDO_SEG_HDR_SIZE, @@ -456,10 +461,11 @@ trx_undo_seg_create( page_hdr + TRX_UNDO_PAGE_NODE, mtr); 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; - return(undo_page); + return(err); } /************************************************************************** @@ -1387,6 +1393,11 @@ trx_undo_mem_create( undo = mem_alloc(sizeof(trx_undo_t)); + if (undo == NULL) { + + return NULL; + } + undo->id = id; undo->type = type; undo->state = TRX_UNDO_ACTIVE; @@ -1464,11 +1475,15 @@ trx_undo_mem_free( /************************************************************************** Creates a new undo log. */ static -trx_undo_t* +ulint trx_undo_create( /*============*/ - /* out: undo log object, NULL if did not - succeed: out of space */ + /* out: DB_SUCCESS if successful in creating + 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_rseg_t* rseg, /* in: rollback segment memory copy */ ulint type, /* in: type of the log: TRX_UNDO_INSERT or @@ -1476,34 +1491,37 @@ trx_undo_create( dulint trx_id, /* in: id of the trx for which the undo log is created */ 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 */ { trx_rsegf_t* rseg_header; ulint page_no; ulint offset; ulint id; - trx_undo_t* undo; page_t* undo_page; + ulint err; ut_ad(mutex_own(&(rseg->mutex))); if (rseg->curr_size == rseg->max_size) { - return(NULL); + return(DB_OUT_OF_FILE_SPACE); } rseg->curr_size++; 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 */ rseg->curr_size--; - return(NULL); + return(err); } page_no = buf_frame_get_page_no(undo_page); @@ -1515,9 +1533,14 @@ trx_undo_create( 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); - return(undo); + if (*undo == NULL) { + + err = DB_OUT_OF_MEMORY; + } + + return(err); } /*================ UNDO LOG ASSIGNMENT AND CLEANUP =====================*/ @@ -1634,17 +1657,20 @@ trx_undo_mark_as_dict_operation( Assigns an undo log for a transaction. A new undo log is created or a cached undo log reused. */ -trx_undo_t* +ulint trx_undo_assign_undo( /*=================*/ - /* out: the undo log, NULL if did not succeed: out of - space */ - trx_t* trx, /* in: transaction */ - ulint type) /* in: TRX_UNDO_INSERT or TRX_UNDO_UPDATE */ + /* out: DB_SUCCESS if undo log assign + successful, possible error codes are: + DD_TOO_MANY_CONCURRENT_TRXS + 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_undo_t* undo; mtr_t mtr; + ulint err = DB_SUCCESS; ut_ad(trx); ut_ad(trx->rseg); @@ -1662,15 +1688,11 @@ trx_undo_assign_undo( undo = trx_undo_reuse_cached(trx, rseg, type, trx->id, &trx->xid, &mtr); if (undo == NULL) { - undo = trx_undo_create(trx, rseg, type, trx->id, &trx->xid, - &mtr); - if (undo == NULL) { - /* Did not succeed */ + err = trx_undo_create(trx, rseg, type, trx->id, &trx->xid, + &undo, &mtr); + if (err != DB_SUCCESS) { - mutex_exit(&(rseg->mutex)); - mtr_commit(&mtr); - - return(NULL); + goto func_exit; } } @@ -1688,10 +1710,11 @@ trx_undo_assign_undo( trx_undo_mark_as_dict_operation(trx, undo, &mtr); } +func_exit: mutex_exit(&(rseg->mutex)); mtr_commit(&mtr); - return(undo); + return err; } /**********************************************************************