Commit c663a941 authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-14154: Failing assertion: slot->last_run <= current_time in fts0opt.cc

The FTS optimizer thread made a false assumption that time(NULL)
is monotonic. The system clock can be adjusted to the past,
for example if the hardware clock was drifting to the future,
and it was adjusted by NTP.

fts_slot_t::interval_time: Replace with the constant
FTS_OPTIMIZE_INTERVAL_IN_SECS.

fts_slot_t::last_run, fts_slot_t::completed: Clarify the
documentation.

fts_optimize_get_time_limit(): Remove a type cast, and
add a FIXME comment about domain mismatch.

fts_optimize_compact(), fts_optimize_words(): Limit the time
also when the current time has been moved to the past.

fts_optimize_table_bk(): Check for wrap-around.

fts_optimize_how_many(): Check for wrap-around, and remove the
failing assertions.

fts_is_sync_needed(): Remove a redundant call to time(NULL).
parent 9e5df967
......@@ -59,7 +59,7 @@ static bool fts_opt_start_shutdown = false;
static const ulint FTS_WORD_NODES_INIT_SIZE = 64;
/** Last time we did check whether system need a sync */
static ib_time_t last_check_sync_time;
static time_t last_check_sync_time;
/** FTS optimize thread message types. */
enum fts_msg_type_t {
......@@ -181,12 +181,11 @@ struct fts_slot_t {
ulint deleted; /*!< Number of doc ids deleted since the
last time this table was optimized */
ib_time_t last_run; /*!< Time last run completed */
/** time(NULL) of completing fts_optimize_table_bk() */
time_t last_run;
ib_time_t completed; /*!< Optimize finish time */
ib_time_t interval_time; /*!< Minimum time to wait before
optimizing the table again. */
/** time(NULL) of latest successful fts_optimize_table() */
time_t completed;
};
/** A table remove message for the FTS optimize thread. */
......@@ -223,8 +222,8 @@ UNIV_INTERN char fts_enable_diag_print;
/** ZLib compressed block size.*/
static ulint FTS_ZIP_BLOCK_SIZE = 1024;
/** The amount of time optimizing in a single pass, in milliseconds. */
static ib_time_t fts_optimize_time_limit = 0;
/** The amount of time optimizing in a single pass, in seconds. */
static ulint fts_optimize_time_limit;
/** It's defined in fts0fts.cc */
extern const char* fts_common_tables[];
......@@ -1553,7 +1552,7 @@ fts_optimize_compact(
/*=================*/
fts_optimize_t* optim, /*!< in: optimize state data */
dict_index_t* index, /*!< in: current FTS being optimized */
ib_time_t start_time) /*!< in: optimize start time */
time_t start_time) /*!< in: optimize start time */
{
ulint i;
dberr_t error = DB_SUCCESS;
......@@ -1586,8 +1585,11 @@ fts_optimize_compact(
/* Free the word that was optimized. */
fts_word_free(word);
ulint interval = ulint(time(NULL) - start_time);
if (fts_optimize_time_limit > 0
&& (ut_time() - start_time) > fts_optimize_time_limit) {
&& (lint(interval) < 0
|| interval > fts_optimize_time_limit)) {
optim->done = TRUE;
}
......@@ -1678,7 +1680,7 @@ fts_optimize_get_index_end_time(
/*============================*/
trx_t* trx, /*!< in: transaction */
dict_index_t* index, /*!< in: FTS index */
ib_time_t* end_time) /*!< out: time in secs */
time_t* end_time) /*!< out: time in secs */
{
return(fts_config_get_index_ulint(
trx, index, FTS_OPTIMIZE_END_TIME, (ulint*) end_time));
......@@ -1693,7 +1695,7 @@ fts_optimize_set_index_end_time(
/*============================*/
trx_t* trx, /*!< in: transaction */
dict_index_t* index, /*!< in: FTS index */
ib_time_t end_time) /*!< in: end time */
time_t end_time) /*!< in: end time */
{
return(fts_config_set_index_ulint(
trx, index, FTS_OPTIMIZE_END_TIME, (ulint) end_time));
......@@ -1755,22 +1757,23 @@ fts_optimize_free(
Get the max time optimize should run in millisecs.
@return max optimize time limit in millisecs. */
static
ib_time_t
ulint
fts_optimize_get_time_limit(
/*========================*/
trx_t* trx, /*!< in: transaction */
fts_table_t* fts_table) /*!< in: aux table */
{
ib_time_t time_limit = 0;
ulint time_limit = 0;
fts_config_get_ulint(
trx, fts_table,
FTS_OPTIMIZE_LIMIT_IN_SECS, (ulint*) &time_limit);
FTS_OPTIMIZE_LIMIT_IN_SECS, &time_limit);
/* FIXME: This is returning milliseconds, while the variable
is being stored and interpreted as seconds! */
return(time_limit * 1000);
}
/**********************************************************************//**
Run OPTIMIZE on the given table. Note: this can take a very long time
(hours). */
......@@ -1783,7 +1786,6 @@ fts_optimize_words(
fts_string_t* word) /*!< in: the starting word to optimize */
{
fts_fetch_t fetch;
ib_time_t start_time;
que_t* graph = NULL;
CHARSET_INFO* charset = optim->fts_index_table.charset;
......@@ -1793,7 +1795,7 @@ fts_optimize_words(
fts_optimize_time_limit = fts_optimize_get_time_limit(
optim->trx, &optim->fts_common_table);
start_time = ut_time();
const time_t start_time = time(NULL);
/* Setup the callback to use for fetching the word ilist etc. */
fetch.read_arg = optim->words;
......@@ -2314,8 +2316,8 @@ fts_optimize_indexes(
dict_index_t* index;
#ifdef FTS_OPTIMIZE_DEBUG
ib_time_t end_time;
ib_time_t start_time;
time_t end_time;
time_t start_time;
/* Get the start and end optimize times for this index. */
error = fts_optimize_get_index_start_time(
......@@ -2443,11 +2445,13 @@ fts_optimize_table_bk(
/*==================*/
fts_slot_t* slot) /*!< in: table to optimiza */
{
dberr_t error;
const time_t now = time(NULL);
const ulint interval = ulint(now - slot->last_run);
/* Avoid optimizing tables that were optimized recently. */
if (slot->last_run > 0
&& (ut_time() - slot->last_run) < slot->interval_time) {
&& lint(interval) >= 0
&& interval < FTS_OPTIMIZE_INTERVAL_IN_SECS) {
return(DB_SUCCESS);
}
......@@ -2455,7 +2459,14 @@ fts_optimize_table_bk(
dict_table_t* table = dict_table_open_on_id(
slot->table_id, FALSE, DICT_TABLE_OP_NORMAL);
if (table && fil_table_accessible(table)
if (!table) {
slot->last_run = now;
return DB_SUCCESS;
}
dberr_t error;
if (fil_table_accessible(table)
&& table->fts && table->fts->cache
&& table->fts->cache->deleted >= FTS_OPTIMIZE_THRESHOLD) {
error = fts_optimize_table(table);
......@@ -2468,7 +2479,7 @@ fts_optimize_table_bk(
}
} else {
/* Note time this run completed. */
slot->last_run = ut_time();
slot->last_run = now;
error = DB_SUCCESS;
}
......@@ -2720,7 +2731,6 @@ static bool fts_optimize_new_table(dict_table_t* table)
slot->table_id = table->id;
slot->running = false;
slot->interval_time = FTS_OPTIMIZE_INTERVAL_IN_SECS;
return(TRUE);
}
......@@ -2757,37 +2767,23 @@ Calculate how many tables in fts_slots need to be optimized.
@return no. of tables to optimize */
static ulint fts_optimize_how_many()
{
ulint i;
ib_time_t delta;
ulint n_tables = 0;
ib_time_t current_time;
current_time = ut_time();
ulint n_tables = 0;
const time_t current_time = time(NULL);
for (i = 0; i < ib_vector_size(fts_slots); ++i) {
for (ulint i = 0; i < ib_vector_size(fts_slots); ++i) {
const fts_slot_t* slot = static_cast<const fts_slot_t*>(
ib_vector_get_const(fts_slots, i));
if (slot->table_id == 0) {
continue;
}
if (!slot->running) {
ut_a(slot->completed <= current_time);
delta = current_time - slot->completed;
const time_t end = slot->running
? slot->last_run : slot->completed;
ulint interval = ulint(current_time - end);
/* Skip slots that have been optimized recently. */
if (delta >= slot->interval_time) {
++n_tables;
}
} else {
ut_a(slot->last_run <= current_time);
delta = current_time - slot->last_run;
if (delta > slot->interval_time) {
++n_tables;
}
if (lint(interval) < 0
|| interval >= FTS_OPTIMIZE_INTERVAL_IN_SECS) {
++n_tables;
}
}
......@@ -2800,13 +2796,14 @@ Check if the total memory used by all FTS table exceeds the maximum limit.
static bool fts_is_sync_needed()
{
ulint total_memory = 0;
double time_diff = difftime(ut_time(), last_check_sync_time);
const time_t now = time(NULL);
double time_diff = difftime(now, last_check_sync_time);
if (fts_need_sync || time_diff < 5) {
if (fts_need_sync || (time_diff >= 0 && time_diff < 5)) {
return(false);
}
last_check_sync_time = ut_time();
last_check_sync_time = now;
for (ulint i = 0; i < ib_vector_size(fts_slots); ++i) {
const fts_slot_t* slot = static_cast<const fts_slot_t*>(
......
......@@ -59,7 +59,7 @@ static bool fts_opt_start_shutdown = false;
static const ulint FTS_WORD_NODES_INIT_SIZE = 64;
/** Last time we did check whether system need a sync */
static ib_time_t last_check_sync_time;
static time_t last_check_sync_time;
/** FTS optimize thread message types. */
enum fts_msg_type_t {
......@@ -181,12 +181,11 @@ struct fts_slot_t {
ulint deleted; /*!< Number of doc ids deleted since the
last time this table was optimized */
ib_time_t last_run; /*!< Time last run completed */
/** time(NULL) of completing fts_optimize_table_bk() */
time_t last_run;
ib_time_t completed; /*!< Optimize finish time */
ib_time_t interval_time; /*!< Minimum time to wait before
optimizing the table again. */
/** time(NULL) of latest successful fts_optimize_table() */
time_t completed;
};
/** A table remove message for the FTS optimize thread. */
......@@ -223,8 +222,8 @@ UNIV_INTERN char fts_enable_diag_print;
/** ZLib compressed block size.*/
static ulint FTS_ZIP_BLOCK_SIZE = 1024;
/** The amount of time optimizing in a single pass, in milliseconds. */
static ib_time_t fts_optimize_time_limit = 0;
/** The amount of time optimizing in a single pass, in seconds. */
static ulint fts_optimize_time_limit;
/** It's defined in fts0fts.cc */
extern const char* fts_common_tables[];
......@@ -1553,7 +1552,7 @@ fts_optimize_compact(
/*=================*/
fts_optimize_t* optim, /*!< in: optimize state data */
dict_index_t* index, /*!< in: current FTS being optimized */
ib_time_t start_time) /*!< in: optimize start time */
time_t start_time) /*!< in: optimize start time */
{
ulint i;
dberr_t error = DB_SUCCESS;
......@@ -1586,8 +1585,11 @@ fts_optimize_compact(
/* Free the word that was optimized. */
fts_word_free(word);
ulint interval = ulint(time(NULL) - start_time);
if (fts_optimize_time_limit > 0
&& (ut_time() - start_time) > fts_optimize_time_limit) {
&& (lint(interval) < 0
|| interval > fts_optimize_time_limit)) {
optim->done = TRUE;
}
......@@ -1678,7 +1680,7 @@ fts_optimize_get_index_end_time(
/*============================*/
trx_t* trx, /*!< in: transaction */
dict_index_t* index, /*!< in: FTS index */
ib_time_t* end_time) /*!< out: time in secs */
time_t* end_time) /*!< out: time in secs */
{
return(fts_config_get_index_ulint(
trx, index, FTS_OPTIMIZE_END_TIME, (ulint*) end_time));
......@@ -1693,7 +1695,7 @@ fts_optimize_set_index_end_time(
/*============================*/
trx_t* trx, /*!< in: transaction */
dict_index_t* index, /*!< in: FTS index */
ib_time_t end_time) /*!< in: end time */
time_t end_time) /*!< in: end time */
{
return(fts_config_set_index_ulint(
trx, index, FTS_OPTIMIZE_END_TIME, (ulint) end_time));
......@@ -1755,22 +1757,23 @@ fts_optimize_free(
Get the max time optimize should run in millisecs.
@return max optimize time limit in millisecs. */
static
ib_time_t
ulint
fts_optimize_get_time_limit(
/*========================*/
trx_t* trx, /*!< in: transaction */
fts_table_t* fts_table) /*!< in: aux table */
{
ib_time_t time_limit = 0;
ulint time_limit = 0;
fts_config_get_ulint(
trx, fts_table,
FTS_OPTIMIZE_LIMIT_IN_SECS, (ulint*) &time_limit);
FTS_OPTIMIZE_LIMIT_IN_SECS, &time_limit);
/* FIXME: This is returning milliseconds, while the variable
is being stored and interpreted as seconds! */
return(time_limit * 1000);
}
/**********************************************************************//**
Run OPTIMIZE on the given table. Note: this can take a very long time
(hours). */
......@@ -1783,7 +1786,6 @@ fts_optimize_words(
fts_string_t* word) /*!< in: the starting word to optimize */
{
fts_fetch_t fetch;
ib_time_t start_time;
que_t* graph = NULL;
CHARSET_INFO* charset = optim->fts_index_table.charset;
......@@ -1793,7 +1795,7 @@ fts_optimize_words(
fts_optimize_time_limit = fts_optimize_get_time_limit(
optim->trx, &optim->fts_common_table);
start_time = ut_time();
const time_t start_time = time(NULL);
/* Setup the callback to use for fetching the word ilist etc. */
fetch.read_arg = optim->words;
......@@ -2314,8 +2316,8 @@ fts_optimize_indexes(
dict_index_t* index;
#ifdef FTS_OPTIMIZE_DEBUG
ib_time_t end_time;
ib_time_t start_time;
time_t end_time;
time_t start_time;
/* Get the start and end optimize times for this index. */
error = fts_optimize_get_index_start_time(
......@@ -2443,11 +2445,13 @@ fts_optimize_table_bk(
/*==================*/
fts_slot_t* slot) /*!< in: table to optimiza */
{
dberr_t error;
const time_t now = time(NULL);
const ulint interval = ulint(now - slot->last_run);
/* Avoid optimizing tables that were optimized recently. */
if (slot->last_run > 0
&& (ut_time() - slot->last_run) < slot->interval_time) {
&& lint(interval) >= 0
&& interval < FTS_OPTIMIZE_INTERVAL_IN_SECS) {
return(DB_SUCCESS);
}
......@@ -2455,7 +2459,14 @@ fts_optimize_table_bk(
dict_table_t* table = dict_table_open_on_id(
slot->table_id, FALSE, DICT_TABLE_OP_NORMAL);
if (table && fil_table_accessible(table)
if (!table) {
slot->last_run = now;
return DB_SUCCESS;
}
dberr_t error;
if (fil_table_accessible(table)
&& table->fts && table->fts->cache
&& table->fts->cache->deleted >= FTS_OPTIMIZE_THRESHOLD) {
error = fts_optimize_table(table);
......@@ -2468,7 +2479,7 @@ fts_optimize_table_bk(
}
} else {
/* Note time this run completed. */
slot->last_run = ut_time();
slot->last_run = now;
error = DB_SUCCESS;
}
......@@ -2720,7 +2731,6 @@ static bool fts_optimize_new_table(dict_table_t* table)
slot->table_id = table->id;
slot->running = false;
slot->interval_time = FTS_OPTIMIZE_INTERVAL_IN_SECS;
return(TRUE);
}
......@@ -2757,37 +2767,23 @@ Calculate how many tables in fts_slots need to be optimized.
@return no. of tables to optimize */
static ulint fts_optimize_how_many()
{
ulint i;
ib_time_t delta;
ulint n_tables = 0;
ib_time_t current_time;
current_time = ut_time();
ulint n_tables = 0;
const time_t current_time = time(NULL);
for (i = 0; i < ib_vector_size(fts_slots); ++i) {
for (ulint i = 0; i < ib_vector_size(fts_slots); ++i) {
const fts_slot_t* slot = static_cast<const fts_slot_t*>(
ib_vector_get_const(fts_slots, i));
if (slot->table_id == 0) {
continue;
}
if (!slot->running) {
ut_a(slot->completed <= current_time);
delta = current_time - slot->completed;
const time_t end = slot->running
? slot->last_run : slot->completed;
ulint interval = ulint(current_time - end);
/* Skip slots that have been optimized recently. */
if (delta >= slot->interval_time) {
++n_tables;
}
} else {
ut_a(slot->last_run <= current_time);
delta = current_time - slot->last_run;
if (delta > slot->interval_time) {
++n_tables;
}
if (lint(interval) < 0
|| interval >= FTS_OPTIMIZE_INTERVAL_IN_SECS) {
++n_tables;
}
}
......@@ -2800,13 +2796,14 @@ Check if the total memory used by all FTS table exceeds the maximum limit.
static bool fts_is_sync_needed()
{
ulint total_memory = 0;
double time_diff = difftime(ut_time(), last_check_sync_time);
const time_t now = time(NULL);
double time_diff = difftime(now, last_check_sync_time);
if (fts_need_sync || time_diff < 5) {
if (fts_need_sync || (time_diff >= 0 && time_diff < 5)) {
return(false);
}
last_check_sync_time = ut_time();
last_check_sync_time = now;
for (ulint i = 0; i < ib_vector_size(fts_slots); ++i) {
const fts_slot_t* slot = static_cast<const fts_slot_t*>(
......
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