]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commit
mon: fix ConnectionTracker::notify_rank_removed 69497/head
authorKamoltat (Junior) Sirivadhna <ksirivad@redhat.com>
Tue, 2 Jun 2026 11:43:14 +0000 (11:43 +0000)
committerKamoltat (Junior) Sirivadhna <ksirivad@redhat.com>
Mon, 15 Jun 2026 22:14:56 +0000 (22:14 +0000)
commitc72f9a86b8833e3b2d159e1411ef68fbfbe8ed5c
tree267d9713956fc39a8bf0366fe82ce392cfeacba9
parentcf5b149eecfc0a1d2038d667b37d2133ac741c97
mon: fix ConnectionTracker::notify_rank_removed

Problem:
When multiple monitors are removed from the monmap in a single update,
the iterative rank adjustment in notify_rank_removed() fails to maintain
the invariant that our local rank matches the monmap's new_rank until
all iterations complete.

Problem:
The old code decremented rank for each removed rank:
```
  for (auto i = monmap->removed_ranks.rbegin(); ...) {
    int remove_rank = *i;
    int new_rank = monmap->get_rank(messenger->get_myaddrs());

    // Old logic (in ConnectionTracker):
    if (remove_rank < rank) {
      --rank;
    }

    // This assert would fail on first iteration when removed_ranks.size() > 1
    ceph_assert(rank == new_rank);
  }
```

When removed_ranks = {0, 1} and we are mon.d (originally rank 3):
- new_rank from monmap = 1 (final correct value after removing 0 and 1)

Iteration 1 (remove rank 1):
  Before: rank = 3
  Execute: rank_removed=1 < 3, so --rank -> rank = 2
  Check: rank == new_rank? -> 2 == 1? FAIL

Iteration 2 (remove rank 0):
  Before: rank = 2
  Execute: rank_removed=0 < 2, so --rank -> rank = 1
  Check: rank == new_rank? → 1 == 1? PASS

The invariant is violated during intermediate iterations because each
--rank adjustment only accounts for ONE removed rank, but new_rank
from monmap already accounts for ALL removed ranks.

Solution:
Trust the caller's new_rank from monmap instead of trying to compute it
iteratively. The monmap has already recalculated ranks correctly by
removing all monitors atomically, so we should just use that value.

Changed:
```
  if (rank_removed < rank) {
    --rank;
  }
```
To:
  // Trust the caller's new_rank from the monmap rather than trying to compute it.
  // This handles cases where multiple ranks are removed simultaneously.
  rank = new_rank;

Now the invariant holds on every iteration regardless of how many ranks
are removed.

Fixes: https://tracker.ceph.com/issues/77423
Signed-off-by: Kamoltat (Junior) Sirivadhna <ksirivad@redhat.com>
src/mon/ConnectionTracker.cc