Jerry Chung created SOLR-18570:
----------------------------------

             Summary: Replica stays "zombie leader" (ZK says leader, local 
CloudDescriptor.isLeader() is false) after ZK SUSPENDED/RECONNECTED flaps; 
never self-heals
                 Key: SOLR-18570
                 URL: https://issues.apache.org/jira/browse/SOLR-18570
             Project: Solr
          Issue Type: Bug
          Components: SolrCloud
    Affects Versions: 10.0
            Reporter: Jerry Chung


After a short series of ZooKeeper connection-state changes, one replica ended 
up as leader in ZooKeeper (leaders/<shard>/leader, state.json, lowest election 
znode) while its in-memory CloudDescriptor.isLeader() was false. It stayed in 
that state for about 3 days. Every update routed to it fails with:

SolrException: ClusterState says we are the leader 
(https://<host>:8983/solr/<core>), but locally we don't think so. Request came 
from null
  at 
DistributedZkUpdateProcessor.doDefensiveChecks(DistributedZkUpdateProcessor.java:1141)

This was about 85 failed updates per second in the worst window. Nothing in 
Solr re-ran the election. RELOAD did not help.

Environment: Solr 10.0.0 (SolrCloud, 70 live nodes, TLOG/NRT per collection, 
HTTP/2 over TLS), ZooKeeper 3.9.4, Curator-based ZK client. Collection has 2 
replicas per shard.

Timeline (UTC, from the node's solr.log)

12:32:07  ConnectionStateManager: State change: SUSPENDED
12:32:09  ClientCnxn: Unable to reconnect, session 0x200000034f50151 has expired
          State change: LOST, then RECONNECTED at 12:32:09.4
12:32:25  coreZkRegister: Registering core <core> afterExpiration? true
12:32:28  ShardLeaderElectionContext: I may be the new leader - try and sync
          ... Creating leader registration node .../leaders/<shard>/leader 
after winning
          ... I am the new leader: https://<host>:8983/solr/<core>/
12:33:29  State change: SUSPENDED
12:33:33  State change: RECONNECTED (same session, not expired)
          ZkController: "ZooKeeper session re-connected ... refreshing core 
states"
12:33:34  Registering core <core> afterExpiration? true
12:33:35  ShardLeaderElectionContextBase: make sure parent is created 
.../leaders/<shard>
          (no "I may be the new leader" this time)
12:34:42  first "ClusterState says we are the leader ... locally we don't think 
so"
12:34:47  State change: SUSPENDED, another session expiry

No Error registering SolrCore or Exception registering core lines appear around 
12:33–12:35. Errors then continued for about 3 days, until manual intervention.

ZK state during the incident (read with bin/solr zk):
- leaders/<shard>/leader named the affected replica.
- The election queue had the affected replica as lowest (n_0000000008) and its 
peer as n_0000000009.
- state.json: the affected replica leader:"true", both replicas active.
- Terms: \{core_nodeA: 8, core_nodeB: 8}.

So ZK was self-consistent. Only the local flag disagreed.

What I think is happening (from reading the code, not confirmed)
- ZkController.onDisconnect runs on SUSPENDED as well as LOST and calls 
setLeader(false) on every core.
- On RECONNECTED of the same session, onReconnect re-registers all cores and 
calls joinElection. The leader and election znodes created by that session are 
still present.
- The second registration never reached I may be the new leader, so the local 
flag was never set to true again (the only place that sets it is 
ShardLeaderElectionContext.java:301).
- RELOAD goes through registerCore(cd, core, false, false) with 
registerInZk=false (CoreContainer.reload), so it never rejoins the election.
- The defensive check in DistributedZkUpdateProcessor throws after about 2.5 s 
of retries (5 × 500 ms). Nothing repairs the mismatch.

Expected behavior: when a core's re-registration after a SUSPENDED/RECONNECTED 
does not result in a leader state consistent with ZK, Solr should detect it and 
recover. For example, it could re-run the leader election, or rejoin at the 
tail so the peer can take over. A defensive check that hits this condition 
could trigger that self-heal instead of only failing the request.

Workaround found: none that was simple. RELOAD does nothing. FORCELEADER only 
changes terms and requires an election to already be running. Deleting the 
election znode left the shard leaderless. A node restart is the reliable way to 
re-register.

Open questions
- Why the second registration at 12:33:35 skipped the leader process. A 
debug-level log of ShardLeaderElectionContext / LeaderElector would show.
- What caused the ZK session instability (cause unknown, under investigation on 
our side).

Suggestions
1. Self-heal in doDefensiveChecks: when ZK says leader and the local flag is 
false after the retry loop, trigger a fresh joinElection for the core, or at 
least log an actionable warning.
2. Consider whether onDisconnect(SUSPENDED) should clear the leader flag when 
the session may still be alive.
3. Make RELOAD re-register in ZK, or document that it does not.

 

Related: SOLR-18560 (recovery commit fan-out) and SOLR-18568 (unload hang in 
closeAndWait). A ZK session flap triggers mass recovery and can lead to all 
three symptoms. This ticket is specifically about the leader flag not being 
restored after SUSPENDED/RECONNECTED.



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to