]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
rbd-mirror: reuse the ImageReplayers in the GroupReplayer
authorN Balachandran <nithya.balachandran@ibm.com>
Tue, 11 Mar 2025 10:19:16 +0000 (15:49 +0530)
committerIlya Dryomov <idryomov@gmail.com>
Sun, 28 Sep 2025 18:25:02 +0000 (20:25 +0200)
This fix will start image replayers even if the group replayer
is primary so as to have the correctmirror pool status.
The group replayer will also attempt to reuse the image replayers where
possible on restart.

Signed-off-by: N Balachandran <nithya.balachandran@ibm.com>
qa/workunits/rbd/rbd_mirror_group.sh
qa/workunits/rbd/rbd_mirror_group_simple.sh
qa/workunits/rbd/rbd_mirror_helpers.sh
src/tools/rbd_mirror/GroupReplayer.cc
src/tools/rbd_mirror/GroupReplayer.h
src/tools/rbd_mirror/group_replayer/BootstrapRequest.cc
src/tools/rbd_mirror/group_replayer/Replayer.cc

index cbe26436004dfaa20be39e59088ea6acfc5337ee..439191d7e893afcd7b113e041f933f2cc7367631 100755 (executable)
@@ -183,8 +183,8 @@ if [ -z "${RBD_MIRROR_USE_RBD_MIRROR}" ]; then
   testlog "TEST: stop/start/restart group via admin socket"
 
   admin_daemons ${CLUSTER1} rbd mirror group stop ${POOL}/${group1}
-  wait_for_group_replay_stopped ${CLUSTER1} ${POOL}/${group1}
-  wait_for_group_status_in_pool_dir ${CLUSTER1} ${POOL}/${group1} 'up+stopped' 0
+  wait_for_group_replay_stopped ${CLUSTER1} ${POOL}/${group1} 1
+  wait_for_group_status_in_pool_dir ${CLUSTER1} ${POOL}/${group1} 'up+stopped' 1
 
   admin_daemons ${CLUSTER1} rbd mirror group start ${POOL}/${group1}
   wait_for_group_replay_started ${CLUSTER1} ${POOL}/${group1} 1
@@ -257,7 +257,7 @@ start_mirrors ${CLUSTER2}
 testlog " - demote and promote same cluster"
 mirror_group_demote ${CLUSTER2} ${POOL}/${group1}
 test_fields_in_group_info ${CLUSTER2} ${POOL}/${group1} 'snapshot' 'enabled' 'false'
-wait_for_group_replay_stopped ${CLUSTER1} ${POOL}/${group1}
+wait_for_group_replay_stopped ${CLUSTER1} ${POOL}/${group1} 0
 
 wait_for_group_status_in_pool_dir ${CLUSTER1} ${POOL}/${group1} 'up+unknown' 0
 wait_for_group_status_in_pool_dir ${CLUSTER2} ${POOL}/${group1} 'up+unknown' 0
@@ -273,7 +273,7 @@ compare_images ${CLUSTER1} ${CLUSTER2} ${POOL} ${POOL} ${image1}
 testlog " - failover (unmodified)"
 mirror_group_demote ${CLUSTER2} ${POOL}/${group}
 test_fields_in_group_info ${CLUSTER2} ${POOL}/${group} 'snapshot' 'enabled' 'false'
-wait_for_group_replay_stopped ${CLUSTER1} ${POOL}/${group}
+wait_for_group_replay_stopped ${CLUSTER1} ${POOL}/${group} 0
 wait_for_group_status_in_pool_dir ${CLUSTER1} ${POOL}/${group} 'up+unknown' 0
 wait_for_group_status_in_pool_dir ${CLUSTER2} ${POOL}/${group} 'up+unknown' 0
 mirror_group_promote ${CLUSTER1} ${POOL}/${group}
@@ -283,7 +283,7 @@ wait_for_group_replay_started ${CLUSTER2} ${POOL}/${group} 1
 testlog " - failback (unmodified)"
 mirror_group_demote ${CLUSTER1} ${POOL}/${group}
 test_fields_in_group_info ${CLUSTER1} ${POOL}/${group} 'snapshot' 'enabled' 'false'
