PglogBasedRecovery::interruptible_future<bool>
PglogBasedRecovery::do_recovery()
{
+ LOG_PREFIX(PglogBasedRecovery::do_recovery);
+ DEBUGDPPI("{}: {}", *pg, __func__, *this);
if (pg->has_reset_since(epoch_started)) {
return seastar::make_ready_future<bool>(false);
}
interruptor>([this] (auto&& trigger) {
return pg->get_recovery_handler()->start_recovery_ops(
trigger,
+ *this,
crimson::common::local_conf()->osd_recovery_max_single_start);
});
});
RecoveryBackend::RecoveryBlockingEvent
> tracking_events;
+ void cancel() {
+ cancelled = true;
+ }
+
+ bool is_cancelled() const {
+ return cancelled;
+ }
+
+ epoch_t get_epoch_started() const {
+ return epoch_started;
+ }
private:
interruptible_future<bool> do_recovery() override;
+ bool cancelled = false;
};
class BackfillRecovery final : public BackgroundRecoveryT<BackfillRecovery> {
peering_state.state_clear(PG_STATE_SNAPTRIM);
peering_state.state_clear(PG_STATE_SNAPTRIM_ERROR);
snap_mapper.reset_backend();
+ reset_pglog_based_recovery_op();
}
void PG::context_registry_on_change() {
DEBUGDPP("remove {} on pglog rollback", *pg, soid);
pg->remove_maybe_snapmapped_object(*t, soid);
}
+
+void PG::set_pglog_based_recovery_op(PglogBasedRecovery *op) {
+ ceph_assert(!pglog_based_recovery_op);
+ pglog_based_recovery_op = op;
+}
+
+void PG::reset_pglog_based_recovery_op() {
+ pglog_based_recovery_op = nullptr;
+}
+
+void PG::cancel_pglog_based_recovery_op() {
+ ceph_assert(pglog_based_recovery_op);
+ pglog_based_recovery_op->cancel();
+ reset_pglog_based_recovery_op();
+}
}
class OpsExecuter;
class BackfillRecovery;
class SnapTrimEvent;
+class PglogBasedRecovery;
class PG : public boost::intrusive_ref_counter<
PG,
recovery_handler->backfill_cancelled();
}
+ void on_recovery_cancelled() final {
+ cancel_pglog_based_recovery_op();
+ }
+
void on_recovery_reserved() final {
recovery_handler->start_pglogbased_recovery();
}
return can_discard_replica_op(m, m.get_map_epoch());
}
+ void set_pglog_based_recovery_op(PglogBasedRecovery *op) final;
+ void reset_pglog_based_recovery_op() final;
+ void cancel_pglog_based_recovery_op();
+
private:
// instead of seastar::gate, we use a boolean flag to indicate
// whether the system is shutting down, as we don't need to track
bool stopping = false;
PGActivationBlocker wait_for_active_blocker;
+ PglogBasedRecovery* pglog_based_recovery_op = nullptr;
friend std::ostream& operator<<(std::ostream&, const PG& pg);
friend class ClientRequest;
using std::map;
using std::set;
+using PglogBasedRecovery = crimson::osd::PglogBasedRecovery;
void PGRecovery::start_pglogbased_recovery()
{
- using PglogBasedRecovery = crimson::osd::PglogBasedRecovery;
- (void) pg->get_shard_services().start_operation<PglogBasedRecovery>(
+ auto [op, fut] = pg->get_shard_services().start_operation<PglogBasedRecovery>(
static_cast<crimson::osd::PG*>(pg),
pg->get_shard_services(),
pg->get_osdmap_epoch(),
float(0.001));
+ pg->set_pglog_based_recovery_op(op.get());
}
PGRecovery::interruptible_future<bool>
PGRecovery::start_recovery_ops(
RecoveryBackend::RecoveryBlockingEvent::TriggerI& trigger,
+ PglogBasedRecovery &recover_op,
size_t max_to_start)
{
assert(pg->is_primary());
assert(pg->is_peered());
- if (!pg->is_recovering() && !pg->is_backfilling()) {
- logger().debug("recovery raced and were queued twice, ignoring!");
+ if (pg->has_reset_since(recover_op.get_epoch_started()) ||
+ recover_op.is_cancelled()) {
+ logger().debug("recovery {} cancelled.", recover_op);
return seastar::make_ready_future<bool>(false);
}
+ ceph_assert(pg->is_recovering());
// in ceph-osd the do_recovery() path handles both the pg log-based
// recovery and the backfill, albeit they are separated at the layer
return interruptor::parallel_for_each(started,
[] (auto&& ifut) {
return std::move(ifut);
- }).then_interruptible([this] {
+ }).then_interruptible([this, &recover_op] {
//TODO: maybe we should implement a recovery race interruptor in the future
- if (!pg->is_recovering() && !pg->is_backfilling()) {
- logger().debug("recovery raced and were queued twice, ignoring!");
+ if (pg->has_reset_since(recover_op.get_epoch_started()) ||
+ recover_op.is_cancelled()) {
+ logger().debug("recovery {} cancelled.", recover_op);
return seastar::make_ready_future<bool>(false);
}
+ ceph_assert(pg->is_recovering());
+ ceph_assert(!pg->is_backfilling());
bool done = !pg->get_peering_state().needs_recovery();
if (done) {
pg->get_osdmap_epoch(),
PeeringState::RequestBackfill{});
}
+ pg->reset_pglog_based_recovery_op();
}
return seastar::make_ready_future<bool>(!done);
});
namespace crimson::osd {
class UrgentRecovery;
+class PglogBasedRecovery;
}
class MOSDPGBackfillRemove;
interruptible_future<bool> start_recovery_ops(
RecoveryBackend::RecoveryBlockingEvent::TriggerI&,
+ crimson::osd::PglogBasedRecovery &recover_op,
size_t max_to_start);
void on_backfill_reserved();
void dispatch_backfill_event(
namespace crimson::osd {
class ShardServices;
+ class PglogBasedRecovery;
};
class RecoveryBackend;
virtual void publish_stats_to_osd() = 0;
virtual OSDriver &get_osdriver() = 0;
virtual SnapMapper &get_snap_mapper() = 0;
+ virtual void set_pglog_based_recovery_op(
+ crimson::osd::PglogBasedRecovery *op) = 0;
+ virtual void reset_pglog_based_recovery_op() = 0;
};
void on_backfill_reserved() override;
void on_backfill_canceled() override;
+ void on_recovery_cancelled() override {}
void on_recovery_reserved() override;
bool is_forced_recovery_or_backfill() const {
ps->state_set(PG_STATE_RECOVERY_WAIT);
pl->cancel_local_background_io_reservation();
release_reservations(true);
+ pl->on_recovery_cancelled();
pl->schedule_event_after(
std::make_shared<PGPeeringEvent>(
ps->get_osdmap_epoch(),
ps->state_set(PG_STATE_RECOVERY_UNFOUND);
pl->cancel_local_background_io_reservation();
release_reservations(true);
+ pl->on_recovery_cancelled();
return transit<NotRecovering>();
}
virtual void on_backfill_reserved() = 0;
virtual void on_backfill_canceled() = 0;
virtual void on_recovery_reserved() = 0;
+ virtual void on_recovery_cancelled() = 0;
// ================recovery space accounting ================
virtual bool try_reserve_recovery_space(