summaryrefslogtreecommitdiffstats
path: root/src/crimson/osd/backfill_state.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/crimson/osd/backfill_state.cc')
-rw-r--r--src/crimson/osd/backfill_state.cc328
1 files changed, 237 insertions, 91 deletions
diff --git a/src/crimson/osd/backfill_state.cc b/src/crimson/osd/backfill_state.cc
index 92b3ea8714c..f957f072c93 100644
--- a/src/crimson/osd/backfill_state.cc
+++ b/src/crimson/osd/backfill_state.cc
@@ -8,11 +8,7 @@
#include "crimson/osd/backfill_state.h"
#include "osd/osd_types_fmt.h"
-namespace {
- seastar::logger& logger() {
- return crimson::get_logger(ceph_subsys_osd);
- }
-}
+SET_SUBSYS(osd);
namespace crimson::osd {
@@ -27,22 +23,23 @@ BackfillState::BackfillState(
progress_tracker(
std::make_unique<BackfillState::ProgressTracker>(backfill_machine))
{
- logger().debug("{}:{}", __func__, __LINE__);
+ LOG_PREFIX(BackfillState::BackfillState);
+ DEBUGDPP("", *backfill_machine.pg);
backfill_machine.initiate();
}
template <class S>
BackfillState::StateHelper<S>::StateHelper()
{
- logger().debug("enter {}",
- boost::typeindex::type_id<S>().pretty_name());
+ LOG_PREFIX(BackfillState::StateHelper);
+ DEBUGDPP("enter {}", pg(), boost::typeindex::type_id<S>().pretty_name());
}
template <class S>
BackfillState::StateHelper<S>::~StateHelper()
{
- logger().debug("exit {}",
- boost::typeindex::type_id<S>().pretty_name());
+ LOG_PREFIX(BackfillState::StateHelper);
+ DEBUG("exit {}", boost::typeindex::type_id<S>().pretty_name());
}
BackfillState::~BackfillState() = default;
@@ -63,22 +60,28 @@ BackfillState::BackfillMachine::~BackfillMachine() = default;
BackfillState::Initial::Initial(my_context ctx)
: my_base(ctx)
{
+ LOG_PREFIX(BackfillState::Initial::Initial);
backfill_state().last_backfill_started = peering_state().earliest_backfill();
- logger().debug("{}: bft={} from {}",
- __func__, peering_state().get_backfill_targets(),
- backfill_state().last_backfill_started);
+ DEBUGDPP("{}: bft={} from {}",
+ pg(),
+ __func__,
+ peering_state().get_backfill_targets(),
+ backfill_state().last_backfill_started);
for (const auto& bt : peering_state().get_backfill_targets()) {
- logger().debug("{}: target shard {} from {}",
- __func__, bt, peering_state().get_peer_last_backfill(bt));
+ DEBUGDPP("{}: target shard {} from {}",
+ pg(), __func__, bt, peering_state().get_peer_last_backfill(bt));
}
ceph_assert(peering_state().get_backfill_targets().size());
ceph_assert(!backfill_state().last_backfill_started.is_max());
+ backfill_state().replicas_in_backfill =
+ peering_state().get_backfill_targets().size();
}
boost::statechart::result
BackfillState::Initial::react(const BackfillState::Triggered& evt)
{
- logger().debug("{}: backfill triggered", __func__);
+ LOG_PREFIX(BackfillState::Initial::react::Triggered);
+ DEBUGDPP("", pg());
ceph_assert(backfill_state().last_backfill_started == \
peering_state().earliest_backfill());
ceph_assert(peering_state().is_backfilling());
@@ -91,24 +94,23 @@ BackfillState::Initial::react(const BackfillState::Triggered& evt)
if (Enqueuing::all_enqueued(peering_state(),
backfill_state().backfill_info,
backfill_state().peer_backfill_info)) {
- logger().debug("{}: switching to Done state", __func__);
+ DEBUGDPP("switching to Done state", pg());
return transit<BackfillState::Done>();
} else {
- logger().debug("{}: switching to Enqueuing state", __func__);
+ DEBUGDPP("switching to Enqueuing state", pg());
return transit<BackfillState::Enqueuing>();
}
}
-
// -- Enqueuing
void BackfillState::Enqueuing::maybe_update_range()
{
+ LOG_PREFIX(BackfillState::Enqueuing::maybe_update_range);
if (auto& primary_bi = backfill_state().backfill_info;
primary_bi.version >= pg().get_projected_last_update()) {
- logger().info("{}: bi is current", __func__);
+ INFODPP("bi is current", pg());
ceph_assert(primary_bi.version == pg().get_projected_last_update());
} else if (primary_bi.version >= peering_state().get_log_tail()) {
-#if 0
if (peering_state().get_pg_log().get_log().empty() &&
pg().get_projected_log().empty()) {
/* Because we don't move log_tail on split, the log might be
@@ -120,30 +122,32 @@ void BackfillState::Enqueuing::maybe_update_range()
ceph_assert(primary_bi.version == eversion_t());
return;
}
-#endif
- logger().debug("{}: bi is old, ({}) can be updated with log to {}",
- __func__,
- primary_bi.version,
- pg().get_projected_last_update());
- logger().debug("{}: scanning pg log first", __func__);
- peering_state().scan_log_after(primary_bi.version,
+ DEBUGDPP("{}: bi is old, ({}) can be updated with log to {}",
+ pg(),
+ primary_bi.version,
+ pg().get_projected_last_update());
+ auto func =
[&](const pg_log_entry_t& e) {
- logger().debug("maybe_update_range(lambda): updating from version {}",
- e.version);
+ DEBUGDPP("maybe_update_range(lambda): updating from version {}",
+ pg(), e.version);
if (e.soid >= primary_bi.begin && e.soid < primary_bi.end) {
if (e.is_update()) {
- logger().debug("maybe_update_range(lambda): {} updated to ver {}",
- e.soid, e.version);
+ DEBUGDPP("maybe_update_range(lambda): {} updated to ver {}",
+ pg(), e.soid, e.version);
primary_bi.objects.erase(e.soid);
primary_bi.objects.insert(std::make_pair(e.soid,
e.version));
} else if (e.is_delete()) {
- logger().debug("maybe_update_range(lambda): {} removed",
- e.soid);
+ DEBUGDPP("maybe_update_range(lambda): {} removed",
+ pg(), e.soid);
primary_bi.objects.erase(e.soid);
}
}
- });
+ };
+ DEBUGDPP("{}: scanning pg log first", pg());
+ peering_state().scan_log_after(primary_bi.version, func);
+ DEBUGDPP("{}: scanning projected log", pg());
+ pg().get_projected_log().scan_log_after(primary_bi.version, func);
primary_bi.version = pg().get_projected_last_update();
} else {
ceph_abort_msg(
@@ -208,7 +212,7 @@ bool BackfillState::Enqueuing::should_rescan_primary(
const BackfillInterval& backfill_info) const
{
return backfill_info.begin <= earliest_peer_backfill(peer_backfill_info) &&
- !backfill_info.extends_to_end();
+ !backfill_info.extends_to_end() && backfill_info.empty();
}
void BackfillState::Enqueuing::trim_backfilled_object_from_intervals(
@@ -226,6 +230,7 @@ void BackfillState::Enqueuing::trim_backfilled_object_from_intervals(
BackfillState::Enqueuing::result_t
BackfillState::Enqueuing::remove_on_peers(const hobject_t& check)
{
+ LOG_PREFIX(BackfillState::Enqueuing::remove_on_peers);
// set `new_last_backfill_started` to `check`
result_t result { {}, check };
for (const auto& bt : peering_state().get_backfill_targets()) {
@@ -237,8 +242,8 @@ BackfillState::Enqueuing::remove_on_peers(const hobject_t& check)
backfill_listener().enqueue_drop(bt, pbi.begin, version);
}
}
- logger().debug("{}: BACKFILL removing {} from peers {}",
- __func__, check, result.pbi_targets);
+ DEBUGDPP("BACKFILL removing {} from peers {}",
+ pg(), check, result.pbi_targets);
ceph_assert(!result.pbi_targets.empty());
return result;
}
@@ -246,9 +251,11 @@ BackfillState::Enqueuing::remove_on_peers(const hobject_t& check)
BackfillState::Enqueuing::result_t
BackfillState::Enqueuing::update_on_peers(const hobject_t& check)
{
- logger().debug("{}: check={}", __func__, check);
+ LOG_PREFIX(BackfillState::Enqueuing::update_on_peers);
+ DEBUGDPP("check={}", pg(), check);
const auto& primary_bi = backfill_state().backfill_info;
result_t result { {}, primary_bi.begin };
+ std::map<hobject_t, std::pair<eversion_t, std::vector<pg_shard_t>>> backfills;
for (const auto& bt : peering_state().get_backfill_targets()) {
const auto& peer_bi = backfill_state().peer_backfill_info.at(bt);
@@ -256,9 +263,13 @@ BackfillState::Enqueuing::update_on_peers(const hobject_t& check)
// Find all check peers that have the wrong version
if (const eversion_t& obj_v = primary_bi.objects.begin()->second;
check == primary_bi.begin && check == peer_bi.begin) {
- if(peer_bi.objects.begin()->second != obj_v &&
- backfill_state().progress_tracker->enqueue_push(primary_bi.begin)) {
- backfill_listener().enqueue_push(primary_bi.begin, obj_v);
+ if (peer_bi.objects.begin()->second != obj_v) {
+ std::ignore = backfill_state().progress_tracker->enqueue_push(
+ primary_bi.begin);
+ auto &[v, peers] = backfills[primary_bi.begin];
+ assert(v == obj_v || v == eversion_t());
+ v = obj_v;
+ peers.push_back(bt);
} else {
// it's fine, keep it! OR already recovering
}
@@ -267,12 +278,22 @@ BackfillState::Enqueuing::update_on_peers(const hobject_t& check)
// Only include peers that we've caught up to their backfill line
// otherwise, they only appear to be missing this object
// because their peer_bi.begin > backfill_info.begin.
- if (primary_bi.begin > peering_state().get_peer_last_backfill(bt) &&
- backfill_state().progress_tracker->enqueue_push(primary_bi.begin)) {
- backfill_listener().enqueue_push(primary_bi.begin, obj_v);
+ if (primary_bi.begin > peering_state().get_peer_last_backfill(bt)) {
+ std::ignore = backfill_state().progress_tracker->enqueue_push(
+ primary_bi.begin);
+ auto &[v, peers] = backfills[primary_bi.begin];
+ assert(v == obj_v || v == eversion_t());
+ v = obj_v;
+ peers.push_back(bt);
}
}
}
+ for (auto &backfill : backfills) {
+ auto &soid = backfill.first;
+ auto &obj_v = backfill.second.first;
+ auto &peers = backfill.second.second;
+ backfill_listener().enqueue_push(soid, obj_v, peers);
+ }
return result;
}
@@ -292,6 +313,7 @@ bool BackfillState::Enqueuing::Enqueuing::all_emptied(
BackfillState::Enqueuing::Enqueuing(my_context ctx)
: my_base(ctx)
{
+ LOG_PREFIX(BackfillState::Enqueuing::Enqueuing);
auto& primary_bi = backfill_state().backfill_info;
// update our local interval to cope with recent changes
@@ -301,8 +323,7 @@ BackfillState::Enqueuing::Enqueuing(my_context ctx)
// that backfill will be spinning here over and over. For the sake
// of performance and complexity we don't synchronize with entire PG.
// similar can happen in classical OSD.
- logger().warn("{}: bi is old, rescanning of local backfill_info",
- __func__);
+ WARNDPP("bi is old, rescanning of local backfill_info", pg());
post_event(RequestPrimaryScanning{});
return;
} else {
@@ -310,16 +331,30 @@ BackfillState::Enqueuing::Enqueuing(my_context ctx)
}
trim_backfill_infos();
- while (!all_emptied(primary_bi, backfill_state().peer_backfill_info)) {
+ if (should_rescan_primary(backfill_state().peer_backfill_info,
+ primary_bi)) {
+ // need to grab one another chunk of the object namespace and restart
+ // the queueing.
+ DEBUGDPP("reached end for current local chunk", pg());
+ post_event(RequestPrimaryScanning{});
+ return;
+ }
+
+ do {
if (!backfill_listener().budget_available()) {
+ DEBUGDPP("throttle failed, turning to Waiting", pg());
post_event(RequestWaiting{});
return;
} else if (should_rescan_replicas(backfill_state().peer_backfill_info,
- primary_bi)) {
+ primary_bi)) {
// Count simultaneous scans as a single op and let those complete
post_event(RequestReplicasScanning{});
return;
}
+
+ if (all_emptied(primary_bi, backfill_state().peer_backfill_info)) {
+ break;
+ }
// Get object within set of peers to operate on and the set of targets
// for which that object applies.
if (const hobject_t check = \
@@ -333,28 +368,36 @@ BackfillState::Enqueuing::Enqueuing(my_context ctx)
trim_backfilled_object_from_intervals(std::move(result),
backfill_state().last_backfill_started,
backfill_state().peer_backfill_info);
- } else {
+ backfill_listener().maybe_flush();
+ } else if (!primary_bi.empty()) {
auto result = update_on_peers(check);
trim_backfilled_object_from_intervals(std::move(result),
backfill_state().last_backfill_started,
backfill_state().peer_backfill_info);
primary_bi.pop_front();
+ backfill_listener().maybe_flush();
+ } else {
+ break;
}
- backfill_listener().maybe_flush();
- }
+ } while (!all_emptied(primary_bi, backfill_state().peer_backfill_info));
if (should_rescan_primary(backfill_state().peer_backfill_info,
- primary_bi)) {
+ primary_bi)) {
// need to grab one another chunk of the object namespace and restart
// the queueing.
- logger().debug("{}: reached end for current local chunk",
- __func__);
+ DEBUGDPP("reached end for current local chunk", pg());
post_event(RequestPrimaryScanning{});
- } else if (backfill_state().progress_tracker->tracked_objects_completed()) {
- post_event(RequestDone{});
+ return;
} else {
- logger().debug("{}: reached end for both local and all peers "
- "but still has in-flight operations", __func__);
+ if (backfill_state().progress_tracker->tracked_objects_completed()
+ && Enqueuing::all_enqueued(peering_state(),
+ backfill_state().backfill_info,
+ backfill_state().peer_backfill_info)) {
+ backfill_state().last_backfill_started = hobject_t::get_max();
+ backfill_listener().update_peers_last_backfill(hobject_t::get_max());
+ }
+ DEBUGDPP("reached end for both local and all peers "
+ "but still has in-flight operations", pg());
post_event(RequestWaiting{});
}
}
@@ -371,17 +414,46 @@ BackfillState::PrimaryScanning::PrimaryScanning(my_context ctx)
boost::statechart::result
BackfillState::PrimaryScanning::react(PrimaryScanned evt)
{
- logger().debug("{}", __func__);
+ LOG_PREFIX(BackfillState::PrimaryScanning::react::PrimaryScanned);
+ DEBUGDPP("", pg());
backfill_state().backfill_info = std::move(evt.result);
- return transit<Enqueuing>();
+ if (!backfill_state().is_suspended()) {
+ return transit<Enqueuing>();
+ } else {
+ DEBUGDPP("backfill suspended, not going Enqueuing", pg());
+ backfill_state().go_enqueuing_on_resume();
+ }
+ return discard_event();
+}
+
+boost::statechart::result
+BackfillState::PrimaryScanning::react(CancelBackfill evt)
+{
+ LOG_PREFIX(BackfillState::PrimaryScanning::react::SuspendBackfill);
+ DEBUGDPP("suspended within PrimaryScanning", pg());
+ backfill_state().on_suspended();
+ return discard_event();
+}
+
+boost::statechart::result
+BackfillState::PrimaryScanning::react(Triggered evt)
+{
+ LOG_PREFIX(BackfillState::PrimaryScanning::react::Triggered);
+ ceph_assert(backfill_state().is_suspended());
+ if (backfill_state().on_resumed()) {
+ DEBUGDPP("Backfill resumed, going Enqueuing", pg());
+ return transit<Enqueuing>();
+ }
+ return discard_event();
}
boost::statechart::result
BackfillState::PrimaryScanning::react(ObjectPushed evt)
{
- logger().debug("PrimaryScanning::react() on ObjectPushed; evt.object={}",
- evt.object);
- backfill_state().progress_tracker->complete_to(evt.object, evt.stat);
+ LOG_PREFIX(BackfillState::PrimaryScanning::react::ObjectPushed);
+ DEBUGDPP("PrimaryScanning::react() on ObjectPushed; evt.object={}",
+ pg(), evt.object);
+ backfill_state().progress_tracker->complete_to(evt.object, evt.stat, true);
return discard_event();
}
@@ -398,11 +470,11 @@ bool BackfillState::ReplicasScanning::replica_needs_scan(
BackfillState::ReplicasScanning::ReplicasScanning(my_context ctx)
: my_base(ctx)
{
+ LOG_PREFIX(BackfillState::ReplicasScanning::ReplicasScanning);
for (const auto& bt : peering_state().get_backfill_targets()) {
if (const auto& pbi = backfill_state().peer_backfill_info.at(bt);
replica_needs_scan(pbi, backfill_state().backfill_info)) {
- logger().debug("{}: scanning peer osd.{} from {}",
- __func__, bt, pbi.end);
+ DEBUGDPP("scanning peer osd.{} from {}", pg(), bt, pbi.end);
backfill_listener().request_replica_scan(bt, pbi.end, hobject_t{});
ceph_assert(waiting_on_backfill.find(bt) == \
@@ -424,8 +496,9 @@ BackfillState::ReplicasScanning::~ReplicasScanning()
boost::statechart::result
BackfillState::ReplicasScanning::react(ReplicaScanned evt)
{
- logger().debug("{}: got scan result from osd={}, result={}",
- __func__, evt.from, evt.result);
+ LOG_PREFIX(BackfillState::ReplicasScanning::react::ReplicaScanned);
+ DEBUGDPP("got scan result from osd={}, result={}",
+ pg(), evt.from, evt.result);
// TODO: maybe we'll be able to move waiting_on_backfill from
// the machine to the state.
ceph_assert(peering_state().is_backfill_target(evt.from));
@@ -434,12 +507,38 @@ BackfillState::ReplicasScanning::react(ReplicaScanned evt)
if (waiting_on_backfill.empty()) {
ceph_assert(backfill_state().peer_backfill_info.size() == \
peering_state().get_backfill_targets().size());
- return transit<Enqueuing>();
+ if (!backfill_state().is_suspended()) {
+ return transit<Enqueuing>();
+ } else {
+ DEBUGDPP("backfill suspended, not going Enqueuing", pg());
+ backfill_state().go_enqueuing_on_resume();
+ }
}
} else {
- // we canceled backfill for a while due to a too full, and this
+ // we suspended backfill for a while due to a too full, and this
// is an extra response from a non-too-full peer
- logger().debug("{}: canceled backfill (too full?)", __func__);
+ DEBUGDPP("suspended backfill (too full?)", pg());
+ }
+ return discard_event();
+}
+
+boost::statechart::result
+BackfillState::ReplicasScanning::react(CancelBackfill evt)
+{
+ LOG_PREFIX(BackfillState::ReplicasScanning::react::SuspendBackfill);
+ DEBUGDPP("suspended within ReplicasScanning", pg());
+ backfill_state().on_suspended();
+ return discard_event();
+}
+
+boost::statechart::result
+BackfillState::ReplicasScanning::react(Triggered evt)
+{
+ LOG_PREFIX(BackfillState::ReplicasScanning::react::Triggered);
+ ceph_assert(backfill_state().is_suspended());
+ if (backfill_state().on_resumed()) {
+ DEBUGDPP("Backfill resumed, going Enqueuing", pg());
+ return transit<Enqueuing>();
}
return discard_event();
}
@@ -447,9 +546,10 @@ BackfillState::ReplicasScanning::react(ReplicaScanned evt)
boost::statechart::result
BackfillState::ReplicasScanning::react(ObjectPushed evt)
{
- logger().debug("ReplicasScanning::react() on ObjectPushed; evt.object={}",
- evt.object);
- backfill_state().progress_tracker->complete_to(evt.object, evt.stat);
+ LOG_PREFIX(BackfillState::ReplicasScanning::react::ObjectPushed);
+ DEBUGDPP("ReplicasScanning::react() on ObjectPushed; evt.object={}",
+ pg(), evt.object);
+ backfill_state().progress_tracker->complete_to(evt.object, evt.stat, true);
return discard_event();
}
@@ -463,27 +563,45 @@ BackfillState::Waiting::Waiting(my_context ctx)
boost::statechart::result
BackfillState::Waiting::react(ObjectPushed evt)
{
- logger().debug("Waiting::react() on ObjectPushed; evt.object={}",
- evt.object);
- backfill_state().progress_tracker->complete_to(evt.object, evt.stat);
- if (!Enqueuing::all_enqueued(peering_state(),
- backfill_state().backfill_info,
- backfill_state().peer_backfill_info)) {
+ LOG_PREFIX(BackfillState::Waiting::react::ObjectPushed);
+ DEBUGDPP("Waiting::react() on ObjectPushed; evt.object={}", pg(), evt.object);
+ backfill_state().progress_tracker->complete_to(evt.object, evt.stat, false);
+ if (!backfill_state().is_suspended()) {
return transit<Enqueuing>();
- } else if (backfill_state().progress_tracker->tracked_objects_completed()) {
- return transit<Done>();
} else {
- // we still have something to wait on
- logger().debug("Waiting::react() on ObjectPushed; still waiting");
- return discard_event();
+ DEBUGDPP("backfill suspended, not going Enqueuing", pg());
+ backfill_state().go_enqueuing_on_resume();
+ }
+ return discard_event();
+}
+
+boost::statechart::result
+BackfillState::Waiting::react(CancelBackfill evt)
+{
+ LOG_PREFIX(BackfillState::Waiting::react::SuspendBackfill);
+ DEBUGDPP("suspended within Waiting", pg());
+ backfill_state().on_suspended();
+ return discard_event();
+}
+
+boost::statechart::result
+BackfillState::Waiting::react(Triggered evt)
+{
+ LOG_PREFIX(BackfillState::Waiting::react::Triggered);
+ ceph_assert(backfill_state().is_suspended());
+ if (backfill_state().on_resumed()) {
+ DEBUGDPP("Backfill resumed, going Enqueuing", pg());
+ return transit<Enqueuing>();
}
+ return discard_event();
}
// -- Done
BackfillState::Done::Done(my_context ctx)
: my_base(ctx)
{
- logger().info("{}: backfill is done", __func__);
+ LOG_PREFIX(BackfillState::Done::Done);
+ INFODPP("backfill is done", pg());
backfill_listener().backfilled();
}
@@ -523,10 +641,11 @@ void BackfillState::ProgressTracker::enqueue_drop(const hobject_t& obj)
void BackfillState::ProgressTracker::complete_to(
const hobject_t& obj,
- const pg_stat_t& stats)
+ const pg_stat_t& stats,
+ bool may_push_to_max)
{
- logger().debug("{}: obj={}",
- __func__, obj);
+ LOG_PREFIX(BackfillState::ProgressTracker::complete_to);
+ DEBUGDPP("obj={}", pg(), obj);
if (auto completion_iter = registry.find(obj);
completion_iter != std::end(registry)) {
completion_iter->second = \
@@ -534,6 +653,7 @@ void BackfillState::ProgressTracker::complete_to(
} else {
ceph_abort_msg("completing untracked object shall not happen");
}
+ auto new_last_backfill = peering_state().earliest_backfill();
for (auto it = std::begin(registry);
it != std::end(registry) &&
it->second.stage != op_stage_t::enqueued_push;
@@ -543,16 +663,42 @@ void BackfillState::ProgressTracker::complete_to(
peering_state().update_complete_backfill_object_stats(
soid,
*item.stats);
+ assert(soid > new_last_backfill);
+ new_last_backfill = soid;
}
- if (Enqueuing::all_enqueued(peering_state(),
+ if (may_push_to_max &&
+ Enqueuing::all_enqueued(peering_state(),
backfill_state().backfill_info,
backfill_state().peer_backfill_info) &&
tracked_objects_completed()) {
backfill_state().last_backfill_started = hobject_t::get_max();
backfill_listener().update_peers_last_backfill(hobject_t::get_max());
} else {
- backfill_listener().update_peers_last_backfill(obj);
+ backfill_listener().update_peers_last_backfill(new_last_backfill);
}
}
+void BackfillState::enqueue_standalone_push(
+ const hobject_t &obj,
+ const eversion_t &v,
+ const std::vector<pg_shard_t> &peers) {
+ progress_tracker->enqueue_push(obj);
+ backfill_machine.backfill_listener.enqueue_push(obj, v, peers);
+}
+
+void BackfillState::enqueue_standalone_delete(
+ const hobject_t &obj,
+ const eversion_t &v,
+ const std::vector<pg_shard_t> &peers)
+{
+ progress_tracker->enqueue_drop(obj);
+ for (auto bt : peers) {
+ backfill_machine.backfill_listener.enqueue_drop(bt, obj, v);
+ }
+}
+
+std::ostream &operator<<(std::ostream &out, const BackfillState::PGFacade &pg) {
+ return pg.print(out);
+}
+
} // namespace crimson::osd