-wait_for_group_replay_stopped ${CLUSTER2} ${POOL}/${group}
+wait_for_group_replay_stopped ${CLUSTER2} ${POOL}/${group} 0
 wait_for_group_status_in_pool_dir ${CLUSTER1} ${POOL}/${group} 'up+unknown' 0
 wait_for_group_status_in_pool_dir ${CLUSTER2} ${POOL}/${group} 'up+unknown' 0
 mirror_group_promote ${CLUSTER2} ${POOL}/${group}
@@ -291,13 +291,13 @@ test_fields_in_group_info ${CLUSTER2} ${POOL}/${group} 'snapshot' 'enabled' 'tru
 wait_for_group_replay_started ${CLUSTER1} ${POOL}/${group} 1
 mirror_group_snapshot_and_wait_for_sync_complete ${CLUSTER1} ${CLUSTER2} ${POOL}/${group}
 wait_for_group_status_in_pool_dir ${CLUSTER1} ${POOL}/${group} 'up+replaying' 1
-wait_for_group_status_in_pool_dir ${CLUSTER2} ${POOL}/${group} 'up+stopped' 0
+wait_for_group_status_in_pool_dir ${CLUSTER2} ${POOL}/${group} 'up+stopped' 1
 compare_images ${CLUSTER1} ${CLUSTER2} ${POOL} ${POOL} ${image}
 
 testlog " - failover"
 mirror_group_demote ${CLUSTER2} ${POOL}/${group1}
 test_fields_in_group_info ${CLUSTER2} ${POOL}/${group1} 'snapshot' 'enabled' 'false'
-wait_for_group_replay_stopped ${CLUSTER1} ${POOL}/${group1}
+wait_for_group_replay_stopped ${CLUSTER1} ${POOL}/${group1} 0
 wait_for_group_status_in_pool_dir ${CLUSTER1} ${POOL}/${group1} 'up+unknown' 0
 wait_for_group_status_in_pool_dir ${CLUSTER2} ${POOL}/${group1} 'up+unknown' 0
 mirror_group_promote ${CLUSTER1} ${POOL}/${group1}
@@ -305,14 +305,14 @@ test_fields_in_group_info ${CLUSTER1} ${POOL}/${group1} 'snapshot' 'enabled' 'tr
 wait_for_group_replay_started ${CLUSTER2} ${POOL}/${group1} 1
 write_image ${CLUSTER1} ${POOL} ${image1} 100
 mirror_group_snapshot_and_wait_for_sync_complete ${CLUSTER2} ${CLUSTER1} ${POOL}/${group1}
-wait_for_group_status_in_pool_dir ${CLUSTER1} ${POOL}/${group1} 'up+stopped' 0
+wait_for_group_status_in_pool_dir ${CLUSTER1} ${POOL}/${group1} 'up+stopped' 1
 wait_for_group_status_in_pool_dir ${CLUSTER2} ${POOL}/${group1} 'up+replaying' 1
 compare_images ${CLUSTER1} ${CLUSTER2} ${POOL} ${POOL} ${image1}
 
 testlog " - failback to cluster2"
 mirror_group_demote ${CLUSTER1} ${POOL}/${group1}
 test_fields_in_group_info ${CLUSTER1} ${POOL}/${group1} 'snapshot' 'enabled' 'false'
-wait_for_group_replay_stopped ${CLUSTER2} ${POOL}/${group1}
+wait_for_group_replay_stopped ${CLUSTER2} ${POOL}/${group1} 0
 wait_for_group_status_in_pool_dir ${CLUSTER1} ${POOL}/${group1} 'up+unknown' 0
 wait_for_group_status_in_pool_dir ${CLUSTER2} ${POOL}/${group1} 'up+unknown' 0
 mirror_group_promote ${CLUSTER2} ${POOL}/${group1}
@@ -321,7 +321,7 @@ wait_for_group_replay_started ${CLUSTER1} ${POOL}/${group1} 1
 write_image ${CLUSTER2} ${POOL} ${image1} 100
 mirror_group_snapshot_and_wait_for_sync_complete ${CLUSTER1} ${CLUSTER2} ${POOL}/${group1}
 wait_for_group_status_in_pool_dir ${CLUSTER1} ${POOL}/${group1} 'up+replaying' 1
