summaryrefslogtreecommitdiff
path: root/innobase
diff options
context:
space:
mode:
Diffstat (limited to 'innobase')
-rw-r--r--innobase/buf/buf0buf.c5
-rw-r--r--innobase/buf/buf0lru.c41
-rw-r--r--innobase/configure.in3
-rw-r--r--innobase/include/buf0lru.h10
-rw-r--r--innobase/include/db0err.h6
-rw-r--r--innobase/include/row0sel.h1
-rw-r--r--innobase/include/ut0mem.h4
-rw-r--r--innobase/lock/lock0lock.c3
-rw-r--r--innobase/mem/mem0pool.c2
-rw-r--r--innobase/row/row0mysql.c3
-rw-r--r--innobase/row/row0sel.c145
-rw-r--r--innobase/srv/srv0start.c3
-rw-r--r--innobase/trx/trx0purge.c23
-rw-r--r--innobase/trx/trx0undo.c2
-rw-r--r--innobase/ut/ut0mem.c20
15 files changed, 191 insertions, 80 deletions
diff --git a/innobase/buf/buf0buf.c b/innobase/buf/buf0buf.c
index adb69f3c3a7..699ad5fb42e 100644
--- a/innobase/buf/buf0buf.c
+++ b/innobase/buf/buf0buf.c
@@ -547,8 +547,9 @@ buf_pool_init(
}
/*----------------------------------------*/
} else {
- buf_pool->frame_mem = ut_malloc(
- UNIV_PAGE_SIZE * (n_frames + 1));
+ buf_pool->frame_mem = ut_malloc_low(
+ UNIV_PAGE_SIZE * (n_frames + 1),
+ TRUE, FALSE);
}
if (buf_pool->frame_mem == NULL) {
diff --git a/innobase/buf/buf0lru.c b/innobase/buf/buf0lru.c
index 796311f0157..f3fb19ae183 100644
--- a/innobase/buf/buf0lru.c
+++ b/innobase/buf/buf0lru.c
@@ -42,6 +42,10 @@ initial segment in buf_LRU_get_recent_limit */
#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
state to BUF_BLOCK_REMOVE_HASH. */
@@ -288,6 +292,32 @@ 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 */
+{
+ 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.
If it is empty, blocks are moved from the end of the LRU list to the free
list. */
@@ -325,7 +355,8 @@ loop:
} else if (!recv_recovery_on && UT_LIST_GET_LEN(buf_pool->free)
+ 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
heaps or the adaptive hash index. This may be a memory
@@ -342,16 +373,18 @@ loop:
"InnoDB: lock heap and hash index sizes.\n",
(ulong) (buf_pool->curr_size / (1024 * 1024 / UNIV_PAGE_SIZE)));
+ buf_lru_switched_on_innodb_mon = TRUE;
srv_print_innodb_monitor = TRUE;
os_event_set(srv_lock_timeout_thread_event);
}
- } else if (!recv_recovery_on && UT_LIST_GET_LEN(buf_pool->free)
- + UT_LIST_GET_LEN(buf_pool->LRU) < buf_pool->max_size / 4) {
+ } else if (buf_lru_switched_on_innodb_mon) {
/* Switch off the InnoDB Monitor; this is a simple way
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;
}
diff --git a/innobase/configure.in b/innobase/configure.in
index 652291f1f38..d83da9fdc5c 100644
--- a/innobase/configure.in
+++ b/innobase/configure.in
@@ -110,6 +110,9 @@ esac
case "$target" in
i[[4567]]86-*-*)
CFLAGS="$CFLAGS -DUNIV_INTEL_X86";;
+ # The compiler on Linux/S390 does not seem to have inlining
+ s390-*-*)
+ CFLAGS="$CFLAGS -DUNIV_MUST_NOT_INLINE";;
esac
AC_OUTPUT(Makefile os/Makefile ut/Makefile btr/Makefile dnl
diff --git a/innobase/include/buf0lru.h b/innobase/include/buf0lru.h
index 69a376f8cab..45164dd561e 100644
--- a/innobase/include/buf0lru.h
+++ b/innobase/include/buf0lru.h
@@ -25,6 +25,16 @@ wasted. */
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
diff --git a/innobase/include/db0err.h b/innobase/include/db0err.h
index be7667bfd0c..de5ac44e73f 100644
--- a/innobase/include/db0err.h
+++ b/innobase/include/db0err.h
@@ -53,7 +53,11 @@ Created 5/24/1996 Heikki Tuuri
name already exists */
#define DB_TABLESPACE_DELETED 44 /* tablespace does not exist or is
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 */
#define DB_FAIL 1000
#define DB_OVERFLOW 1001
diff --git a/innobase/include/row0sel.h b/innobase/include/row0sel.h
index bb6fb70ca86..8d5187bfc1c 100644
--- a/innobase/include/row0sel.h
+++ b/innobase/include/row0sel.h
@@ -120,6 +120,7 @@ row_search_for_mysql(
/* out: DB_SUCCESS,
DB_RECORD_NOT_FOUND,
DB_END_OF_INDEX, DB_DEADLOCK,
+ DB_LOCK_TABLE_FULL,
or DB_TOO_BIG_RECORD */
byte* buf, /* in/out: buffer for the fetched
row in the MySQL format */
diff --git a/innobase/include/ut0mem.h b/innobase/include/ut0mem.h
index 73ecb25101a..74357f6bf13 100644
--- a/innobase/include/ut0mem.h
+++ b/innobase/include/ut0mem.h
@@ -38,8 +38,10 @@ ut_malloc_low(
/*==========*/
/* out, own: allocated memory */
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 */
+ 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
defined. */
diff --git a/innobase/lock/lock0lock.c b/innobase/lock/lock0lock.c
index 6f2d58b72c3..78a78c9dd95 100644
--- a/innobase/lock/lock0lock.c
+++ b/innobase/lock/lock0lock.c
@@ -4064,6 +4064,9 @@ lock_print_info(
(ulong) ut_dulint_get_low(purge_sys->purge_undo_no));
fprintf(file,
+ "History list length %lu\n", (ulong) trx_sys->rseg_history_len);
+
+ fprintf(file,
"Total number of lock structs in row lock hash table %lu\n",
(ulong) lock_get_n_rec_locks());
diff --git a/innobase/mem/mem0pool.c b/innobase/mem/mem0pool.c
index 023369e8ec5..cb891a03092 100644
--- a/innobase/mem/mem0pool.c
+++ b/innobase/mem/mem0pool.c
@@ -199,7 +199,7 @@ mem_pool_create(
but only when allocated at a higher level in mem0mem.c.
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;
mutex_create(&(pool->mutex));
diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c
index 17747ae2a8e..9613da2e286 100644
--- a/innobase/row/row0mysql.c
+++ b/innobase/row/row0mysql.c
@@ -308,7 +308,8 @@ handle_new_error:
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
to version 3.23.43 */
diff --git a/innobase/row/row0sel.c b/innobase/row/row0sel.c
index 740241fa210..edaa555c555 100644
--- a/innobase/row/row0sel.c
+++ b/innobase/row/row0sel.c
@@ -638,23 +638,24 @@ row_sel_get_clust_rec(
if (!node->read_view) {
/* Try to place a lock on the index record */
- /* If innodb_locks_unsafe_for_binlog option is used,
- we lock only the record, i.e. next-key locking is
- not used.
- */
- if ( srv_locks_unsafe_for_binlog )
- {
- err = lock_clust_rec_read_check_and_lock(0, clust_rec,
- index,node->row_lock_mode, LOCK_REC_NOT_GAP, thr);
- }
- else
- {
- err = lock_clust_rec_read_check_and_lock(0, clust_rec, index,
- node->row_lock_mode, LOCK_ORDINARY, thr);
-
- }
-
- if (err != DB_SUCCESS) {
+ /* If innodb_locks_unsafe_for_binlog option is used,
+ we lock only the record, i.e. next-key locking is
+ not used.
+ */
+
+ if (srv_locks_unsafe_for_binlog) {
+ err = lock_clust_rec_read_check_and_lock(0,
+ clust_rec,
+ index, node->row_lock_mode,
+ LOCK_REC_NOT_GAP, thr);
+ } else {
+ err = lock_clust_rec_read_check_and_lock(0,
+ clust_rec,
+ index, node->row_lock_mode,
+ LOCK_ORDINARY, thr);
+ }
+
+ if (err != DB_SUCCESS) {
return(err);
}
@@ -729,8 +730,18 @@ sel_set_rec_lock(
ulint type, /* in: LOCK_ORDINARY, LOCK_GAP, or LOC_REC_NOT_GAP */
que_thr_t* thr) /* in: query thread */
{
+ trx_t* trx;
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) {
err = lock_clust_rec_read_check_and_lock(0, rec, index, mode,
type, thr);
@@ -1205,22 +1216,24 @@ rec_loop:
if (!consistent_read) {
- /* If innodb_locks_unsafe_for_binlog option is used,
- we lock only the record, i.e. next-key locking is
- not used.
- */
-
- if ( srv_locks_unsafe_for_binlog )
- {
- err = sel_set_rec_lock(page_rec_get_next(rec), index,
- node->row_lock_mode, LOCK_REC_NOT_GAP, thr);
- }
- else
- {
- err = sel_set_rec_lock(page_rec_get_next(rec), index,
- node->row_lock_mode, LOCK_ORDINARY, thr);
- }
- if (err != DB_SUCCESS) {
+ /* If innodb_locks_unsafe_for_binlog option is used,
+ we lock only the record, i.e. next-key locking is
+ not used.
+ */
+
+ if (srv_locks_unsafe_for_binlog) {
+ err = sel_set_rec_lock(page_rec_get_next(rec),
+ index,
+ node->row_lock_mode,
+ LOCK_REC_NOT_GAP, thr);
+ } else {
+ err = sel_set_rec_lock(page_rec_get_next(rec),
+ index,
+ node->row_lock_mode,
+ LOCK_ORDINARY, thr);
+ }
+
+ if (err != DB_SUCCESS) {
/* Note that in this case we will store in pcur
the PREDECESSOR of the record we are waiting
the lock for */
@@ -1245,21 +1258,18 @@ rec_loop:
if (!consistent_read) {
/* Try to place a lock on the index record */
- /* If innodb_locks_unsafe_for_binlog option is used,
- we lock only the record, i.e. next-key locking is
- not used.
- */
+ /* If innodb_locks_unsafe_for_binlog option is used,
+ we lock only the record, i.e. next-key locking is
+ not used.
+ */
- if ( srv_locks_unsafe_for_binlog )
- {
- err = sel_set_rec_lock(rec, index, node->row_lock_mode,
+ if (srv_locks_unsafe_for_binlog) {
+ err = sel_set_rec_lock(rec, index, node->row_lock_mode,
LOCK_REC_NOT_GAP, thr);
- }
- else
- {
- err = sel_set_rec_lock(rec, index, node->row_lock_mode,
+ } else {
+ err = sel_set_rec_lock(rec, index, node->row_lock_mode,
LOCK_ORDINARY, thr);
- }
+ }
if (err != DB_SUCCESS) {
@@ -2765,6 +2775,7 @@ row_search_for_mysql(
/* out: DB_SUCCESS,
DB_RECORD_NOT_FOUND,
DB_END_OF_INDEX, DB_DEADLOCK,
+ DB_LOCK_TABLE_FULL,
or DB_TOO_BIG_RECORD */
byte* buf, /* in/out: buffer for the fetched
row in the MySQL format */
@@ -3209,8 +3220,7 @@ rec_loop:
we do not lock gaps. Supremum record is really
a gap and therefore we do not set locks there. */
- if ( srv_locks_unsafe_for_binlog == FALSE )
- {
+ if (srv_locks_unsafe_for_binlog == FALSE) {
err = sel_set_rec_lock(rec, index,
prebuilt->select_lock_type,
LOCK_ORDINARY, thr);
@@ -3312,11 +3322,18 @@ rec_loop:
if (prebuilt->select_lock_type != LOCK_NONE
&& set_also_gap_locks) {
- /* Try to place a lock on the index record */
- err = sel_set_rec_lock(rec, index,
+ /* Try to place a gap lock on the index
+ record only if innodb_locks_unsafe_for_binlog
+ option is not set */
+
+ if (srv_locks_unsafe_for_binlog == FALSE) {
+
+ err = sel_set_rec_lock(rec, index,
prebuilt->select_lock_type,
LOCK_GAP, thr);
+ }
+
if (err != DB_SUCCESS) {
goto lock_wait_or_error;
@@ -3338,11 +3355,18 @@ rec_loop:
if (prebuilt->select_lock_type != LOCK_NONE
&& set_also_gap_locks) {
- /* Try to place a lock on the index record */
- err = sel_set_rec_lock(rec, index,
+ /* Try to place a gap lock on the index
+ record only if innodb_locks_unsafe_for_binlog
+ option is not set */
+
+ if (srv_locks_unsafe_for_binlog == FALSE) {
+
+ err = sel_set_rec_lock(rec, index,
prebuilt->select_lock_type,
LOCK_GAP, thr);
+ }
+
if (err != DB_SUCCESS) {
goto lock_wait_or_error;
@@ -3376,19 +3400,16 @@ rec_loop:
prebuilt->select_lock_type,
LOCK_REC_NOT_GAP, thr);
} else {
- /* If innodb_locks_unsafe_for_binlog option is used,
- we lock only the record, i.e. next-key locking is
- not used.
- */
- if ( srv_locks_unsafe_for_binlog )
- {
- err = sel_set_rec_lock(rec, index,
+ /* If innodb_locks_unsafe_for_binlog option is used,
+ we lock only the record, i.e. next-key locking is
+ not used. */
+
+ if (srv_locks_unsafe_for_binlog) {
+ err = sel_set_rec_lock(rec, index,
prebuilt->select_lock_type,
LOCK_REC_NOT_GAP, thr);
- }
- else
- {
- err = sel_set_rec_lock(rec, index,
+ } else {
+ err = sel_set_rec_lock(rec, index,
prebuilt->select_lock_type,
LOCK_ORDINARY, thr);
}
diff --git a/innobase/srv/srv0start.c b/innobase/srv/srv0start.c
index 9709f5235de..69341a1d7d1 100644
--- a/innobase/srv/srv0start.c
+++ b/innobase/srv/srv0start.c
@@ -1172,6 +1172,9 @@ NetWare. */
}
if (ret == NULL) {
+ fprintf(stderr,
+"InnoDB: Fatal error: cannot allocate the memory for the buffer pool\n");
+
return(DB_ERROR);
}
diff --git a/innobase/trx/trx0purge.c b/innobase/trx/trx0purge.c
index 5c62640e011..3df34111281 100644
--- a/innobase/trx/trx0purge.c
+++ b/innobase/trx/trx0purge.c
@@ -289,7 +289,7 @@ trx_purge_add_update_undo_to_history(
flst_get_len(seg_header + TRX_UNDO_PAGE_LIST, mtr));
mlog_write_ulint(rseg_header + TRX_RSEG_HISTORY_SIZE,
- hist_size + undo->size, MLOG_4BYTES, mtr);
+ hist_size + undo->size, MLOG_4BYTES, mtr);
}
/* Add the log as the first in the history list */
@@ -646,6 +646,27 @@ trx_purge_rseg_get_next_history_log(
mutex_exit(&(rseg->mutex));
mtr_commit(&mtr);
+ mutex_enter(&kernel_mutex);
+
+ /* Add debug code to track history list corruption reported
+ on the MySQL mailing list on Nov 9, 2004. The fut0lst.c
+ file-based list was corrupt. The prev node pointer was
+ FIL_NULL, even though the list length was over 8 million nodes!
+ We assume that purge truncates the history list in moderate
+ size pieces, and if we here reach the head of the list, the
+ list cannot be longer than 20 000 undo logs now. */
+
+ if (trx_sys->rseg_history_len > 20000) {
+ ut_print_timestamp(stderr);
+ fprintf(stderr,
+" InnoDB: Warning: purge reached the head of the history list,\n"
+"InnoDB: but its length is still reported as %lu! Make a detailed bug\n"
+"InnoDB: report, and post it to bugs.mysql.com\n",
+ (ulong)trx_sys->rseg_history_len);
+ }
+
+ mutex_exit(&kernel_mutex);
+
return;
}
diff --git a/innobase/trx/trx0undo.c b/innobase/trx/trx0undo.c
index c1edc223cbc..8d1518753dd 100644
--- a/innobase/trx/trx0undo.c
+++ b/innobase/trx/trx0undo.c
@@ -1241,7 +1241,7 @@ trx_undo_lists_init(
if (page_no != FIL_NULL
&& srv_force_recovery < SRV_FORCE_NO_UNDO_LOG_SCAN) {
-
+
undo = trx_undo_mem_create_at_db_start(rseg, i,
page_no, &mtr);
size += undo->size;
diff --git a/innobase/ut/ut0mem.c b/innobase/ut/ut0mem.c
index a6002d7fd83..6ed61b0b5de 100644
--- a/innobase/ut/ut0mem.c
+++ b/innobase/ut/ut0mem.c
@@ -61,8 +61,10 @@ ut_malloc_low(
/*==========*/
/* out, own: allocated memory */
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 */
+ ibool assert_on_error) /* in: if TRUE, we crash mysqld if the memory
+ cannot be allocated */
{
void* ret;
@@ -86,9 +88,7 @@ ut_malloc_low(
"InnoDB: Check if you should increase the swap file or\n"
"InnoDB: ulimits of your operating system.\n"
"InnoDB: On FreeBSD check you have compiled the OS with\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",
+ "InnoDB: a big enough maximum process size.\n",
(ulong) n, (ulong) ut_total_allocated_memory,
#ifdef __WIN__
(ulong) GetLastError()
@@ -110,7 +110,15 @@ ut_malloc_low(
/* Intentional segfault on NetWare causes an abend. Avoid this
by graceful exit handling in ut_a(). */
#if (!defined __NETWARE__)
- if (*ut_mem_null_ptr) ut_mem_null_ptr = 0;
+ 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;
+ } else {
+ return(NULL);
+ }
#else
ut_a(0);
#endif
@@ -144,7 +152,7 @@ ut_malloc(
/* out, own: allocated memory */
ulint n) /* in: number of bytes to allocate */
{
- return(ut_malloc_low(n, TRUE));
+ return(ut_malloc_low(n, TRUE, TRUE));
}
/**************************************************************************