Many files:

  Fix bug #6817 and bug #6827 : do not crash the server if the buffer pool becomes filled by the locks of ONE huge transaction, return error 1206 instead; do not crash the server, but return error if we cannot allocate memory for the buffer pool at a mysqld startup
parent a3d2aaf9
...@@ -547,8 +547,9 @@ buf_pool_init( ...@@ -547,8 +547,9 @@ buf_pool_init(
} }
/*----------------------------------------*/ /*----------------------------------------*/
} else { } else {
buf_pool->frame_mem = ut_malloc( buf_pool->frame_mem = ut_malloc_low(
UNIV_PAGE_SIZE * (n_frames + 1)); UNIV_PAGE_SIZE * (n_frames + 1),
TRUE, FALSE);
} }
if (buf_pool->frame_mem == NULL) { if (buf_pool->frame_mem == NULL) {
......
...@@ -42,6 +42,10 @@ initial segment in buf_LRU_get_recent_limit */ ...@@ -42,6 +42,10 @@ initial segment in buf_LRU_get_recent_limit */
#define BUF_LRU_INITIAL_RATIO 8 #define BUF_LRU_INITIAL_RATIO 8
/* If we switch on the InnoDB monitor because there are too few available
frames in the buffer pool, we set this to TRUE */
ibool buf_lru_switched_on_innodb_mon = FALSE;
/********************************************************************** /**********************************************************************
Takes a block out of the LRU list and page hash table and sets the block Takes a block out of the LRU list and page hash table and sets the block
state to BUF_BLOCK_REMOVE_HASH. */ state to BUF_BLOCK_REMOVE_HASH. */
...@@ -287,6 +291,32 @@ buf_LRU_try_free_flushed_blocks(void) ...@@ -287,6 +291,32 @@ buf_LRU_try_free_flushed_blocks(void)
mutex_exit(&(buf_pool->mutex)); mutex_exit(&(buf_pool->mutex));
} }
/**********************************************************************
Returns TRUE if less than 15 % of the buffer pool is available. This can be
used in heuristics to prevent huge transactions eating up the whole buffer
pool for their locks. */
ibool
buf_LRU_buf_pool_running_out(void)
/*==============================*/
/* out: TRUE if less than 15 % of buffer pool
left */
{
ibool ret = FALSE;
mutex_enter(&(buf_pool->mutex));
if (!recv_recovery_on && UT_LIST_GET_LEN(buf_pool->free)
+ UT_LIST_GET_LEN(buf_pool->LRU) < buf_pool->max_size / 7) {
ret = TRUE;
}
mutex_exit(&(buf_pool->mutex));
return(ret);
}
/********************************************************************** /**********************************************************************
Returns a free block from buf_pool. The block is taken off the free list. Returns a free block from buf_pool. The block is taken off the free list.
If it is empty, blocks are moved from the end of the LRU list to the free If it is empty, blocks are moved from the end of the LRU list to the free
...@@ -325,7 +355,8 @@ loop: ...@@ -325,7 +355,8 @@ loop:
} else if (!recv_recovery_on && UT_LIST_GET_LEN(buf_pool->free) } else if (!recv_recovery_on && UT_LIST_GET_LEN(buf_pool->free)
+ UT_LIST_GET_LEN(buf_pool->LRU) < buf_pool->max_size / 5) { + UT_LIST_GET_LEN(buf_pool->LRU) < buf_pool->max_size / 5) {
if (!srv_print_innodb_monitor) {
if (!buf_lru_switched_on_innodb_mon) {
/* Over 80 % of the buffer pool is occupied by lock /* Over 80 % of the buffer pool is occupied by lock
heaps or the adaptive hash index. This may be a memory heaps or the adaptive hash index. This may be a memory
...@@ -342,16 +373,18 @@ loop: ...@@ -342,16 +373,18 @@ loop:
"InnoDB: lock heap and hash index sizes.\n", "InnoDB: lock heap and hash index sizes.\n",
(ulong) (buf_pool->curr_size / (1024 * 1024 / UNIV_PAGE_SIZE))); (ulong) (buf_pool->curr_size / (1024 * 1024 / UNIV_PAGE_SIZE)));
buf_lru_switched_on_innodb_mon = TRUE;
srv_print_innodb_monitor = TRUE; srv_print_innodb_monitor = TRUE;
os_event_set(srv_lock_timeout_thread_event); os_event_set(srv_lock_timeout_thread_event);
} }
} else if (!recv_recovery_on && UT_LIST_GET_LEN(buf_pool->free) } else if (buf_lru_switched_on_innodb_mon) {
+ UT_LIST_GET_LEN(buf_pool->LRU) < buf_pool->max_size / 4) {
/* Switch off the InnoDB Monitor; this is a simple way /* Switch off the InnoDB Monitor; this is a simple way
to stop the monitor if the situation becomes less urgent, to stop the monitor if the situation becomes less urgent,
but may also surprise users! */ but may also surprise users if the user also switched on the
monitor! */
buf_lru_switched_on_innodb_mon = FALSE;
srv_print_innodb_monitor = FALSE; srv_print_innodb_monitor = FALSE;
} }
......
...@@ -25,6 +25,16 @@ wasted. */ ...@@ -25,6 +25,16 @@ wasted. */
void void
buf_LRU_try_free_flushed_blocks(void); buf_LRU_try_free_flushed_blocks(void);
/*==================================*/ /*==================================*/
/**********************************************************************
Returns TRUE if less than 15 % of the buffer pool is available. This can be
used in heuristics to prevent huge transactions eating up the whole buffer
pool for their locks. */
ibool
buf_LRU_buf_pool_running_out(void);
/*==============================*/
/* out: TRUE if less than 15 % of buffer pool
left */
/*####################################################################### /*#######################################################################
These are low-level functions These are low-level functions
......
...@@ -53,6 +53,10 @@ Created 5/24/1996 Heikki Tuuri ...@@ -53,6 +53,10 @@ Created 5/24/1996 Heikki Tuuri
name already exists */ name already exists */
#define DB_TABLESPACE_DELETED 44 /* tablespace does not exist or is #define DB_TABLESPACE_DELETED 44 /* tablespace does not exist or is
being dropped right now */ being dropped right now */
#define DB_LOCK_TABLE_FULL 45 /* lock structs have exhausted the
buffer pool (for big transactions,
InnoDB stores the lock structs in the
buffer pool) */
/* The following are partial failure codes */ /* The following are partial failure codes */
#define DB_FAIL 1000 #define DB_FAIL 1000
......
...@@ -120,6 +120,7 @@ row_search_for_mysql( ...@@ -120,6 +120,7 @@ row_search_for_mysql(
/* out: DB_SUCCESS, /* out: DB_SUCCESS,
DB_RECORD_NOT_FOUND, DB_RECORD_NOT_FOUND,
DB_END_OF_INDEX, DB_DEADLOCK, DB_END_OF_INDEX, DB_DEADLOCK,
DB_LOCK_TABLE_FULL,
or DB_TOO_BIG_RECORD */ or DB_TOO_BIG_RECORD */
byte* buf, /* in/out: buffer for the fetched byte* buf, /* in/out: buffer for the fetched
row in the MySQL format */ row in the MySQL format */
......
...@@ -38,8 +38,10 @@ ut_malloc_low( ...@@ -38,8 +38,10 @@ ut_malloc_low(
/*==========*/ /*==========*/
/* out, own: allocated memory */ /* out, own: allocated memory */
ulint n, /* in: number of bytes to allocate */ ulint n, /* in: number of bytes to allocate */
ibool set_to_zero); /* in: TRUE if allocated memory should be set ibool set_to_zero, /* in: TRUE if allocated memory should be set
to zero if UNIV_SET_MEM_TO_ZERO is defined */ to zero if UNIV_SET_MEM_TO_ZERO is defined */
ibool assert_on_error); /* in: if TRUE, we crash mysqld if the memory
cannot be allocated */
/************************************************************************** /**************************************************************************
Allocates memory. Sets it also to zero if UNIV_SET_MEM_TO_ZERO is Allocates memory. Sets it also to zero if UNIV_SET_MEM_TO_ZERO is
defined. */ defined. */
......
...@@ -199,7 +199,7 @@ mem_pool_create( ...@@ -199,7 +199,7 @@ mem_pool_create(
but only when allocated at a higher level in mem0mem.c. but only when allocated at a higher level in mem0mem.c.
This is to avoid masking useful Purify warnings. */ This is to avoid masking useful Purify warnings. */
pool->buf = ut_malloc_low(size, FALSE); pool->buf = ut_malloc_low(size, FALSE, TRUE);
pool->size = size; pool->size = size;
mutex_create(&(pool->mutex)); mutex_create(&(pool->mutex));
......
...@@ -308,7 +308,8 @@ handle_new_error: ...@@ -308,7 +308,8 @@ handle_new_error:
return(TRUE); return(TRUE);
} else if (err == DB_DEADLOCK || err == DB_LOCK_WAIT_TIMEOUT) { } else if (err == DB_DEADLOCK || err == DB_LOCK_WAIT_TIMEOUT
|| err == DB_LOCK_TABLE_FULL) {
/* Roll back the whole transaction; this resolution was added /* Roll back the whole transaction; this resolution was added
to version 3.23.43 */ to version 3.23.43 */
......
...@@ -730,8 +730,18 @@ sel_set_rec_lock( ...@@ -730,8 +730,18 @@ sel_set_rec_lock(
ulint type, /* in: LOCK_ORDINARY, LOCK_GAP, or LOC_REC_NOT_GAP */ ulint type, /* in: LOCK_ORDINARY, LOCK_GAP, or LOC_REC_NOT_GAP */
que_thr_t* thr) /* in: query thread */ que_thr_t* thr) /* in: query thread */
{ {
trx_t* trx;
ulint err; ulint err;
trx = thr_get_trx(thr);
if (UT_LIST_GET_LEN(trx->trx_locks) > 10000) {
if (buf_LRU_buf_pool_running_out()) {
return(DB_LOCK_TABLE_FULL);
}
}
if (index->type & DICT_CLUSTERED) { if (index->type & DICT_CLUSTERED) {
err = lock_clust_rec_read_check_and_lock(0, rec, index, mode, err = lock_clust_rec_read_check_and_lock(0, rec, index, mode,
type, thr); type, thr);
...@@ -2765,6 +2775,7 @@ row_search_for_mysql( ...@@ -2765,6 +2775,7 @@ row_search_for_mysql(
/* out: DB_SUCCESS, /* out: DB_SUCCESS,
DB_RECORD_NOT_FOUND, DB_RECORD_NOT_FOUND,
DB_END_OF_INDEX, DB_DEADLOCK, DB_END_OF_INDEX, DB_DEADLOCK,
DB_LOCK_TABLE_FULL,
or DB_TOO_BIG_RECORD */ or DB_TOO_BIG_RECORD */
byte* buf, /* in/out: buffer for the fetched byte* buf, /* in/out: buffer for the fetched
row in the MySQL format */ row in the MySQL format */
......
...@@ -1172,6 +1172,9 @@ NetWare. */ ...@@ -1172,6 +1172,9 @@ NetWare. */
} }
if (ret == NULL) { if (ret == NULL) {
fprintf(stderr,
"InnoDB: Fatal error: cannot allocate the memory for the buffer pool\n");
return(DB_ERROR); return(DB_ERROR);
} }
......
...@@ -61,8 +61,10 @@ ut_malloc_low( ...@@ -61,8 +61,10 @@ ut_malloc_low(
/*==========*/ /*==========*/
/* out, own: allocated memory */ /* out, own: allocated memory */
ulint n, /* in: number of bytes to allocate */ ulint n, /* in: number of bytes to allocate */
ibool set_to_zero) /* in: TRUE if allocated memory should be set ibool set_to_zero, /* in: TRUE if allocated memory should be set
to zero if UNIV_SET_MEM_TO_ZERO is defined */ to zero if UNIV_SET_MEM_TO_ZERO is defined */
ibool assert_on_error) /* in: if TRUE, we crash mysqld if the memory
cannot be allocated */
{ {
void* ret; void* ret;
...@@ -86,9 +88,7 @@ ut_malloc_low( ...@@ -86,9 +88,7 @@ ut_malloc_low(
"InnoDB: Check if you should increase the swap file or\n" "InnoDB: Check if you should increase the swap file or\n"
"InnoDB: ulimits of your operating system.\n" "InnoDB: ulimits of your operating system.\n"
"InnoDB: On FreeBSD check you have compiled the OS with\n" "InnoDB: On FreeBSD check you have compiled the OS with\n"
"InnoDB: a big enough maximum process size.\n" "InnoDB: a big enough maximum process size.\n",
"InnoDB: We now intentionally generate a seg fault so that\n"
"InnoDB: on Linux we get a stack trace.\n",
(ulong) n, (ulong) ut_total_allocated_memory, (ulong) n, (ulong) ut_total_allocated_memory,
#ifdef __WIN__ #ifdef __WIN__
(ulong) GetLastError() (ulong) GetLastError()
...@@ -110,7 +110,15 @@ ut_malloc_low( ...@@ -110,7 +110,15 @@ ut_malloc_low(
/* Intentional segfault on NetWare causes an abend. Avoid this /* Intentional segfault on NetWare causes an abend. Avoid this
by graceful exit handling in ut_a(). */ by graceful exit handling in ut_a(). */
#if (!defined __NETWARE__) #if (!defined __NETWARE__)
if (assert_on_error) {
fprintf(stderr,
"InnoDB: We now intentionally generate a seg fault so that\n"
"InnoDB: on Linux we get a stack trace.\n");
if (*ut_mem_null_ptr) ut_mem_null_ptr = 0; if (*ut_mem_null_ptr) ut_mem_null_ptr = 0;
} else {
return(NULL);
}
#else #else
ut_a(0); ut_a(0);
#endif #endif
...@@ -144,7 +152,7 @@ ut_malloc( ...@@ -144,7 +152,7 @@ ut_malloc(
/* out, own: allocated memory */ /* out, own: allocated memory */
ulint n) /* in: number of bytes to allocate */ ulint n) /* in: number of bytes to allocate */
{ {
return(ut_malloc_low(n, TRUE)); return(ut_malloc_low(n, TRUE, TRUE));
} }
/************************************************************************** /**************************************************************************
......
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