-wait_for_group_status_in_pool_dir ${CLUSTER2} ${POOL}/${group1} 'up+stopped' 0
+wait_for_group_status_in_pool_dir ${CLUSTER2} ${POOL}/${group1} 'up+stopped' 1
 compare_images ${CLUSTER1} ${CLUSTER2} ${POOL} ${POOL} ${image1}
 
 testlog " - force promote cluster1"
@@ -332,10 +332,10 @@ mirror_group_promote ${CLUSTER1} ${POOL}/${group} '--force'
 test_fields_in_group_info ${CLUSTER1} ${POOL}/${group} 'snapshot' 'enabled' 'true'
 test_fields_in_group_info ${CLUSTER2} ${POOL}/${group} 'snapshot' 'enabled' 'true'
 
-wait_for_group_replay_stopped ${CLUSTER1} ${POOL}/${group}
-wait_for_group_replay_stopped ${CLUSTER2} ${POOL}/${group}
-wait_for_group_status_in_pool_dir ${CLUSTER1} ${POOL}/${group} 'up+stopped' 0
-wait_for_group_status_in_pool_dir ${CLUSTER2} ${POOL}/${group} 'up+stopped' 0
+wait_for_group_replay_stopped ${CLUSTER1} ${POOL}/${group} 1
+wait_for_group_replay_stopped ${CLUSTER2} ${POOL}/${group} 1
+wait_for_group_status_in_pool_dir ${CLUSTER1} ${POOL}/${group} 'up+stopped' 1
+wait_for_group_status_in_pool_dir ${CLUSTER2} ${POOL}/${group} 'up+stopped' 1
 write_image ${CLUSTER1} ${POOL} ${image} 100
 write_image ${CLUSTER2} ${POOL} ${image} 100
 wait_for_group_present ${CLUSTER1} ${POOL} ${group} 1
@@ -477,7 +477,7 @@ testlog "TEST: split-brain"
 mirror_group_promote ${CLUSTER1} ${POOL}/${group} --force
 test_fields_in_group_info ${CLUSTER1} ${POOL}/${group} 'snapshot' 'enabled' 'true'
 test_fields_in_group_info ${CLUSTER2} ${POOL}/${group} 'snapshot' 'enabled' 'true'
-wait_for_group_status_in_pool_dir ${CLUSTER1} ${POOL}/${group} 'up+stopped' 0
+wait_for_group_status_in_pool_dir ${CLUSTER1} ${POOL}/${group} 'up+stopped' 1
 write_image ${CLUSTER1} ${POOL} ${image} 10
 mirror_group_demote ${CLUSTER1} ${POOL}/${group}
 test_fields_in_group_info ${CLUSTER1} ${POOL}/${group} 'snapshot' 'enabled' 'false'
index e011ce6f8f1ea0b073e86bbdd082f089a060d816..aada13a3494cf0f4f3ed4721ee41df77721d399b 100755 (executable)
@@ -2198,7 +2198,7 @@ test_force_promote_delete_group()
   wait_for_group_status_in_pool_dir "${secondary_cluster}" "${pool}"/"${group0}" 'up+replaying' "${image_count}"
 
   if [ -z "${RBD_MIRROR_USE_RBD_MIRROR}" ]; then
-    wait_for_group_status_in_pool_dir "${primary_cluster}" "${pool}"/"${group0}" 'up+stopped' 0
+    wait_for_group_status_in_pool_dir "${primary_cluster}" "${pool}"/"${group0}" 'up+stopped' "${image_count}" 
   fi
 
   wait_for_group_synced "${primary_cluster}" "${pool}"/"${group0}"
@@ -2208,8 +2208,8 @@ test_force_promote_delete_group()
   mirror_group_promote "${secondary_cluster}" "${pool}/${group0}" '--force'
   wait_for_group_replay_stopped ${secondary_cluster} ${pool}/${group0}
   wait_for_group_replay_stopped ${primary_cluster} ${pool}/${group0}
-  wait_for_group_status_in_pool_dir ${secondary_cluster} ${pool}/${group0} 'up+stopped' 0
-  wait_for_group_status_in_pool_dir ${primary_cluster} ${pool}/${group0} 'up+stopped' 0
+  wait_for_group_status_in_pool_dir ${secondary_cluster} ${pool}/${group0} 'up+stopped' "${image_count}"
+  wait_for_group_status_in_pool_dir ${primary_cluster} ${pool}/${group0} 'up+stopped' "${image_count}"
 
   wait_for_group_present "${primary_cluster}" "${pool}" "${group0}" "${image_count}"
   wait_for_group_present "${secondary_cluster}" "${pool}" "${group0}" "${image_count}"
