]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
rbd-mirror: ensure that the last non-primary snapshot cannot be pruned
authorJason Dillaman <dillaman@redhat.com>
Thu, 11 Feb 2021 20:45:01 +0000 (15:45 -0500)
committerJason Dillaman <dillaman@redhat.com>
Mon, 22 Feb 2021 13:17:33 +0000 (08:17 -0500)
Tweak the normal pruning behavior to ensure that an incomplete initial
non-primary snapshot is not included in the prune set since we know
it will be complete since otherwise the image would have been deleted
due to not updating the mirror-image-state to enabled. Also ensure
we cannot prune a non-primary mirror snapshot if we don't have a
predecessor.

Signed-off-by: Jason Dillaman <dillaman@redhat.com>
(cherry picked from commit ecd3778a6f9a6ca33aebbf47cef79db5f04157a9)

src/tools/rbd_mirror/image_replayer/snapshot/Replayer.cc

index 5be76aa0683ca2742238ec90a7facb10223d5290..bde5150847506236f662c251e5a82c0f546c71e7 100644 (file)
@@ -404,6 +404,7 @@ void Replayer<I>::scan_local_mirror_snapshots(
 
   std::set<uint64_t> prune_snap_ids;
 
+  bool completed_non_primary_snapshots_exist = false;
   auto local_image_ctx = m_state_builder->local_image_ctx;
   std::shared_lock image_locker{local_image_ctx->image_lock};
   for (auto snap_info_it = local_image_ctx->snap_info.begin();
@@ -425,26 +426,39 @@ void Replayer<I>::scan_local_mirror_snapshots(
         // if remote has new snapshots, we would sync from here
         m_local_snap_id_start = local_snap_id;
         m_local_snap_id_end = CEPH_NOSNAP;
+        completed_non_primary_snapshots_exist = true;
 
         if (mirror_ns->mirror_peer_uuids.empty()) {
           // no other peer will attempt to sync to this snapshot so store as
           // a candidate for removal
           prune_snap_ids.insert(local_snap_id);
         }
-      } else {
-        if (mirror_ns->last_copied_object_number == 0) {
-          // snapshot might be missing image state, object-map, etc, so just
-          // delete and re-create it if we haven't started copying data
-          // objects. Also only prune this snapshot since we will need the
-          // previous mirror snapshot for syncing.
-          prune_snap_ids.clear();
-          prune_snap_ids.insert(local_snap_id);
-          break;
+      } else if (mirror_ns->last_copied_object_number == 0 &&
+                 m_local_snap_id_start > 0) {
+        // shouldn't be possible, but ensure that pruning this snapshot
+        // wouldn't leave this image w/o any non-primary snapshots
+        if (!completed_non_primary_snapshots_exist) {
+          derr << "incomplete local non-primary snapshot" << dendl;
+          handle_replay_complete(locker, -EINVAL,
+                                 "incomplete local non-primary snapshot");
+          return;
         }
 
+        // snapshot might be missing image state, object-map, etc, so just
+        // delete and re-create it if we haven't started copying data
+        // objects. Also only prune this snapshot since we will need the
+        // previous mirror snapshot for syncing. Special case exception for
+        // the first non-primary snapshot since we know its snapshot is
+        // well-formed because otherwise the mirror-image-state would have
+        // forced an image deletion.
+        prune_snap_ids.clear();
+        prune_snap_ids.insert(local_snap_id);
+        break;
+      } else {
         // start snap will be last complete mirror snapshot or initial
         // image revision
         m_local_snap_id_end = local_snap_id;
+        break;
       }
     } else if (mirror_ns->is_primary()) {
       if (mirror_ns->complete) {