]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
osd_types: generalize PastIntervals to support multiple representations
authorSamuel Just <sjust@redhat.com>
Fri, 20 Jan 2017 20:39:36 +0000 (12:39 -0800)
committerSage Weil <sage@redhat.com>
Fri, 28 Apr 2017 15:30:38 +0000 (11:30 -0400)
Signed-off-by: Samuel Just <sjust@redhat.com>
src/osd/OSD.cc
src/osd/PG.cc
src/osd/PG.h
src/osd/osd_types.cc
src/osd/osd_types.h

index 8e06a9b609dbce5d2b891652b8d071eb3516882c..42a346aa103bda97e1c74aa87a8c82fad8ec7406 100644 (file)
@@ -7954,7 +7954,9 @@ void OSD::handle_pg_create(OpRequestRef op)
     bool mapped = osdmap->get_primary_shard(on, &pgid);
     assert(mapped);
 
-    PastIntervals pi;
+    PastIntervals pi(
+      osdmap->get_pools().at(pgid.pool()).ec_pool(),
+      *osdmap);
     pg_history_t history;
     history.epoch_created = created;
     history.last_scrub_stamp = ci->second;
@@ -8554,7 +8556,9 @@ void OSD::handle_pg_query(OpRequestRef op)
            it->second.epoch_sent,
            osdmap->get_epoch(),
            empty),
-         PastIntervals()));
+         PastIntervals(
+           osdmap->get_pools().at(pgid.pool()).ec_pool(),
+           *osdmap)));
     }
   }
   do_notifies(notify_list, osdmap);
index 2a64601a00c9808160489a356515a2e14489ab23..051dc34c9e31958786c03fe813f7ab1451563cb5 100644 (file)
@@ -235,6 +235,9 @@ PG::PG(OSDService *o, OSDMapRef curmap,
   coll(p), pg_log(cct),
   pgmeta_oid(p.make_pgmeta_oid()),
   missing_loc(this),
+  past_intervals(
+    curmap->get_pools().at(p.pgid.pool()).ec_pool(),
+    *curmap),
   stat_queue_item(this),
   scrub_queued(false),
   recovery_queued(false),
@@ -762,21 +765,6 @@ void PG::check_past_interval_bounds() const
   }
 }
 
-/*
- * Trim past_intervals.
- *
- * This gets rid of all the past_intervals that happened before last_epoch_clean.
- */
-void PG::trim_past_intervals()
-{
-  past_intervals.trim(
-    info.history.last_epoch_clean,
-    [&](const PastIntervals::pg_interval_t &trimmed) {
-      dout(10) << __func__ << ": trimming " << trimmed << dendl;
-      dirty_big_info = true;
-    });
-}
-
 bool PG::adjust_need_up_thru(const OSDMapRef osdmap)
 {
   epoch_t up_thru = osdmap->get_up_thru(osd->whoami);
@@ -857,7 +845,6 @@ PastIntervals::PriorSet PG::build_prior()
     pool.info.ec_pool(),
     get_pgbackend()->get_is_recoverable_predicate(),
     *get_osdmap(),
-    past_intervals,
     up,
     acting,
     info,
@@ -1966,7 +1953,7 @@ void PG::mark_clean()
   // strays yet.
   info.history.last_epoch_clean = get_osdmap()->get_epoch();
 
-  trim_past_intervals();
+  past_intervals.clear();
 
   if (is_active()) {
     /* The check is needed because if we are below min_size we're not
@@ -5705,6 +5692,7 @@ void PG::handle_advance_map(
           << dendl;
   update_osdmap_ref(osdmap);
   pool.update(osdmap);
+  past_intervals.update_type_from_map(pool.info.ec_pool(), *osdmap);
   if (cct->_conf->osd_debug_verify_cached_snaps) {
     interval_set<snapid_t> actual_removed_snaps;
     const pg_pool_t *pi = osdmap->get_pg_pool(info.pgid.pool());
index 372ec4f5dbed070e9791d104e3d80798b813bce8..c61574b4e6bb254a02ee84a19f614fe896869c15 100644 (file)
@@ -923,8 +923,7 @@ public:
     }
   }
   void check_past_interval_bounds() const;
-  void trim_past_intervals();
-       PastIntervals::PriorSet build_prior();
+  PastIntervals::PriorSet build_prior();
 
   void remove_down_peer_info(const OSDMapRef osdmap);
 
index df19f9927f9d257b5d9292cb0eaaa674997dcc1e..9cec5d6330556b048686be864f429157ec044185 100644 (file)
@@ -2858,152 +2858,168 @@ void PastIntervals::pg_interval_t::generate_test_instances(list<pg_interval_t*>&
 }
 
 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(
@@ -3129,6 +3145,8 @@ bool PastIntervals::check_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,
@@ -3141,7 +3159,7 @@ bool PastIntervals::check_new_interval(
        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);
@@ -3210,6 +3228,7 @@ bool PastIntervals::check_new_interval(
       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;
@@ -3217,10 +3236,10 @@ bool PastIntervals::check_new_interval(
 }
 
 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,
@@ -3285,77 +3304,66 @@ PastIntervals::PriorSet::PriorSet(
       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
index c0ee2f6bd475cce6123649c925e4e1581ce9ecb3..938e9c07519608e34b5f294354476d77e6d98001 100644 (file)
@@ -2497,13 +2497,75 @@ public:
     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);
 
   /**
@@ -2571,25 +2633,31 @@ public:
   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);
@@ -2601,14 +2669,10 @@ public:
    */
   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
@@ -2617,16 +2681,8 @@ public:
    * 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 {
@@ -2651,10 +2707,10 @@ public:
 
   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,
@@ -2663,25 +2719,12 @@ public:
     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)