}
WRITE_CLASS_ENCODER(PastIntervals::pg_interval_t)
-void PastIntervals::encode(bufferlist &bl) const
-{
- ::encode(past_intervals, bl);
-}
-void PastIntervals::decode(bufferlist::iterator &bl)
-{
- ::decode(past_intervals, bl);
-}
+class pi_simple_rep : public PastIntervals::interval_rep {
+ map<epoch_t, PastIntervals::pg_interval_t> interval_map;
+public:
+ size_t size() const override { return interval_map.size(); }
+ bool empty() const override { return interval_map.empty(); }
+ void clear() override { interval_map.clear(); }
+ pair<pair<epoch_t, epoch_t>, epoch_t> get_bounds() const override {
+ auto iter = interval_map.begin();
+ if (iter != interval_map.end()) {
+ auto riter = interval_map.rbegin();
+ return make_pair(
+ make_pair(iter->second.first, iter->second.last + 1),
+ riter->second.last + 1);
+ } else {
+ assert(0 == "get_bounds only valid if !empty()");
+ return make_pair(make_pair(0, 0), 0);
+ }
+ }
+ set<pg_shard_t> get_might_have_unfound(
+ pg_shard_t pg_whoami,
+ bool ec_pool) const override {
+ set<pg_shard_t> might_have_unfound;
+
+ // We need to decide who might have unfound objects that we need
+ auto p = interval_map.rbegin();
+ auto end = interval_map.rend();
+ for (; p != end; ++p) {
+ const PastIntervals::pg_interval_t &interval(p->second);
+ // If nothing changed, we don't care about this interval.
+ if (!interval.maybe_went_rw)
+ continue;
-void PastIntervals::dump(Formatter *f) const
-{
- f->open_array_section("PastIntervals");
- for (auto &&i: past_intervals) {
- f->open_object_section("pg_interval_t");
- f->dump_int("epoch", i.first);
- f->open_object_section("interval");
- i.second.dump(f);
- f->close_section();
+ int i = 0;
+ std::vector<int>::const_iterator a = interval.acting.begin();
+ std::vector<int>::const_iterator a_end = interval.acting.end();
+ for (; a != a_end; ++a, ++i) {
+ pg_shard_t shard(*a, ec_pool ? shard_id_t(i) : shard_id_t::NO_SHARD);
+ if (*a != CRUSH_ITEM_NONE && shard != pg_whoami)
+ might_have_unfound.insert(shard);
+ }
+ }
+ return might_have_unfound;
+ }
+ void add_interval(
+ bool ec_pool,
+ const PastIntervals::pg_interval_t &interval) override {
+ interval_map[interval.first] = interval;
+ }
+ unique_ptr<PastIntervals::interval_rep> clone() const override {
+ return unique_ptr<PastIntervals::interval_rep>(new pi_simple_rep(*this));
+ }
+ ostream &print(ostream &out) const override {
+ return out << interval_map;
+ }
+ void encode(bufferlist &bl) const override {
+ ::encode(interval_map, bl);
+ }
+ void decode(bufferlist::iterator &bl) override {
+ ::decode(interval_map, bl);
+ }
+ void dump(Formatter *f) const override {
+ f->open_array_section("PastIntervals::compat_rep");
+ for (auto &&i: interval_map) {
+ f->open_object_section("pg_interval_t");
+ f->dump_int("epoch", i.first);
+ f->open_object_section("interval");
+ i.second.dump(f);
+ f->close_section();
+ f->close_section();
+ }
f->close_section();
}
- f->close_section();
-}
+ bool is_classic() const override {
+ return true;
+ }
+ static void generate_test_instances(list<pi_simple_rep*> &o) {
+ /* todo */
+ return;
+ }
+ void iterate_mayberw_back_to(
+ bool ec_pool,
+ epoch_t les,
+ std::function<void(epoch_t, const set<pg_shard_t> &)> &&f) const override {
+ for (auto i = interval_map.rbegin(); i != interval_map.rend(); ++i) {
+ if (i->second.last < les)
+ break;
+ set<pg_shard_t> actingset;
+ for (unsigned j = 0; j < i->second.acting.size(); ++j) {
+ if (i->second.acting[j] == CRUSH_ITEM_NONE)
+ continue;
+ actingset.insert(
+ pg_shard_t(
+ i->second.acting[j],
+ ec_pool ? shard_id_t(j) : shard_id_t::NO_SHARD));
+ }
+ f(i->second.first, actingset);
+ }
+ }
+ virtual ~pi_simple_rep() override {}
+};
-void PastIntervals::generate_test_instances(list<PastIntervals*> &o)
+
+PastIntervals::PastIntervals(const PastIntervals &rhs)
+ : past_intervals(rhs.past_intervals ?
+ rhs.past_intervals->clone() :
+ nullptr) {}
+
+PastIntervals &PastIntervals::operator=(const PastIntervals &rhs)
{
- /* todo */
- return;
+ PastIntervals other(rhs);
+ ::swap(other, *this);
+ return *this;
}
ostream& operator<<(ostream& out, const PastIntervals &i)
{
- return out << i.past_intervals;
+ assert(i.past_intervals);
+ return i.past_intervals->print(out);
}
-
-set<pg_shard_t> PastIntervals::get_might_have_unfound(
- pg_shard_t pg_whoami,
- bool ec_pool) const
+void PastIntervals::decode(bufferlist::iterator &bl)
{
- set<pg_shard_t> might_have_unfound;
-
- // We need to decide who might have unfound objects that we need
- PastIntervals::const_reverse_iterator p = past_intervals.rbegin();
- PastIntervals::const_reverse_iterator end = past_intervals.rend();
- for (; p != end; ++p) {
-
- const PastIntervals::pg_interval_t &interval(p->second);
- // If nothing changed, we don't care about this interval.
- if (!interval.maybe_went_rw)
- continue;
-
- int i = 0;
- std::vector<int>::const_iterator a = interval.acting.begin();
- std::vector<int>::const_iterator a_end = interval.acting.end();
- for (; a != a_end; ++a, ++i) {
- pg_shard_t shard(*a, ec_pool ? shard_id_t(i) : shard_id_t::NO_SHARD);
- if (*a != CRUSH_ITEM_NONE && shard != pg_whoami)
- might_have_unfound.insert(shard);
- }
+ DECODE_START(1, bl);
+ __u8 classic = 0;
+ ::decode(classic, bl);
+ if (classic) {
+ past_intervals.reset(new pi_simple_rep);
+ past_intervals->decode(bl);
+ } else {
+ assert(0 == "not implemented");
}
- return might_have_unfound;
+ DECODE_FINISH(bl);
}
-bool PastIntervals::has_crashed_interval_since(
- epoch_t since,
- const vector<int> &acting,
- const vector<int> &up,
- const OSDMap &osdmap,
- const DoutPrefixProvider *dpp) const {
- bool crashed = false;
-
- for (PastIntervals::const_reverse_iterator p = past_intervals.rbegin();
- p != past_intervals.rend();
- ++p) {
- const PastIntervals::pg_interval_t &interval = p->second;
- ldpp_dout(dpp,10) << "may_need_replay " << interval << dendl;
-
- if (interval.last < since)
- break; // we don't care
-
- if (interval.acting.empty())
- continue;
-
- if (!interval.maybe_went_rw)
- continue;
-
- // look at whether any of the osds during this interval survived
- // past the end of the interval (i.e., didn't crash and
- // potentially fail to COMMIT a write that it ACKed).
- bool any_survived_interval = false;
+void PastIntervals::decode_classic(bufferlist::iterator &bl)
+{
+ past_intervals.reset(new pi_simple_rep);
+ past_intervals->decode(bl);
+}
- // consider ACTING osds
- for (unsigned i=0; i<interval.acting.size(); i++) {
- int o = interval.acting[i];
- if (o == CRUSH_ITEM_NONE)
- continue;
+void PastIntervals::generate_test_instances(list<PastIntervals*> &o)
+{
+ /* todo */
+ return;
+}
- const osd_info_t *pinfo = 0;
- if (osdmap.exists(o))
- pinfo = &osdmap.get_info(o);
-
- // does this osd appear to have survived through the end of the
- // interval?
- if (pinfo) {
- if (pinfo->up_from <= interval.first && pinfo->up_thru > interval.last) {
- ldpp_dout(dpp, 10) << "may_need_replay osd." << o
- << " up_from " << pinfo->up_from
- << " up_thru " << pinfo->up_thru
- << " survived the interval" << dendl;
- any_survived_interval = true;
- }
- else if (pinfo->up_from <= interval.first &&
- (std::find(acting.begin(), acting.end(), o) != acting.end() ||
- std::find(up.begin(), up.end(), o) != up.end())) {
- ldpp_dout(dpp, 10) << "may_need_replay osd." << o
- << " up_from " << pinfo->up_from
- << " and is in acting|up,"
- << " assumed to have survived the interval"
- << dendl;
- // (if it hasn't, we will rebuild PriorSet)
- any_survived_interval = true;
- }
- else if (pinfo->up_from > interval.last &&
- pinfo->last_clean_begin <= interval.first &&
- pinfo->last_clean_end > interval.last) {
- ldpp_dout(dpp, 10) << "may_need_replay prior osd." << o
- << " up_from " << pinfo->up_from
- << " and last clean interval ["
- << pinfo->last_clean_begin << ","
- << pinfo->last_clean_end
- << ") survived the interval" << dendl;
- any_survived_interval = true;
- }
- }
+void PastIntervals::update_type_from_map(bool ec_pool, const OSDMap &osdmap)
+{
+ if (!osdmap.test_flag(CEPH_OSDMAP_REQUIRE_LUMINOUS)) {
+ if (!past_intervals) {
+ past_intervals.reset(new pi_simple_rep);
+ } else {
+ assert(is_classic());
}
-
- if (!any_survived_interval) {
- ldpp_dout(dpp, 3) << "may_need_replay no known survivors of interval "
- << interval.first << "-" << interval.last
- << ", may need replay" << dendl;
- crashed = true;
- break;
+ } else {
+ if (!past_intervals) {
+ // inialize new type
+ } else if (!is_classic()) {
+ // upgrade
}
}
- return crashed;
}
bool PastIntervals::is_new_interval(
// NOTE: a change in the up set primary triggers an interval
// change, even though the interval members in the pg_interval_t
// do not change.
+ assert(past_intervals);
+ assert(past_intervals->past_intervals);
if (is_new_interval(
old_acting_primary,
new_acting_primary,
osdmap,
lastmap,
pgid)) {
- pg_interval_t& i = past_intervals->past_intervals[same_interval_since];
+ pg_interval_t i;
i.first = same_interval_since;
i.last = osdmap->get_epoch() - 1;
assert(i.first <= i.last);
if (out)
*out << __func__ << " " << i << " : acting set is too small" << std::endl;
}
+ past_intervals->past_intervals->add_interval(old_pg_pool.ec_pool(), i);
return true;
} else {
return false;
}
PastIntervals::PriorSet::PriorSet(
+ const PastIntervals &past_intervals,
bool ec_pool,
IsPGRecoverablePredicate *c,
const OSDMap &osdmap,
- const PastIntervals &past_intervals,
const vector<int> &up,
const vector<int> &acting,
const pg_info_t &info,
probe.insert(pg_shard_t(up[i], ec_pool ? shard_id_t(i) : shard_id_t::NO_SHARD));
}
- for (PastIntervals::const_reverse_iterator p = past_intervals.rbegin();
- p != past_intervals.rend();
- ++p) {
- const PastIntervals::pg_interval_t &interval = p->second;
- ldpp_dout(dpp, 10) << "build_prior " << interval << dendl;
-
- if (interval.last < info.history.last_epoch_started)
- break; // we don't care
-
- if (interval.acting.empty())
- continue;
-
- if (!interval.maybe_went_rw)
- continue;
-
- // look at candidate osds during this interval. each falls into
- // one of three categories: up, down (but potentially
- // interesting), or lost (down, but we won't wait for it).
- set<pg_shard_t> up_now;
- bool any_down_now = false; // any candidates down now (that might have useful data)
-
- // consider ACTING osds
- for (unsigned i = 0; i < interval.acting.size(); i++) {
- int o = interval.acting[i];
- if (o == CRUSH_ITEM_NONE)
- continue;
- pg_shard_t so(o, ec_pool ? shard_id_t(i) : shard_id_t::NO_SHARD);
-
- const osd_info_t *pinfo = 0;
- if (osdmap.exists(o))
- pinfo = &osdmap.get_info(o);
-
- if (osdmap.is_up(o)) {
- // include past acting osds if they are up.
- probe.insert(so);
- up_now.insert(so);
- } else if (!pinfo) {
- ldpp_dout(dpp, 10) << "build_prior prior osd." << o << " no longer exists" << dendl;
- down.insert(o);
- } else if (pinfo->lost_at > interval.first) {
- ldpp_dout(dpp, 10) << "build_prior prior osd." << o << " is down, but lost_at " << pinfo->lost_at << dendl;
- up_now.insert(so);
- down.insert(o);
- } else {
- ldpp_dout(dpp, 10) << "build_prior prior osd." << o << " is down" << dendl;
- down.insert(o);
- any_down_now = true;
+ past_intervals.iterate_mayberw_back_to(
+ ec_pool,
+ info.history.last_epoch_started,
+ [&](epoch_t start, const set<pg_shard_t> &acting) {
+ ldpp_dout(dpp, 10) << "build_prior maybe_rw interval:" << start
+ << ", acting: " << acting << dendl;
+
+ // look at candidate osds during this interval. each falls into
+ // one of three categories: up, down (but potentially
+ // interesting), or lost (down, but we won't wait for it).
+ set<pg_shard_t> up_now;
+ // any candidates down now (that might have useful data)
+ bool any_down_now = false;
+
+ // consider ACTING osds
+ for (auto &&so: acting) {
+ const osd_info_t *pinfo = 0;
+ if (osdmap.exists(so.osd))
+ pinfo = &osdmap.get_info(so.osd);
+
+ if (osdmap.is_up(so.osd)) {
+ // include past acting osds if they are up.
+ probe.insert(so);
+ up_now.insert(so);
+ } else if (!pinfo) {
+ ldpp_dout(dpp, 10) << "build_prior prior osd." << so.osd
+ << " no longer exists" << dendl;
+ down.insert(so.osd);
+ } else if (pinfo->lost_at > start) {
+ ldpp_dout(dpp, 10) << "build_prior prior osd." << so.osd
+ << " is down, but lost_at " << pinfo->lost_at << dendl;
+ up_now.insert(so);
+ down.insert(so.osd);
+ } else {
+ ldpp_dout(dpp, 10) << "build_prior prior osd." << so.osd
+ << " is down" << dendl;
+ down.insert(so.osd);
+ any_down_now = true;
+ }
}
- }
- // if not enough osds survived this interval, and we may have gone rw,
- // then we need to wait for one of those osds to recover to
- // ensure that we haven't lost any information.
- if (!(*pcontdec)(up_now) && any_down_now) {
- // fixme: how do we identify a "clean" shutdown anyway?
- ldpp_dout(dpp, 10) << "build_prior possibly went active+rw, insufficient up;"
- << " including down osds" << dendl;
- for (vector<int>::const_iterator i = interval.acting.begin();
- i != interval.acting.end();
- ++i) {
- if (osdmap.exists(*i) && // if it doesn't exist, we already consider it lost.
- osdmap.is_down(*i)) {
- pg_down = true;
-
- // make note of when any down osd in the cur set was lost, so that
- // we can notice changes in prior_set_affected.
- blocked_by[*i] = osdmap.get_info(*i).lost_at;
+ // if not enough osds survived this interval, and we may have gone rw,
+ // then we need to wait for one of those osds to recover to
+ // ensure that we haven't lost any information.
+ if (!(*pcontdec)(up_now) && any_down_now) {
+ // fixme: how do we identify a "clean" shutdown anyway?
+ ldpp_dout(dpp, 10) << "build_prior possibly went active+rw, insufficient up;"
+ << " including down osds" << dendl;
+ for (auto &&so: acting) {
+ if (osdmap.exists(so.osd) && // if it doesn't exist, we already consider it lost.
+ osdmap.is_down(so.osd)) {
+ pg_down = true;
+
+ // make note of when any down osd in the cur set was lost, so that
+ // we can notice changes in prior_set_affected.
+ blocked_by[so.osd] = osdmap.get_info(so.osd).lost_at;
+ }
}
}
- }
- }
+ });
ldpp_dout(dpp, 10) << "build_prior final: probe " << probe
<< " down " << down
void dump(Formatter *f) const;
static void generate_test_instances(list<pg_interval_t*>& o);
};
+
+ PastIntervals() = default;
+ PastIntervals(bool ec_pool, const OSDMap &osdmap) : PastIntervals() {
+ update_type_from_map(ec_pool, osdmap);
+ }
+ PastIntervals(bool ec_pool, bool compact) : PastIntervals() {
+ update_type(ec_pool, compact);
+ }
+ PastIntervals(PastIntervals &&rhs) = default;
+ PastIntervals &operator=(PastIntervals &&rhs) = default;
+
+ PastIntervals(const PastIntervals &rhs);
+ PastIntervals &operator=(const PastIntervals &rhs);
+
+ class interval_rep {
+ public:
+ virtual size_t size() const = 0;
+ virtual bool empty() const = 0;
+ virtual void clear() = 0;
+ virtual pair<pair<epoch_t, epoch_t>, epoch_t> get_bounds() const = 0;
+ virtual set<pg_shard_t> get_might_have_unfound(
+ pg_shard_t pg_whoami,
+ bool ec_pool) const = 0;
+ virtual void add_interval(bool ec_pool, const pg_interval_t &interval) = 0;
+ virtual unique_ptr<interval_rep> clone() const = 0;
+ virtual ostream &print(ostream &out) const = 0;
+ virtual void encode(bufferlist &bl) const = 0;
+ virtual void decode(bufferlist::iterator &bl) = 0;
+ virtual void dump(Formatter *f) const = 0;
+ virtual bool is_classic() const = 0;
+ virtual void iterate_mayberw_back_to(
+ bool ec_pool,
+ epoch_t les,
+ std::function<void(epoch_t, const set<pg_shard_t> &)> &&f) const = 0;
+ virtual ~interval_rep() {}
+ };
+ friend class pi_simple_rep;
+ friend class pi_compact_rep;
private:
- map<epoch_t, pg_interval_t> past_intervals;
+
+ unique_ptr<interval_rep> past_intervals;
public:
- void encode(bufferlist &bl) const;
+ bool is_classic() const {
+ assert(past_intervals);
+ return past_intervals->is_classic();
+ }
+
+ void encode(bufferlist &bl) const {
+ assert(past_intervals);
+ ENCODE_START(1, 1, bl);
+ __u8 classic = is_classic();
+ ::encode(classic, bl);
+ past_intervals->encode(bl);
+ ENCODE_FINISH(bl);
+ }
+ void encode_classic(bufferlist &bl) const {
+ assert(past_intervals);
+ assert(past_intervals->is_classic());
+ past_intervals->encode(bl);
+ }
+
void decode(bufferlist::iterator &bl);
- void dump(Formatter *f) const;
+ void decode_classic(bufferlist::iterator &bl);
+
+ void dump(Formatter *f) const {
+ assert(past_intervals);
+ past_intervals->dump(f);
+ }
static void generate_test_instances(list<PastIntervals *> & o);
/**
friend ostream& operator<<(ostream& out, const PastIntervals &i);
template <typename F>
- void trim(epoch_t bound, F &&f) {
- PastIntervals::iterator pif = past_intervals.begin();
- PastIntervals::iterator end = past_intervals.end();
- while (pif != end) {
- if (pif->second.last >= bound)
- return;
- f(pif->second);
- past_intervals.erase(pif++);
- }
+ void iterate_mayberw_back_to(
+ bool ec_pool,
+ epoch_t les,
+ F &&f) const {
+ assert(past_intervals);
+ past_intervals->iterate_mayberw_back_to(ec_pool, les, std::forward<F>(f));
+ }
+ void clear() {
+ assert(past_intervals);
+ past_intervals->clear();
}
- void clear() { past_intervals.clear(); }
/**
* Should return a value which gives an indication of the amount
* of state contained
*/
- size_t size() const { return past_intervals.size(); }
+ size_t size() const {
+ assert(past_intervals);
+ return past_intervals->size();
+ }
- bool empty() const { return past_intervals.empty(); }
+ bool empty() const {
+ assert(past_intervals);
+ return past_intervals->empty();
+ }
void swap(PastIntervals &other) {
::swap(other.past_intervals, past_intervals);
*/
set<pg_shard_t> get_might_have_unfound(
pg_shard_t pg_whoami,
- bool ec_pool) const;
-
- bool has_crashed_interval_since(
- epoch_t since,
- const vector<int> &acting,
- const vector<int> &up,
- const OSDMap& osdmap,
- const DoutPrefixProvider *dpp) const;
+ bool ec_pool) const {
+ assert(past_intervals);
+ return past_intervals->get_might_have_unfound(pg_whoami, ec_pool);
+ }
/* Return the set of epochs
* [(start_interval_start, start_interval_end), end) represented by the
* the first interval so a user can verify that last_epoch_started falls
* within it */
pair<pair<epoch_t, epoch_t>, epoch_t> get_bounds() const {
- auto iter = past_intervals.begin();
- if (iter != past_intervals.end()) {
- auto riter = past_intervals.rbegin();
- return make_pair(
- make_pair(iter->second.first, iter->second.last + 1),
- riter->second.last + 1);
- } else {
- assert(0 == "get_bounds only valid if !empty()");
- return make_pair(make_pair(0, 0), 0);
- }
+ assert(past_intervals);
+ return past_intervals->get_bounds();
}
struct PriorSet {
private:
PriorSet(
+ const PastIntervals &past_intervals,
bool ec_pool,
IsPGRecoverablePredicate *c,
const OSDMap &osdmap,
- const PastIntervals &past_intervals,
const vector<int> &up,
const vector<int> &acting,
const pg_info_t &info,
friend class PastIntervals;
};
+ void update_type_from_map(const OSDMap &osdmap);
+
template <typename... Args>
PriorSet get_prior_set(Args&&... args) const {
- return PriorSet(std::forward<Args>(args)...);
+ return PriorSet(*this, std::forward<Args>(args)...);
}
-
-private:
- using iterator = map<epoch_t, pg_interval_t>::iterator;
- using const_iterator = map<epoch_t, pg_interval_t>::const_iterator;
- using reverse_iterator = map<epoch_t, pg_interval_t>::reverse_iterator;
- using const_reverse_iterator =
- map<epoch_t, pg_interval_t>::const_reverse_iterator;
- iterator begin() { return past_intervals.begin(); }
- iterator end() { return past_intervals.end(); }
- const_iterator begin() const { return past_intervals.begin(); }
- const_iterator end() const { return past_intervals.end(); }
- reverse_iterator rbegin() { return past_intervals.rbegin(); }
- reverse_iterator rend() { return past_intervals.rend(); }
- const_reverse_iterator rbegin() const { return past_intervals.rbegin(); }
- const_reverse_iterator rend() const { return past_intervals.rend(); }
};
WRITE_CLASS_ENCODER(PastIntervals)