@@ -2220,21 +2220,21 @@ test_force_promote_delete_group()
   wait_for_group_present "${secondary_cluster}" "${pool}" "${group0}" "${image_count}"
 
   # group still exists on original primary
-  wait_for_group_status_in_pool_dir ${primary_cluster} ${pool}/${group0} 'up+stopped' 0
+  wait_for_group_status_in_pool_dir ${primary_cluster} ${pool}/${group0} 'up+stopped' "${image_count}"
 
   group_image_remove ${secondary_cluster} ${pool}/${group0} ${pool}/${image_prefix}0
 
   wait_for_group_present "${primary_cluster}" "${pool}" "${group0}" "${image_count}"
   wait_for_group_present "${secondary_cluster}" "${pool}" "${group0}" $(("${image_count}"-1))
   test_fields_in_group_info ${primary_cluster} ${pool}/${group0} 'snapshot' 'enabled' 'true'
-  wait_for_group_status_in_pool_dir ${primary_cluster} ${pool}/${group0} 'up+stopped' 0
+  wait_for_group_status_in_pool_dir ${primary_cluster} ${pool}/${group0} 'up+stopped' "${image_count}"
 
   mirror_group_enable "${secondary_cluster}" "${pool}/${group0}"
   test_fields_in_group_info ${primary_cluster} ${pool}/${group0} 'snapshot' 'enabled' 'true'
   test_fields_in_group_info ${secondary_cluster} ${pool}/${group0} 'snapshot' 'enabled' 'true'
 
-  wait_for_group_status_in_pool_dir ${primary_cluster} ${pool}/${group0} 'up+stopped' 0
-  wait_for_group_status_in_pool_dir ${secondary_cluster} ${pool}/${group0} 'up+stopped' 0
+  wait_for_group_status_in_pool_dir ${primary_cluster} ${pool}/${group0} 'up+stopped' "${image_count}"
+  wait_for_group_status_in_pool_dir ${secondary_cluster} ${pool}/${group0} 'up+stopped' $(("${image_count}"-1))
 
   # TODO - test normally fails on next line with missing images
   wait_for_group_present "${secondary_cluster}" "${pool}" "${group0}" $(("${image_count}"-1))
index 185c274ec8c91df73b45506a60b3234d7f5dfd07..211696a3c055f02ac81d95cd26323fb3295da8f2 100755 (executable)
@@ -2591,7 +2591,7 @@ wait_for_group_replay_state()
     for s in 0.1 1 2 4 8 8 8 8 8 8 8 8 16 16; do
         sleep ${s}
         if [ 'true' = "${asok_query}" ]; then
-            test_group_replay_state "${cluster}" "${group_spec}" "${state}" "${image_count}" && return 0
+            test_group_replay_state "${cluster}" "${group_spec}" "${state}" && return 0
         else
             test_group_replay_state_cli "${cluster}" "${group_spec}" "${state}" "${image_count}" && return 0
         fi
@@ -2615,11 +2615,13 @@ wait_for_group_replay_stopped()
 {
     local cluster=$1
     local group_spec=$2
+    local image_count=$3
 
-    # Image_count will be 0 if the group is stopped
-    # Query the state via daemon socket and also via the cli to confirm that they agree
+    # Image_count will be 0 if the group is stopped except when the group is primary
+    # Query the state via daemon socket and also via the cli.
+    # The admin socket status does not include image information
     wait_for_group_replay_state "${cluster}" "${group_spec}" 'stopped' 0 'true'
-    wait_for_group_replay_state "${cluster}" "${group_spec}" 'stopped' 0 'false'
+    wait_for_group_replay_state "${cluster}" "${group_spec}" 'stopped' "${image_count}" 'false'
 }
 
 get_newest_group_snapshot_id()
