debug_log_all_jobs();
}
- auto candidate = m_queue.pop_ready_entry(env_restrictions, scrub_time);
+ auto candidate = m_queue.pop_ready_entry(
+ is_sched_target_eligible, env_restrictions, scrub_time);
if (!candidate) {
dout(20) << "no PGs are ready for scrubbing" << dendl;
return;
}
+/**
+ *
+ * Note: only checking those conditions that are frequent, and should not cause
+ * a queue reshuffle.
+ */
+bool OsdScrub::is_sched_target_eligible(
+ const Scrub::SchedEntry& e,
+ const Scrub::OSDRestrictions& r,
+ utime_t time_now)
+{
+ using ScrubJob = Scrub::ScrubJob;
+ if (e.schedule.not_before > time_now) {
+ return false;
+ }
+ if (r.max_concurrency_reached &&
+ ScrubJob::observes_max_concurrency(e.urgency)) {
+ return false;
+ }
+ if (!r.load_is_low && ScrubJob::observes_random_backoff(e.urgency)) {
+ return false;
+ }
+ if (!r.time_permit && ScrubJob::observes_allowed_hours(e.urgency)) {
+ return false;
+ }
+ if (!r.load_is_low && ScrubJob::observes_load_limit(e.urgency)) {
+ return false;
+ }
+ // recovery?
+ return true;
+}
+
+
Scrub::OSDRestrictions OsdScrub::restrictions_on_scrubbing(
bool is_recovery_active,
utime_t scrub_clock_now) const
bool is_recovery_active,
utime_t scrub_clock_now) const;
+ static bool is_sched_target_eligible(
+ const Scrub::SchedEntry& e,
+ const Scrub::OSDRestrictions& r,
+ utime_t time_now);
+
/**
* initiate a scrub on a specific PG
* The PG is locked, enabling us to query its state. Specifically, we
std::optional<Scrub::SchedEntry> ScrubQueue::pop_ready_entry(
+ EligibilityPred eligibility_pred,
OSDRestrictions restrictions,
utime_t time_now)
{
- auto eligible_filtr = [time_now, rst = restrictions](
+ /// \todo must handle 'only_deadlined'!
+
+ auto eligible_filtr = [&, rst = restrictions](
const SchedEntry& e) -> bool {
- return (e.schedule.not_before <= time_now) &&
- (e.is_high_priority() ||
- (!rst.high_priority_only &&
- (!rst.only_deadlined ||
- (!e.schedule.deadline.is_zero() &&
- e.schedule.deadline <= time_now))));
+ return eligibility_pred(e, rst, time_now);
};
std::unique_lock lck{jobs_lock};
using EntryPred =
std::function<bool(const ::Scrub::SchedEntry&, bool only_eligibles)>;
+ /// a predicate to check entries against some common temporary restrictions
+ using EligibilityPred = std::function<
+ bool(const Scrub::SchedEntry&, const Scrub::OSDRestrictions&, utime_t)>;
+
/**
* the set of all PGs named by the entries in the queue (but only those
* entries that satisfy the predicate)
* nullopt is returned if no such entry exists.
*/
std::optional<Scrub::SchedEntry> pop_ready_entry(
+ EligibilityPred eligibility_pred,
Scrub::OSDRestrictions restrictions,
utime_t time_now);
if (m_active_target) {
f->dump_bool("active", true);
- dump_active_scrubber(f, state_test(PG_STATE_DEEP_SCRUB));
+ dump_active_scrubber(f);
} else {
f->dump_bool("active", false);
- f->dump_bool("must_scrub",
- (m_planned_scrub.must_scrub || m_flags.required));
- f->dump_bool("must_deep_scrub", request_flags.must_deep_scrub);
+ const auto now_is = ceph_clock_now();
+ const auto& earliest = m_scrub_job->earliest_target(now_is);
+ f->dump_bool("must_scrub", earliest.is_high_priority());
+ f->dump_bool("must_deep_scrub", request_flags.must_deep_scrub); // RRR
f->dump_bool("must_repair", request_flags.must_repair);
f->dump_bool("need_auto", request_flags.need_auto);
- f->dump_stream("scrub_reg_stamp") << m_scrub_job->get_sched_time();
-
- // note that we are repeating logic that is coded elsewhere (currently
- // PG.cc). This is not optimal.
- bool deep_expected =
- (ceph_clock_now() >= m_pg->next_deepscrub_interval()) ||
- request_flags.must_deep_scrub || request_flags.need_auto;
- auto sched_state =
- m_scrub_job->scheduling_state(ceph_clock_now(), deep_expected);
+ f->dump_stream("scrub_reg_stamp") << earliest.sched_info.schedule.not_before;
+ auto sched_state = m_scrub_job->scheduling_state(now_is);
f->dump_string("schedule", sched_state);
}
if (m_publish_sessions) {
- f->dump_int("test_sequence",
- m_sessions_counter); // an ever-increasing number used by tests
+ // this is a test-only feature. It is not expected to be used in production.
+ // The 'test_sequence' is an ever-increasing number used by tests.
+ f->dump_int("test_sequence", m_sessions_counter);
}
f->close_section();
}
-void PgScrubber::dump_active_scrubber(ceph::Formatter* f, bool is_deep) const
+void PgScrubber::dump_active_scrubber(ceph::Formatter* f) const
{
f->dump_stream("epoch_start") << m_interval_start;
f->dump_stream("start") << m_start;
f->dump_stream("end") << m_end;
f->dump_stream("max_end") << m_max_end;
f->dump_stream("subset_last_update") << m_subset_last_update;
- // note that m_is_deep will be set some time after PG_STATE_DEEP_SCRUB is
- // asserted. Thus, using the latter.
- f->dump_bool("deep", is_deep);
+ f->dump_bool("deep", m_active_target->is_deep());
// dump the scrub-type flags
f->dump_bool("req_scrub", m_flags.required);
pg_scrub_sched_status_t::queued,
false,
(targ.is_deep() ? scrub_level_t::deep : scrub_level_t::shallow),
- !targ.sched_info.is_high_priority()};
+ !targ.is_high_priority()};
}
// both targets are not ready yet
pg_scrub_sched_status_t::scheduled,
false,
(targ.is_deep() ? scrub_level_t::deep : scrub_level_t::shallow),
- !targ.sched_info.is_high_priority()};
+ !targ.is_high_priority()};
}
void run_callbacks();
// 'query' command data for an active scrub
- void dump_active_scrubber(ceph::Formatter* f, bool is_deep) const;
+ void dump_active_scrubber(ceph::Formatter* f) const;
/**
* move the 'not before' to a later time (with a delay amount that is
return (compr == std::weak_ordering::less) ? shallow_target : deep_target;
}
+
+SchedTarget& ScrubJob::earliest_target(utime_t scrub_clock_now)
+{
+ std::weak_ordering compr = cmp_entries(scrub_clock_now,
+ shallow_target.queued_element(), deep_target.queued_element());
+ return (compr == std::weak_ordering::less) ? shallow_target : deep_target;
+}
+
+const SchedTarget& ScrubJob::earliest_target(utime_t scrub_clock_now) const
+{
+ std::weak_ordering compr = cmp_entries(scrub_clock_now,
+ shallow_target.queued_element(), deep_target.queued_element());
+ return (compr == std::weak_ordering::less) ? shallow_target : deep_target;
+}
+
+
utime_t ScrubJob::get_sched_time() const
{
return earliest_target().sched_info.schedule.not_before;
}
-std::string ScrubJob::scheduling_state(utime_t now_is, bool is_deep_expected)
- const
+std::string ScrubJob::scheduling_state(utime_t now_is) const
{
// if not registered, not a candidate for scrubbing on this OSD (or at all)
if (!registered) {
if (first_ready) {
// the target is ready to be scrubbed
return fmt::format(
- "queued for {}scrub at {:s} (debug RRR: {})",
+ "queued for {}scrub at {:s}",
(first_ready->get().is_deep() ? "deep " : ""),
- first_ready->get().sched_info.schedule.scheduled_at,
- (is_deep_expected ? "deep " : ""));
+ first_ready->get().sched_info.schedule.scheduled_at);
} else {
// both targets are in the future
const auto& nearest = earliest_target();
{
return urgency < urgency_t::operator_requested;
}
+
+bool ScrubJob::observes_random_backoff(urgency_t urgency)
+{
+ return urgency < urgency_t::after_repair;
+}
urgency_t urgency() const { return sched_info.urgency; }
+ /**
+ * a loose definition of 'high priority' scrubs. Can only be used for
+ * logs and user messages. Actual scheduling decisions should be based
+ * on the 'urgency' attribute and its fine-grained characteristics.
+ */
+ bool is_high_priority() const
+ {
+ return urgency() != urgency_t::periodic_regular;
+ }
+
bool was_delayed() const { return sched_info.last_issue != delay_cause_t::none; }
/// provides r/w access to the scheduling sub-object
const SchedTarget& earliest_target() const;
SchedTarget& earliest_target();
+ /**
+ * the target that will be scrubbed first. Basically - used
+ * cmp_entries() to determine the order of the two targets.
+ * Which means: if only one of the targets is eligible, it will be returned.
+ * If both - the one with the highest priority -> level -> target time.
+ * Otherwise - the one with the earliest not-before.
+ */
+ const SchedTarget& earliest_target(utime_t scrub_clock_now) const;
+ SchedTarget& earliest_target(utime_t scrub_clock_now);
+
/// the not-before of our earliest target (either shallow or deep)
utime_t get_sched_time() const;
* a text description of the "scheduling intentions" of this PG:
* are we already scheduled for a scrub/deep scrub? when?
*/
- std::string scheduling_state(utime_t now_is, bool is_deep_expected) const;
+ std::string scheduling_state(utime_t now_is) const;
std::ostream& gen_prefix(std::ostream& out, std::string_view fn) const;
std::string log_msg_prefix;
static bool requires_randomization(urgency_t urgency);
static bool observes_max_concurrency(urgency_t urgency);
-};
+
+ static bool observes_random_backoff(urgency_t urgency);};
} // namespace Scrub
namespace std {
/// either 'none', or the reason for the latest failure/delay (for
/// logging/reporting purposes)
delay_cause_t last_issue{delay_cause_t::none};
-
- // note: is_high_priority() is temporary. Will be removed
- // in a followup commit.
- bool is_high_priority() const
- {
- return urgency != urgency_t::periodic_regular;
- }
};