2018-08-20 15:34:37 +00:00
|
|
|
#include "BackgroundSchedulePool.h"
|
2017-12-29 22:32:04 +00:00
|
|
|
#include <Common/MemoryTracker.h>
|
|
|
|
#include <Common/Exception.h>
|
|
|
|
#include <Common/setThreadName.h>
|
|
|
|
#include <Common/Stopwatch.h>
|
2018-05-29 18:14:31 +00:00
|
|
|
#include <Common/CurrentThread.h>
|
2017-12-29 22:32:04 +00:00
|
|
|
#include <common/logger_useful.h>
|
|
|
|
#include <chrono>
|
2018-06-19 20:30:35 +00:00
|
|
|
#include <ext/scope_guard.h>
|
2018-05-28 19:53:03 +00:00
|
|
|
|
2017-12-29 22:32:04 +00:00
|
|
|
|
|
|
|
namespace DB
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
class TaskNotification final : public Poco::Notification
|
|
|
|
{
|
|
|
|
public:
|
2019-08-03 11:02:40 +00:00
|
|
|
explicit TaskNotification(const BackgroundSchedulePoolTaskInfoPtr & task_) : task(task_) {}
|
2017-12-29 22:32:04 +00:00
|
|
|
void execute() { task->execute(); }
|
|
|
|
|
|
|
|
private:
|
2019-04-06 01:09:15 +00:00
|
|
|
BackgroundSchedulePoolTaskInfoPtr task;
|
2017-12-29 22:32:04 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2019-04-06 01:09:15 +00:00
|
|
|
BackgroundSchedulePoolTaskInfo::BackgroundSchedulePoolTaskInfo(
|
|
|
|
BackgroundSchedulePool & pool_, const std::string & log_name_, const BackgroundSchedulePool::TaskFunc & function_)
|
|
|
|
: pool(pool_), log_name(log_name_), function(function_)
|
2017-12-29 22:32:04 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2019-04-06 01:09:15 +00:00
|
|
|
bool BackgroundSchedulePoolTaskInfo::schedule()
|
2017-12-29 22:32:04 +00:00
|
|
|
{
|
2018-01-01 20:35:29 +00:00
|
|
|
std::lock_guard lock(schedule_mutex);
|
2017-12-29 22:32:04 +00:00
|
|
|
|
|
|
|
if (deactivated || scheduled)
|
|
|
|
return false;
|
|
|
|
|
2018-08-22 13:43:27 +00:00
|
|
|
scheduleImpl(lock);
|
2017-12-29 22:32:04 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-04-24 20:00:00 +00:00
|
|
|
bool BackgroundSchedulePoolTaskInfo::scheduleAfter(size_t ms, bool overwrite)
|
2017-12-29 22:32:04 +00:00
|
|
|
{
|
2018-01-01 20:35:29 +00:00
|
|
|
std::lock_guard lock(schedule_mutex);
|
|
|
|
|
|
|
|
if (deactivated || scheduled)
|
|
|
|
return false;
|
2020-04-24 20:00:00 +00:00
|
|
|
if (delayed && !overwrite)
|
|
|
|
return false;
|
2018-01-01 20:35:29 +00:00
|
|
|
|
|
|
|
pool.scheduleDelayedTask(shared_from_this(), ms, lock);
|
2017-12-29 22:32:04 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-04-06 01:09:15 +00:00
|
|
|
void BackgroundSchedulePoolTaskInfo::deactivate()
|
2017-12-29 22:32:04 +00:00
|
|
|
{
|
2018-01-01 20:35:29 +00:00
|
|
|
std::lock_guard lock_exec(exec_mutex);
|
|
|
|
std::lock_guard lock_schedule(schedule_mutex);
|
|
|
|
|
2017-12-29 22:32:04 +00:00
|
|
|
if (deactivated)
|
|
|
|
return;
|
|
|
|
|
|
|
|
deactivated = true;
|
|
|
|
scheduled = false;
|
|
|
|
|
|
|
|
if (delayed)
|
2018-01-01 20:35:29 +00:00
|
|
|
pool.cancelDelayedTask(shared_from_this(), lock_schedule);
|
2017-12-29 22:32:04 +00:00
|
|
|
}
|
|
|
|
|
2019-04-06 01:09:15 +00:00
|
|
|
void BackgroundSchedulePoolTaskInfo::activate()
|
2017-12-29 22:32:04 +00:00
|
|
|
{
|
2018-01-01 20:35:29 +00:00
|
|
|
std::lock_guard lock(schedule_mutex);
|
2017-12-29 22:32:04 +00:00
|
|
|
deactivated = false;
|
|
|
|
}
|
|
|
|
|
2019-04-06 01:09:15 +00:00
|
|
|
bool BackgroundSchedulePoolTaskInfo::activateAndSchedule()
|
2018-08-22 13:43:27 +00:00
|
|
|
{
|
|
|
|
std::lock_guard lock(schedule_mutex);
|
|
|
|
|
|
|
|
deactivated = false;
|
|
|
|
if (scheduled)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
scheduleImpl(lock);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-04-06 01:09:15 +00:00
|
|
|
void BackgroundSchedulePoolTaskInfo::execute()
|
2017-12-29 22:32:04 +00:00
|
|
|
{
|
2018-05-31 13:05:05 +00:00
|
|
|
Stopwatch watch;
|
2020-04-22 23:00:24 +00:00
|
|
|
CurrentMetrics::Increment metric_increment{pool.tasks_metric};
|
2018-05-31 13:05:05 +00:00
|
|
|
|
2018-01-01 20:35:29 +00:00
|
|
|
std::lock_guard lock_exec(exec_mutex);
|
2017-12-29 22:32:04 +00:00
|
|
|
|
2018-01-01 20:35:29 +00:00
|
|
|
{
|
|
|
|
std::lock_guard lock_schedule(schedule_mutex);
|
|
|
|
|
|
|
|
if (deactivated)
|
|
|
|
return;
|
|
|
|
|
|
|
|
scheduled = false;
|
2018-03-22 20:26:03 +00:00
|
|
|
executing = true;
|
2018-01-01 20:35:29 +00:00
|
|
|
}
|
2017-12-29 22:32:04 +00:00
|
|
|
|
|
|
|
function();
|
|
|
|
UInt64 milliseconds = watch.elapsedMilliseconds();
|
|
|
|
|
|
|
|
/// If the task is executed longer than specified time, it will be logged.
|
2018-05-31 13:05:05 +00:00
|
|
|
static const int32_t slow_execution_threshold_ms = 200;
|
2017-12-29 22:32:04 +00:00
|
|
|
|
|
|
|
if (milliseconds >= slow_execution_threshold_ms)
|
2020-05-23 22:24:01 +00:00
|
|
|
LOG_TRACE(&Logger::get(log_name), "Execution took {} ms.", milliseconds);
|
2018-03-22 20:26:03 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
std::lock_guard lock_schedule(schedule_mutex);
|
|
|
|
|
|
|
|
executing = false;
|
|
|
|
|
|
|
|
/// In case was scheduled while executing (including a scheduleAfter which expired) we schedule the task
|
2018-07-11 16:10:02 +00:00
|
|
|
/// on the queue. We don't call the function again here because this way all tasks
|
|
|
|
/// will have their chance to execute
|
2018-03-22 20:26:03 +00:00
|
|
|
|
2018-06-01 13:35:16 +00:00
|
|
|
if (scheduled)
|
2018-05-31 13:05:05 +00:00
|
|
|
pool.queue.enqueueNotification(new TaskNotification(shared_from_this()));
|
2018-03-22 20:26:03 +00:00
|
|
|
}
|
2017-12-29 22:32:04 +00:00
|
|
|
}
|
|
|
|
|
2019-04-06 01:09:15 +00:00
|
|
|
void BackgroundSchedulePoolTaskInfo::scheduleImpl(std::lock_guard<std::mutex> & schedule_mutex_lock)
|
2018-08-22 13:43:27 +00:00
|
|
|
{
|
|
|
|
scheduled = true;
|
|
|
|
|
|
|
|
if (delayed)
|
|
|
|
pool.cancelDelayedTask(shared_from_this(), schedule_mutex_lock);
|
|
|
|
|
|
|
|
/// If the task is not executing at the moment, enqueue it for immediate execution.
|
|
|
|
/// But if it is currently executing, do nothing because it will be enqueued
|
|
|
|
/// at the end of the execute() method.
|
|
|
|
if (!executing)
|
|
|
|
pool.queue.enqueueNotification(new TaskNotification(shared_from_this()));
|
|
|
|
}
|
|
|
|
|
2019-04-06 01:09:15 +00:00
|
|
|
Coordination::WatchCallback BackgroundSchedulePoolTaskInfo::getWatchCallback()
|
2018-03-22 12:34:42 +00:00
|
|
|
{
|
2018-08-25 01:58:14 +00:00
|
|
|
return [t = shared_from_this()](const Coordination::WatchResponse &)
|
2018-07-24 18:46:23 +00:00
|
|
|
{
|
2018-04-10 13:20:14 +00:00
|
|
|
t->schedule();
|
|
|
|
};
|
2018-03-22 12:34:42 +00:00
|
|
|
}
|
|
|
|
|
2017-12-29 22:32:04 +00:00
|
|
|
|
2020-04-23 00:14:58 +00:00
|
|
|
BackgroundSchedulePool::BackgroundSchedulePool(size_t size_, CurrentMetrics::Metric tasks_metric_, CurrentMetrics::Metric memory_metric_, const char *thread_name_)
|
2019-08-03 11:02:40 +00:00
|
|
|
: size(size_)
|
2020-04-22 23:00:24 +00:00
|
|
|
, tasks_metric(tasks_metric_)
|
|
|
|
, memory_metric(memory_metric_)
|
2020-04-23 00:14:58 +00:00
|
|
|
, thread_name(thread_name_)
|
2017-12-29 22:32:04 +00:00
|
|
|
{
|
2020-05-23 22:24:01 +00:00
|
|
|
LOG_INFO(&Logger::get("BackgroundSchedulePool/" + thread_name), "Create BackgroundSchedulePool with {} threads", size);
|
2017-12-29 22:32:04 +00:00
|
|
|
|
|
|
|
threads.resize(size);
|
|
|
|
for (auto & thread : threads)
|
2019-01-14 19:22:09 +00:00
|
|
|
thread = ThreadFromGlobalPool([this] { threadFunction(); });
|
2017-12-29 22:32:04 +00:00
|
|
|
|
2019-01-14 19:22:09 +00:00
|
|
|
delayed_thread = ThreadFromGlobalPool([this] { delayExecutionThreadFunction(); });
|
2017-12-29 22:32:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
BackgroundSchedulePool::~BackgroundSchedulePool()
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2018-03-22 10:31:05 +00:00
|
|
|
{
|
2018-05-31 13:05:05 +00:00
|
|
|
std::unique_lock lock(delayed_tasks_mutex);
|
2018-03-22 10:31:05 +00:00
|
|
|
shutdown = true;
|
|
|
|
wakeup_cond.notify_all();
|
|
|
|
}
|
2017-12-29 22:32:04 +00:00
|
|
|
|
2018-03-22 10:31:05 +00:00
|
|
|
queue.wakeUpAll();
|
2017-12-29 22:32:04 +00:00
|
|
|
delayed_thread.join();
|
|
|
|
|
2020-05-23 22:24:01 +00:00
|
|
|
LOG_TRACE(&Logger::get("BackgroundSchedulePool/" + thread_name), "Waiting for threads to finish.");
|
2019-01-14 19:22:09 +00:00
|
|
|
for (auto & thread : threads)
|
2017-12-29 22:32:04 +00:00
|
|
|
thread.join();
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
tryLogCurrentException(__PRETTY_FUNCTION__);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-05-31 13:05:05 +00:00
|
|
|
BackgroundSchedulePool::TaskHolder BackgroundSchedulePool::createTask(const std::string & name, const TaskFunc & function)
|
2017-12-29 22:32:04 +00:00
|
|
|
{
|
2018-05-31 13:05:05 +00:00
|
|
|
return TaskHolder(std::make_shared<TaskInfo>(*this, name, function));
|
2017-12-29 22:32:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-05-31 13:05:05 +00:00
|
|
|
void BackgroundSchedulePool::scheduleDelayedTask(const TaskInfoPtr & task, size_t ms, std::lock_guard<std::mutex> & /* task_schedule_mutex_lock */)
|
2017-12-29 22:32:04 +00:00
|
|
|
{
|
|
|
|
Poco::Timestamp current_time;
|
|
|
|
|
|
|
|
{
|
2018-05-31 13:05:05 +00:00
|
|
|
std::lock_guard lock(delayed_tasks_mutex);
|
2017-12-29 22:32:04 +00:00
|
|
|
|
|
|
|
if (task->delayed)
|
|
|
|
delayed_tasks.erase(task->iterator);
|
|
|
|
|
|
|
|
task->iterator = delayed_tasks.emplace(current_time + (ms * 1000), task);
|
|
|
|
task->delayed = true;
|
|
|
|
}
|
|
|
|
|
2018-03-22 10:31:05 +00:00
|
|
|
wakeup_cond.notify_all();
|
2017-12-29 22:32:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-05-31 13:05:05 +00:00
|
|
|
void BackgroundSchedulePool::cancelDelayedTask(const TaskInfoPtr & task, std::lock_guard<std::mutex> & /* task_schedule_mutex_lock */)
|
2017-12-29 22:32:04 +00:00
|
|
|
{
|
|
|
|
{
|
2018-05-31 13:05:05 +00:00
|
|
|
std::lock_guard lock(delayed_tasks_mutex);
|
2017-12-29 22:32:04 +00:00
|
|
|
delayed_tasks.erase(task->iterator);
|
|
|
|
task->delayed = false;
|
|
|
|
}
|
|
|
|
|
2018-03-22 10:31:05 +00:00
|
|
|
wakeup_cond.notify_all();
|
2017-12-29 22:32:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-22 04:04:02 +00:00
|
|
|
void BackgroundSchedulePool::attachToThreadGroup()
|
2017-12-29 22:32:04 +00:00
|
|
|
{
|
2018-08-22 04:04:02 +00:00
|
|
|
std::lock_guard lock(delayed_tasks_mutex);
|
2017-12-29 22:32:04 +00:00
|
|
|
|
2018-08-22 04:04:02 +00:00
|
|
|
if (thread_group)
|
2018-08-22 03:58:19 +00:00
|
|
|
{
|
2018-08-22 04:04:02 +00:00
|
|
|
/// Put all threads to one thread pool
|
|
|
|
CurrentThread::attachTo(thread_group);
|
2018-08-22 03:58:19 +00:00
|
|
|
}
|
2018-08-22 04:04:02 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
CurrentThread::initializeQuery();
|
|
|
|
thread_group = CurrentThread::getGroup();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void BackgroundSchedulePool::threadFunction()
|
|
|
|
{
|
2020-04-23 00:14:58 +00:00
|
|
|
setThreadName(thread_name.c_str());
|
2018-08-22 03:58:19 +00:00
|
|
|
|
2018-08-22 04:04:02 +00:00
|
|
|
attachToThreadGroup();
|
2018-06-19 20:30:35 +00:00
|
|
|
SCOPE_EXIT({ CurrentThread::detachQueryIfNotDetached(); });
|
2020-04-22 05:39:31 +00:00
|
|
|
if (auto * memory_tracker = CurrentThread::getMemoryTracker())
|
2020-04-22 23:00:24 +00:00
|
|
|
memory_tracker->setMetric(memory_metric);
|
2017-12-29 22:32:04 +00:00
|
|
|
|
|
|
|
while (!shutdown)
|
|
|
|
{
|
|
|
|
if (Poco::AutoPtr<Poco::Notification> notification = queue.waitDequeueNotification())
|
|
|
|
{
|
|
|
|
TaskNotification & task_notification = static_cast<TaskNotification &>(*notification);
|
|
|
|
task_notification.execute();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void BackgroundSchedulePool::delayExecutionThreadFunction()
|
|
|
|
{
|
2020-04-23 00:14:58 +00:00
|
|
|
setThreadName((thread_name + "/D").c_str());
|
2017-12-29 22:32:04 +00:00
|
|
|
|
2018-08-22 04:04:02 +00:00
|
|
|
attachToThreadGroup();
|
2018-06-19 20:30:35 +00:00
|
|
|
SCOPE_EXIT({ CurrentThread::detachQueryIfNotDetached(); });
|
|
|
|
|
2017-12-29 22:32:04 +00:00
|
|
|
while (!shutdown)
|
|
|
|
{
|
2018-05-31 13:05:05 +00:00
|
|
|
TaskInfoPtr task;
|
2018-03-27 13:29:41 +00:00
|
|
|
bool found = false;
|
2017-12-29 22:32:04 +00:00
|
|
|
|
|
|
|
{
|
2018-05-31 13:05:05 +00:00
|
|
|
std::unique_lock lock(delayed_tasks_mutex);
|
2018-03-22 10:31:05 +00:00
|
|
|
|
2018-11-23 18:52:00 +00:00
|
|
|
while (!shutdown)
|
2018-01-01 20:35:29 +00:00
|
|
|
{
|
2018-03-27 13:29:41 +00:00
|
|
|
Poco::Timestamp min_time;
|
|
|
|
|
|
|
|
if (!delayed_tasks.empty())
|
|
|
|
{
|
|
|
|
auto t = delayed_tasks.begin();
|
|
|
|
min_time = t->first;
|
|
|
|
task = t->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!task)
|
|
|
|
{
|
|
|
|
wakeup_cond.wait(lock);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
Poco::Timestamp current_time;
|
|
|
|
|
|
|
|
if (min_time > current_time)
|
|
|
|
{
|
|
|
|
wakeup_cond.wait_for(lock, std::chrono::microseconds(min_time - current_time));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/// We have a task ready for execution
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
2018-01-01 20:35:29 +00:00
|
|
|
}
|
2017-12-29 22:32:04 +00:00
|
|
|
}
|
2018-01-01 20:35:29 +00:00
|
|
|
|
2018-06-01 13:35:16 +00:00
|
|
|
if (found)
|
2018-03-27 13:29:41 +00:00
|
|
|
task->schedule();
|
2017-12-29 22:32:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|