Skip to content

Commit ed36286

Browse files
committed
Add timepoint to periodic task settings.
1 parent af89e2c commit ed36286

File tree

3 files changed

+77
-3
lines changed

3 files changed

+77
-3
lines changed

core/include/userver/utils/periodic_task.hpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,15 @@ class PeriodicTask final {
6262

6363
/// Configuration parameters for PeriodicTask.
6464
struct Settings final {
65+
struct TimePoint {
66+
std::uint8_t hour;
67+
std::uint8_t minute;
68+
69+
bool operator==(const TimePoint& other) const {
70+
return std::tie(hour, minute) == std::tie(other.hour, other.minute);
71+
}
72+
};
73+
6574
static constexpr uint8_t kDistributionPercent = 25;
6675

6776
constexpr /*implicit*/ Settings(
@@ -91,6 +100,12 @@ class PeriodicTask final {
91100
UASSERT(distribution_percent <= 100);
92101
}
93102

103+
constexpr Settings(
104+
TimePoint time_point,
105+
logging::Level span_level = logging::Level::kInfo
106+
)
107+
: strong_mode_anchor(time_point), span_level(span_level) {}
108+
94109
template <class Rep, class Period>
95110
constexpr /*implicit*/ Settings(std::chrono::duration<Rep, Period> period)
96111
: Settings(period, kDistributionPercent, {}, logging::Level::kInfo) {}
@@ -110,6 +125,10 @@ class PeriodicTask final {
110125
/// `(period +/- distribution) - time of previous execution`
111126
std::chrono::milliseconds distribution{};
112127

128+
/// @brief Time point for the task execution. Task is repeated every
129+
/// hour:minute UTC of the current/next day
130+
std::optional<TimePoint> strong_mode_anchor;
131+
113132
/// @brief Used instead of `period` in case of exception, if set.
114133
std::optional<std::chrono::milliseconds> exception_period;
115134

core/src/utils/periodic_task.cpp

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ namespace {
2222

2323
auto TieSettings(const PeriodicTask::Settings& settings) {
2424
// Can't use Boost.Pfr, because Settings has custom constructors.
25-
const auto& [f1, f2, f3, f4, f5, f6] = settings;
26-
return std::tie(f1, f2, f3, f4, f5, f6);
25+
const auto& [f1, f2, f3, f4, f5, f6, f7] = settings;
26+
return std::tie(f1, f2, f3, f4, f5, f6, f7);
2727
}
2828

2929
} // namespace
@@ -156,7 +156,21 @@ void PeriodicTask::Run() {
156156
if (!no_exception) period = exception_period;
157157

158158
std::chrono::steady_clock::time_point start;
159-
if (settings->flags & Flags::kStrong) {
159+
160+
if (settings->strong_mode_anchor) {
161+
const auto now = utils::datetime::WallCoarseClock::now();
162+
const auto today = std::chrono::time_point_cast<std::chrono::duration<long long, std::ratio<86400>>>(now);
163+
164+
const auto [hours, minutes] = settings->strong_mode_anchor.value();
165+
auto anchor_time_point = today + std::chrono::hours(hours) + std::chrono::minutes(minutes);
166+
if (now > anchor_time_point) {
167+
anchor_time_point += std::chrono::hours(24);
168+
}
169+
170+
start = std::chrono::steady_clock::now()
171+
+ std::chrono::duration_cast<std::chrono::steady_clock::duration>(anchor_time_point - now);
172+
}
173+
else if (settings->flags & Flags::kStrong) {
160174
start = before;
161175
} else {
162176
start = std::chrono::steady_clock::now();

core/src/utils/periodic_task_test.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,47 @@ UTEST(PeriodicTask, Strong) {
157157
task.Stop();
158158
}
159159

160+
UTEST(PeriodicTask, StrongModeAnchor) {
161+
SimpleTaskData simple;
162+
163+
simple.sleep = 6s;
164+
165+
const std::chrono::system_clock::time_point tp = std::chrono::system_clock::now();
166+
167+
auto tp_days = std::chrono::time_point_cast<std::chrono::duration<int, std::ratio<86400>>>(tp);
168+
169+
auto tod = tp - tp_days;
170+
171+
auto h = std::chrono::duration_cast<std::chrono::hours>(tod);
172+
tod -= h;
173+
auto m = std::chrono::duration_cast<std::chrono::minutes>(tod);
174+
175+
auto hour = static_cast<uint8_t>(h.count());
176+
auto minute = static_cast<uint8_t>(m.count());
177+
178+
minute++;
179+
if (minute >= 60) {
180+
minute = 0;
181+
hour++;
182+
if (hour >= 24) {
183+
hour = 0;
184+
}
185+
}
186+
187+
utils::PeriodicTask::Settings::TimePoint mode_anchor{
188+
.hour = hour,
189+
.minute = minute
190+
};
191+
utils::PeriodicTask task(
192+
"task",
193+
utils::PeriodicTask::Settings(mode_anchor),
194+
simple.GetTaskFunction()
195+
);
196+
EXPECT_TRUE(simple.WaitFor(simple.sleep * kSlowRatio, [&] { return simple.GetCount() == 1; }));
197+
198+
task.Stop();
199+
}
200+
160201
UTEST(PeriodicTask, Now) {
161202
SimpleTaskData simple;
162203

0 commit comments

Comments
 (0)