index 8dace7ce7584f19f8ad80aa75e07b5fe615e552e..59d2e60482e1e54094dfa837dc363ce36aecfa98 100644 (file)
@@ -219,10 +219,24 @@ GroupReplayer<I>::~GroupReplayer() {
   unregister_admin_socket_hook();
   ceph_assert(m_on_start_finish == nullptr);
   ceph_assert(m_bootstrap_request == nullptr);
-  ceph_assert(m_bootstrap_request == nullptr);
   ceph_assert(m_replayer == nullptr);
   ceph_assert(m_image_replayers.empty());
   ceph_assert(m_on_stop_contexts.empty());
+  ceph_assert(m_replayer_check_task == nullptr);
+}
+
+template <typename I>
+void GroupReplayer<I>::destroy() {
+  {
+    std::lock_guard locker{m_lock};
+
+    for (auto &it : m_image_replayers) {
+      ceph_assert(it.second->is_stopped());
+      it.second->destroy();
+    }
+    m_image_replayers.clear();
+  }
+  delete this;
 }
 
 template <typename I>
@@ -321,9 +335,13 @@ void GroupReplayer<I>::start(Context *on_finish, bool manual, bool restart) {
       m_last_r = 0;
       m_state_desc.clear();
       m_local_group_snaps.clear();
+      ceph_assert(m_replayer_check_task == nullptr);
       ceph_assert(m_replayer == nullptr);
-      ceph_assert(m_image_replayers.empty());
-      m_image_replayers.clear();
+      if (m_destroy_replayers) {
+        ceph_assert(m_image_replayers.empty());
+      }
+      m_destroy_replayers = false;
+  //  FIXME: replayer index is not used
       m_image_replayer_index.clear();
       m_manual_stop = false;
       m_finished = false;
@@ -463,12 +481,13 @@ void GroupReplayer<I>::print_status(Formatter *f) {
     state = STATE_STOPPED;
   }
   f->dump_string("state", state_to_string(state));
-// TODO: remove the image_replayers section
+/*
   f->open_array_section("image_replayers");
   for (auto &[_, image_replayer] : m_image_replayers) {
     image_replayer->print_status(f);
   }
   f->close_section(); // image_replayers
+*/
   f->close_section(); // group_replayer
 }
 
@@ -489,6 +508,7 @@ void GroupReplayer<I>::on_stop_replay(int r, const std::string &desc)
     m_state_desc = "";
   }
 
+  cancel_image_replayers_check();
   shut_down(r);
 }
 
@@ -514,7 +534,6 @@ void GroupReplayer<I>::bootstrap_group() {
   }
 
   ceph_assert(m_replayer == nullptr);
-  ceph_assert(m_image_replayers.empty());
 
   auto ctx = create_context_callback<
       GroupReplayer,
@@ -561,12 +580,17 @@ void GroupReplayer<I>::handle_bootstrap_group(int r) {
     reregister_admin_socket_hook();
   }
 
+//FIXME : Relook at this once the Bootstrap no longer reuses values.
+// Since a rerun may end up with variables populated by a previous run,
+// it is safer to destroy the image_replayers now.
+  m_destroy_replayers = true;
   if (finish_start_if_interrupted()) {
     return;
   } else if (r == -ENOENT) {
     finish_start_fail(r, "group removed");
     return;
   } else if (r == -EREMOTEIO) {
+    m_destroy_replayers = true;
     finish_start_fail(r, "remote group is non-primary");
     return;
   } else if (r == -EEXIST) {
@@ -577,21 +601,23 @@ void GroupReplayer<I>::handle_bootstrap_group(int r) {
     return;
   }
 
+  // Start the image replayers in case the group is primary
+  // in order to have the mirror pool health status set to ok.
+  m_destroy_replayers = false;
+
+/*
   if (m_local_group_ctx.primary) { // XXXMG
     set_mirror_group_status_update(
        cls::rbd::MIRROR_GROUP_STATUS_STATE_STOPPED,
        "local group is primary");
     finish_start_fail(0, "local group is primary");
     return;
-  } else if (m_remote_group_id.empty()) { // m_remote_group_id matter for
-                                          // secondary cluster case.
-    finish_start_fail(-EINVAL, "remote is not ready yet");
-    return;
   }
-
+*/
   m_local_group_ctx.listener = &m_listener;
 
-  create_group_replayer();
+  // Start the image replayers first
+  start_image_replayers();
 }
 
 template <typename I>
@@ -623,7 +649,23 @@ void GroupReplayer<I>::handle_create_group_replayer(int r) {
     return;
   }
 
