mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 03:52:35 +01:00
9ab0d7b4e9
Copy of commit dcd9379eb5707bc7514a2ff4d9127790356505cb Author: Manuel Ung <mung@fb.com> Date: Fri Jun 14 10:38:17 2019 -0700 Skip valgrind for rocksdb.force_shutdown Summary: This test does unclean shutdown, and leaks memory. Squash with: D15749084 Reviewed By: hermanlee Differential Revision: D15828957 fbshipit-source-id: 30541455d74
240 lines
6.8 KiB
C++
240 lines
6.8 KiB
C++
/*
|
|
Copyright (c) 2017, Facebook, Inc.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; version 2 of the License.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */
|
|
|
|
/* This C++ file's header */
|
|
#include "./rdb_io_watchdog.h"
|
|
|
|
/* C++ standard header files */
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
/* Rdb_io_watchdog doesn't work on Windows [yet] */
|
|
#ifdef HAVE_TIMER_DELETE
|
|
|
|
namespace myrocks {
|
|
|
|
void Rdb_io_watchdog::expire_io_callback(union sigval timer_data) {
|
|
DBUG_ASSERT(timer_data.sival_ptr != nullptr);
|
|
|
|
// The treatment of any pending signal generated by the deleted timer is
|
|
// unspecified. Therefore we still need to handle the rare case where we
|
|
// finished the I/O operation right before the timer was deleted and callback
|
|
// was in flight.
|
|
if (!m_io_in_progress.load()) {
|
|
return;
|
|
}
|
|
|
|
// At this point we know that I/O has been stuck in `write()` for more than
|
|
// `m_write_timeout` seconds. We'll log a message and shut down the service.
|
|
// NO_LINT_DEBUG
|
|
sql_print_error(
|
|
"MyRocks has detected a combination of I/O requests which "
|
|
"have cumulatively been blocking for more than %u seconds. "
|
|
"Shutting the service down.",
|
|
m_write_timeout);
|
|
|
|
abort();
|
|
}
|
|
|
|
void Rdb_io_watchdog::io_check_callback(union sigval timer_data) {
|
|
RDB_MUTEX_LOCK_CHECK(m_reset_mutex);
|
|
|
|
DBUG_ASSERT(timer_data.sival_ptr != nullptr);
|
|
|
|
struct sigevent e;
|
|
|
|
e.sigev_notify = SIGEV_THREAD;
|
|
e.sigev_notify_function = &Rdb_io_watchdog::expire_io_callback_wrapper;
|
|
e.sigev_value.sival_ptr = this;
|
|
e.sigev_notify_attributes = nullptr;
|
|
|
|
int ret = timer_create(CLOCK_MONOTONIC, &e, &m_io_check_watchdog_timer);
|
|
|
|
if (unlikely(ret)) {
|
|
// NO_LINT_DEBUG
|
|
sql_print_warning("Creating a watchdog I/O timer failed with %d.", errno);
|
|
RDB_MUTEX_UNLOCK_CHECK(m_reset_mutex);
|
|
return;
|
|
}
|
|
|
|
struct itimerspec timer_spec;
|
|
memset(&timer_spec, 0, sizeof(timer_spec));
|
|
|
|
// One time execution only for the watchdog. No interval.
|
|
timer_spec.it_value.tv_sec = m_write_timeout;
|
|
|
|
ret = timer_settime(m_io_check_watchdog_timer, 0, &timer_spec, nullptr);
|
|
|
|
if (unlikely(ret)) {
|
|
// NO_LINT_DEBUG
|
|
sql_print_warning("Setting time for a watchdog I/O timer failed with %d.",
|
|
errno);
|
|
RDB_MUTEX_UNLOCK_CHECK(m_reset_mutex);
|
|
return;
|
|
}
|
|
|
|
m_io_in_progress.store(true);
|
|
|
|
// Verify the write access to all directories we care about.
|
|
for (const std::string &directory : m_dirs_to_check) {
|
|
ret = check_write_access(directory);
|
|
|
|
// We'll log a warning and attept to continue to see if the problem happens
|
|
// in other cases as well.
|
|
if (unlikely(ret != HA_EXIT_SUCCESS)) {
|
|
// NO_LINT_DEBUG
|
|
sql_print_warning("Unable to verify write access to %s (error code %d).",
|
|
directory.c_str(), ret);
|
|
}
|
|
}
|
|
|
|
m_io_in_progress.store(false);
|
|
|
|
// Clean up the watchdog timer.
|
|
ret = timer_delete(m_io_check_watchdog_timer);
|
|
|
|
if (unlikely(ret)) {
|
|
// NO_LINT_DEBUG
|
|
sql_print_warning("Deleting the watchdog I/O timer failed with %d.", errno);
|
|
}
|
|
|
|
m_io_check_watchdog_timer = nullptr;
|
|
|
|
RDB_MUTEX_UNLOCK_CHECK(m_reset_mutex);
|
|
}
|
|
|
|
int Rdb_io_watchdog::check_write_access(const std::string &dirname) const {
|
|
DBUG_ASSERT(!dirname.empty());
|
|
DBUG_ASSERT(m_buf != nullptr);
|
|
|
|
const std::string fname = dirname + FN_DIRSEP + RDB_IO_DUMMY_FILE_NAME;
|
|
|
|
// O_DIRECT is a key flag here to make sure that we'll bypass the kernel's
|
|
// buffer cache.
|
|
int fd = open(fname.c_str(), O_WRONLY | O_DIRECT | O_CREAT | O_SYNC,
|
|
S_IRWXU | S_IWUSR);
|
|
|
|
if (unlikely(fd == -1)) {
|
|
return fd;
|
|
}
|
|
|
|
int ret = write(fd, m_buf, RDB_IO_WRITE_BUFFER_SIZE);
|
|
|
|
if (unlikely(ret != RDB_IO_WRITE_BUFFER_SIZE)) {
|
|
return ret;
|
|
}
|
|
|
|
ret = close(fd);
|
|
|
|
if (unlikely(ret)) {
|
|
return ret;
|
|
}
|
|
|
|
ret = unlink(fname.c_str());
|
|
|
|
if (unlikely(ret)) {
|
|
return ret;
|
|
}
|
|
|
|
return HA_EXIT_SUCCESS;
|
|
}
|
|
|
|
int Rdb_io_watchdog::reset_timeout(const uint32_t write_timeout) {
|
|
// This function will be called either from a thread initializing MyRocks
|
|
// engine or handling system variable changes. We need to account for the
|
|
// possibility of I/O callback executing at the same time. If that happens
|
|
// then we'll wait for it to finish.
|
|
RDB_MUTEX_LOCK_CHECK(m_reset_mutex);
|
|
|
|
struct sigevent e;
|
|
|
|
// In all the cases all the active timers needs to be stopped.
|
|
int ret = stop_timers();
|
|
|
|
if (unlikely(ret)) {
|
|
// NO_LINT_DEBUG
|
|
sql_print_warning("Stopping I/O timers failed with %d.", errno);
|
|
RDB_MUTEX_UNLOCK_CHECK(m_reset_mutex);
|
|
return ret;
|
|
}
|
|
|
|
m_write_timeout = write_timeout;
|
|
m_io_in_progress.store(false);
|
|
|
|
// Zero means that the I/O timer will be disabled. Therefore there's nothing
|
|
// for us to do here.
|
|
if (!write_timeout) {
|
|
RDB_MUTEX_UNLOCK_CHECK(m_reset_mutex);
|
|
return HA_EXIT_SUCCESS;
|
|
}
|
|
|
|
free(m_buf);
|
|
|
|
ret = posix_memalign(reinterpret_cast<void **>(&m_buf),
|
|
RDB_IO_WRITE_BUFFER_SIZE, RDB_IO_WRITE_BUFFER_SIZE);
|
|
|
|
if (unlikely(ret)) {
|
|
m_buf = nullptr;
|
|
RDB_MUTEX_UNLOCK_CHECK(m_reset_mutex);
|
|
// NB! The value of errno is not set.
|
|
return ret;
|
|
}
|
|
|
|
DBUG_ASSERT(m_buf != nullptr);
|
|
memset(m_buf, 0, RDB_IO_WRITE_BUFFER_SIZE);
|
|
|
|
// Common case gets handled here - we'll create a timer with a specific
|
|
// interval to check a set of directories for write access.
|
|
DBUG_ASSERT(m_dirs_to_check.size() > 0);
|
|
|
|
e.sigev_notify = SIGEV_THREAD;
|
|
e.sigev_notify_function = &Rdb_io_watchdog::io_check_callback_wrapper;
|
|
e.sigev_value.sival_ptr = this;
|
|
e.sigev_notify_attributes = nullptr;
|
|
|
|
ret = timer_create(CLOCK_MONOTONIC, &e, &m_io_check_timer);
|
|
|
|
if (unlikely(ret)) {
|
|
// NO_LINT_DEBUG
|
|
sql_print_warning("Creating a I/O timer failed with %d.", errno);
|
|
RDB_MUTEX_UNLOCK_CHECK(m_reset_mutex);
|
|
return ret;
|
|
}
|
|
|
|
struct itimerspec timer_spec;
|
|
memset(&timer_spec, 0, sizeof(timer_spec));
|
|
|
|
// I/O timer will need to execute on a certain interval.
|
|
timer_spec.it_value.tv_sec = m_write_timeout;
|
|
timer_spec.it_interval.tv_sec = m_write_timeout;
|
|
|
|
ret = timer_settime(m_io_check_timer, 0, &timer_spec, nullptr);
|
|
|
|
if (unlikely(ret)) {
|
|
// NO_LINT_DEBUG
|
|
sql_print_warning("Setting time for a watchdog I/O timer failed with %d.",
|
|
errno);
|
|
}
|
|
|
|
RDB_MUTEX_UNLOCK_CHECK(m_reset_mutex);
|
|
|
|
return HA_EXIT_SUCCESS;
|
|
}
|
|
|
|
} // namespace myrocks
|
|
|
|
#endif
|
|
|