@@ -398,6 +398,108 @@ void HSHomeObject::on_pg_complete_replace_member(group_id_t group_id, const std:
398398 boost::uuids::to_string (member_out.id ), boost::uuids::to_string (member_in.id ), tid);
399399}
400400
401+ // This function actually perform rollback for replace member task: Remove in_member, and ensure out_member exists
402+ void HSHomeObject::on_pg_clean_replace_member_task (group_id_t group_id, const std::string& task_id,
403+ const replica_member_info& member_out,
404+ const replica_member_info& member_in, trace_id_t tid) {
405+ std::unique_lock lck (_pg_lock);
406+ for (const auto & iter : _pg_map) {
407+ auto & pg = iter.second ;
408+ if (pg_repl_dev (*pg).group_id () == group_id) {
409+ auto hs_pg = static_cast < HSHomeObject::HS_PG* >(pg.get ());
410+
411+ // Remove the in_member (the one that was added but now needs to be removed)
412+ auto removed_count = pg->pg_info_ .members .erase (PGMember (member_in.id ));
413+
414+ // Ensure out_member exists
415+ // Using emplace is safe - it won't overwrite if already exists
416+ auto out_pg_member = to_pg_member (member_out);
417+ auto [it, inserted] = pg->pg_info_ .members .emplace (std::move (out_pg_member));
418+
419+ // Update superblock
420+ hs_pg->update_membership (pg->pg_info_ .members );
421+
422+ LOGI (" PG clean replace member task done (rollback), task_id={}, removed in_member={} (removed={}), "
423+ " ensured out_member={} (inserted={}), member_nums={}, trace_id={}" ,
424+ task_id, boost::uuids::to_string (member_in.id ), removed_count,
425+ boost::uuids::to_string (member_out.id ), inserted, pg->pg_info_ .members .size (), tid);
426+ return ;
427+ }
428+ }
429+ LOGE (" PG clean replace member task failed, pg not found, task_id={}, member_out={}, member_in={}, trace_id={}" , task_id,
430+ boost::uuids::to_string (member_out.id ), boost::uuids::to_string (member_in.id ), tid);
431+ }
432+
433+ bool HSHomeObject::reconcile_membership (pg_id_t pg_id) {
434+ std::unique_lock lck (_pg_lock);
435+ auto hs_pg = const_cast < HS_PG* >(_get_hs_pg_unlocked (pg_id));
436+ if (!hs_pg) {
437+ LOGE (" PG {} not found for reconcile_membership" , pg_id);
438+ return false ;
439+ }
440+
441+ // Get actual members from replication layer
442+ auto & repl_dev = hs_pg->repl_dev_ ;
443+ auto actual_members = repl_dev->get_replication_quorum ();
444+ if (actual_members.empty ()) {
445+ LOGW (" Failed to get replication quorum for PG {}, repl_dev may not be ready" , pg_id);
446+ return false ;
447+ }
448+
449+ // Build new member set from actual members
450+ std::set< PGMember > new_members;
451+ for (auto & member_id : actual_members) {
452+ auto existing = hs_pg->pg_info_ .members .find (PGMember (member_id));
453+ if (existing != hs_pg->pg_info_ .members .end ()) {
454+ // Keep the existing member with its name and priority
455+ new_members.insert (*existing);
456+ } else {
457+ // New member not in our records, add with default name
458+ PGMember new_member (member_id);
459+ new_members.insert (std::move (new_member));
460+ LOGE (" Adding new member {} to pg={} membership, should not happen!" , boost::uuids::to_string (member_id), hs_pg->pg_info_ .id );
461+ }
462+ }
463+ // Check if membership changed
464+ if (new_members == hs_pg->pg_info_ .members ) {
465+ LOGD (" Membership already in sync for pg={}, no update needed" , hs_pg->pg_info_ .id );
466+ return true ;
467+ }
468+
469+ // Log the differences
470+ std::vector< peer_id_t > removed_members;
471+ std::vector< peer_id_t > added_members;
472+
473+ for (auto & old_member : hs_pg->pg_info_ .members ) {
474+ if (new_members.find (old_member) == new_members.end ()) {
475+ removed_members.push_back (old_member.id );
476+ }
477+ }
478+
479+ for (auto & new_member : new_members) {
480+ if (hs_pg->pg_info_ .members .find (new_member) == hs_pg->pg_info_ .members .end ()) {
481+ added_members.push_back (new_member.id );
482+ }
483+ }
484+
485+ LOGI (" Reconciling PG {} membership: removing {} members, adding {} members, old membership count {}, new membership count: {}" ,
486+ pg_id, removed_members.size (), added_members.size (), hs_pg->pg_info_ .members .size (), new_members.size ());
487+
488+ for (auto & member_id : removed_members) {
489+ LOGI (" - Removing member: {}" , boost::uuids::to_string (member_id));
490+ }
491+ for (auto & member_id : added_members) {
492+ LOGI (" - Adding member: {}" , boost::uuids::to_string (member_id));
493+ }
494+
495+ // Update membership
496+ hs_pg->pg_info_ .members = std::move (new_members);
497+ hs_pg->update_membership (hs_pg->pg_info_ .members );
498+
499+ LOGI (" Successfully reconciled PG {} membership, new member_count={}" , pg_id, hs_pg->pg_info_ .members .size ());
500+ return true ;
501+ }
502+
401503void HSHomeObject::on_remove_member (homestore::group_id_t group_id, const peer_id_t & member, trace_id_t tid) {
402504 LOGINFO (" PG remove member, member={}, trace_id={}" , boost::uuids::to_string (member), tid);
403505 std::unique_lock lck (_pg_lock);
0 commit comments