-  start_image_replayers();
+  Context *on_finish = nullptr;
+  {
+    std::unique_lock locker{m_lock};
+    ceph_assert(m_state == STATE_STARTING);
+    m_state = STATE_REPLAYING;
+    std::swap(m_on_start_finish, on_finish);
+
+    std::unique_lock timer_locker{m_threads->timer_lock};
+    schedule_image_replayers_check();
+  }
+
+  set_mirror_group_status_update(
+     cls::rbd::MIRROR_GROUP_STATUS_STATE_REPLAYING, "replaying");
+
+  if (on_finish) {
+    on_finish->complete(0);
+  }
 }
 
 // TODO: Move this to group_replayer::Replayer?
@@ -664,19 +706,90 @@ void GroupReplayer<I>::handle_start_image_replayers(int r) {
     return;
   }
 
-  Context *on_finish = nullptr;
+  create_group_replayer();
+}
+
+template <typename I>
+void GroupReplayer<I>::check_image_replayers_running() {
+  dout(10) << dendl;
+  bool stopped = false;
   {
-    std::unique_lock locker{m_lock};
-    ceph_assert(m_state == STATE_STARTING);
-    m_state = STATE_REPLAYING;
-    std::swap(m_on_start_finish, on_finish);
+    std::lock_guard locker{m_lock};
+    if (m_state == STATE_STOPPING || m_state == STATE_STOPPED) {
+      dout(10) << "stopping" <<dendl;
+      return;
+    }
+
+    for (auto &it : m_image_replayers) {
+      if (it.second->is_stopped()) {
+       dout(10) << "image replayer stopped for global_id : "
+                 << it.second->get_global_image_id() <<  dendl;
+        stopped = true;
+        break;
+      }
+    }
   }
 
-  set_mirror_group_status_update(
-     cls::rbd::MIRROR_GROUP_STATUS_STATE_REPLAYING, "replaying");
+  if (stopped) {
+    // shut down
+    dout(10) << " stopping group replayer" << dendl;
+    //TODO: determine the error
+    on_stop_replay();
+    return;
+  }
+}
 
-  if(on_finish) {
-    on_finish->complete(0);
+template <typename I>
+void GroupReplayer<I>::schedule_image_replayers_check() {
+  ceph_assert(ceph_mutex_is_locked_by_me(m_lock));
+  ceph_assert(ceph_mutex_is_locked_by_me(m_threads->timer_lock));
+
+  if (m_state != STATE_REPLAYING) {
+    return;
+  }
+
+  dout(10) << dendl;
+
+  ceph_assert(m_replayer_check_task == nullptr);
+  m_replayer_check_task = create_context_callback<
+    GroupReplayer<I>,
+    &GroupReplayer<I>::handle_image_replayers_check>(this);
+  m_threads->timer->add_event_after(10, m_replayer_check_task);
+}
+
+template <typename I>
+void GroupReplayer<I>::handle_image_replayers_check(int r) {
+  dout(10) << dendl;
+
+  ceph_assert(ceph_mutex_is_locked_by_me(m_threads->timer_lock));
+
+  ceph_assert(m_replayer_check_task != nullptr);
+  m_replayer_check_task = nullptr;
+
+  auto ctx = new LambdaContext([this](int) {
+    check_image_replayers_running();
+    {
+      std::unique_lock locker{m_lock};
+      std::unique_lock timer_locker{m_threads->timer_lock};
+
+      schedule_image_replayers_check();
+    }
+    m_in_flight_op_tracker.finish_op();
+  });
+
+  m_in_flight_op_tracker.start_op();
+  m_threads->work_queue->queue(ctx, 0);
+}
+
+template <typename I>
+void GroupReplayer<I>::cancel_image_replayers_check() {
+  std::unique_lock timer_locker{m_threads->timer_lock};
+  if (m_replayer_check_task != nullptr) {
+    dout(10) << dendl;
+
+    if (m_threads->timer->cancel_event(m_replayer_check_task)) {
+      m_replayer_check_task = nullptr;
+    }
   }
 }
 
@@ -739,6 +852,14 @@ void GroupReplayer<I>::shut_down(int r) {
     ceph_assert(m_state == STATE_STOPPING);
   }
 
+  if (!m_in_flight_op_tracker.empty()) {
+    dout(15) << "waiting for in-flight operations to complete" << dendl;
+    m_in_flight_op_tracker.wait_for_ops(new LambdaContext([this, r](int) {
+        shut_down(r);
+      }));
+    return;
+  }
+
   // chain the shut down sequence (reverse order)
   Context *ctx = new LambdaContext(
     [this, r](int _r) {
@@ -747,19 +868,20 @@ void GroupReplayer<I>::shut_down(int r) {
     });
 
   // stop and destroy the replayers
-  ctx = new LambdaContext([this, ctx](int r) {
-    {
-      std::lock_guard locker{m_lock};
+  if (m_destroy_replayers) {
+    ctx = new LambdaContext([this, ctx](int r) {
+      {
+       std::lock_guard locker{m_lock};
 
-      for (auto &it : m_image_replayers) {
-       ceph_assert(it.second->is_stopped());
-       it.second->destroy();
+       for (auto &it : m_image_replayers) {
+         ceph_assert(it.second->is_stopped());
+         it.second->destroy();
+       }
+       m_image_replayers.clear();
       }
-      m_image_replayers.clear();
-    }
-    ctx->complete(0);
-  });
-
+      ctx->complete(0);
+    });
+  }
   ctx = new LambdaContext([this, ctx](int r) {
     C_Gather *gather_ctx = new C_Gather(g_ceph_context, ctx);
     {
index 21cff6c0090d979d556acfef7cdddeae3dd45258..1d4ad73a2e8303e920a7867572dfc7b68d7397b6 100644 (file)
@@ -50,9 +50,7 @@ public:
                              threads, instance_watcher, local_status_updater,
                              cache_manager_handler, pool_meta_cache);
   }
-  void destroy() {
-    delete this;
-  }
+  void destroy();
 
   GroupReplayer(librados::IoCtx &local_io_ctx,
                 const std::string &local_mirror_uuid,
@@ -201,8 +199,9 @@ private:
   std::string m_local_group_id;
   std::string m_remote_group_id;
 
+  bool m_destroy_replayers = false;
+
   mutable ceph::mutex m_lock;
-  AsyncOpTracker m_async_op_tracker;
   State m_state = STATE_STOPPED;
   std::string m_state_desc;
   cls::rbd::MirrorGroupStatusState m_status_state;
@@ -222,6 +221,9 @@ private:
 
   AdminSocketHook *m_asok_hook = nullptr;
 
+  AsyncOpTracker m_in_flight_op_tracker;
+  Context* m_replayer_check_task = nullptr;
+
   group_replayer::BootstrapRequest<ImageCtxT> *m_bootstrap_request = nullptr;
   group_replayer::Replayer<ImageCtxT> *m_replayer = nullptr;
   std::list<std::pair<librados::IoCtx, ImageReplayer<ImageCtxT> *>> m_image_replayers;
@@ -267,6 +269,11 @@ private:
   void start_image_replayers();
   void handle_start_image_replayers(int r);
 
+  void check_image_replayers_running();
+  void schedule_image_replayers_check();
+  void handle_image_replayers_check(int r);
+  void cancel_image_replayers_check();
+
   bool finish_start_if_interrupted();
   bool finish_start_if_interrupted(ceph::mutex &lock);
   void finish_start_fail(int r, const std::string &desc);
index 23ade4c5ff0cbab8b0014d3a8afd7cef0d7ca50b..c6a8424f34381356aabcc21f46ce0e8fcf6793f6 100644 (file)
@@ -1364,6 +1364,12 @@ template <typename I>
 int BootstrapRequest<I>::create_replayers() {
   dout(10) << dendl;
 
+  //TODO: check that the images have not changed
+  if (!m_image_replayers->empty()) {
+    dout(10) << "image replayers already exist."<< dendl;
+    return 0;
+  }
+
   int r = 0;
   if (m_remote_mirror_group.state == cls::rbd::MIRROR_GROUP_STATE_ENABLED &&
       m_remote_mirror_group_primary) {
index 2b71f9c8ca2f42820a6920a6f2100ad93dec5197..4d3d250d78b9fa6642e5d1da6c83d1a49c9fa784 100644 (file)
@@ -321,8 +321,7 @@ void Replayer<I>::handle_load_local_group_snapshots(int r) {
     if (ns->state != cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY) {
       break;
     }
-    // this is primary, IDLE the group replayer
-    m_state = STATE_IDLE;
+    m_state = STATE_COMPLETE;
     notify_group_listener_stop();
     return;
   }