[ 
https://issues.apache.org/jira/browse/ZOOKEEPER-4781?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

zhanglu153 updated ZOOKEEPER-4781:
----------------------------------
    Description: 
This issue occurred in our abnormal testing environment, where the disk was 
injected with anomalies and frequently filled up.

The the scenario is as follows:
 # Configure three node ZooKeeper cluster, lets say nodes are A, B and C.
 # Start the cluster, and node C becomes the leader.
 # The disk of Node C is injected with a full disk exception.
 # Node C called the org.apache.zookeeper.server.quorum.Leader#lead method.
{code:java}
void lead() throws IOException, InterruptedException {
    self.end_fle = Time.currentElapsedTime();
    long electionTimeTaken = self.end_fle - self.start_fle;
    self.setElectionTimeTaken(electionTimeTaken);
    LOG.info("LEADING - LEADER ELECTION TOOK - {}", electionTimeTaken);
    self.start_fle = 0;
    self.end_fle = 0;

    zk.registerJMX(new LeaderBean(this, zk), self.jmxLocalPeerBean);

    try {
        self.tick.set(0);
        zk.loadData();
        
        leaderStateSummary = new StateSummary(self.getCurrentEpoch(), 
zk.getLastProcessedZxid());

        // Start thread that waits for connection requests from 
        // new followers.
        cnxAcceptor = new LearnerCnxAcceptor();
        cnxAcceptor.start();
        
        readyToStart = true;
        long epoch = getEpochToPropose(self.getId(), self.getAcceptedEpoch());
        
        zk.setZxid(ZxidUtils.makeZxid(epoch, 0));
        
        synchronized(this){
            lastProposed = zk.getZxid();
        }
        
        newLeaderProposal.packet = new QuorumPacket(NEWLEADER, zk.getZxid(),
                null, null);


        if ((newLeaderProposal.packet.getZxid() & 0xffffffffL) != 0) {
            LOG.info("NEWLEADER proposal has Zxid of "
                    + Long.toHexString(newLeaderProposal.packet.getZxid()));
        }
        
        waitForEpochAck(self.getId(), leaderStateSummary);
        self.setCurrentEpoch(epoch);
... {code}

 # Node C, as the leader, will start the LearnerCnxAcceptor thread, and then 
call the org.apache.zookeeper.server.quorum.Leader#getEpochToPropose method. At 
this time, the value of waitingForNewEpoch is true, and the size of 
connectingFollowers is not greater than n/2. Node C directly calls 
connectingFollowers.wait to wait. The maximum waiting time is 
self.getInitLimit()*self.getTickTime() ms.
{code:java}
public long getEpochToPropose(long sid, long lastAcceptedEpoch) throws 
InterruptedException, IOException {
    synchronized(connectingFollowers) {
        if (!waitingForNewEpoch) {
            return epoch;
        }
        if (lastAcceptedEpoch >= epoch) {
            epoch = lastAcceptedEpoch+1;
        }
        if (isParticipant(sid)) {
            connectingFollowers.add(sid);
        }
        QuorumVerifier verifier = self.getQuorumVerifier();
        if (connectingFollowers.contains(self.getId()) && 
                                        
verifier.containsQuorum(connectingFollowers)) {
            self.setAcceptedEpoch(epoch);
            waitingForNewEpoch = false;
            connectingFollowers.notifyAll();
        } else {
            long start = Time.currentElapsedTime();
            long cur = start;
            long end = start + self.getInitLimit()*self.getTickTime();
            while(waitingForNewEpoch && cur < end) {
                connectingFollowers.wait(end - cur);
                cur = Time.currentElapsedTime();
            }
            if (waitingForNewEpoch) {
                throw new InterruptedException("Timeout while waiting for epoch 
from quorum");        
            }
        }
        return epoch;
    }
}{code}

 # Node B connects to the 2888 communication port of node C and starts a new 
LeanerHandler thread. 
 # Node A connects to the 2888 communication port of node C and starts a new 
LeanerHandler thread.
 # After node B connects to node C, call the 
org.apache.zookeeper.server.quorum.Leader#getEpochToPropose method in the 
LearnerHandler thread.At this point, the value of waitingForNewEpoch is true, 
and the size of connectingFollowers is greater than n/2. Then, set the value of 
waitingForNewEpoch to false. Due to the disk of node C being full, calling 
setAcceptedEpoch to write the acceptedEpoch value failed with an IO exception. 
Node C fails to update the acceptedEpoch file and did not successfully call the 
connectingFollowers.notifyAll() method. This will cause node C to wait at 
connectingFollowers.wait, with a maximum wait of 
self.getInitLimit()*self.getTickTime() ms. Due to an IO exception thrown, the 
socket connection between node B and node C was disconnected, and the 
LeanerHandler thread exited.
{code:java}
2023-12-15 15:08:35,572 [myid:3] - ERROR 
[LearnerHandler-/192.168.174.227:41696:LearnerHandler@648] - Unexpected 
exception causing shutdown while sock still open2023-12-15 15:08:35,572 
[myid:3] - ERROR [LearnerHandler-/192.168.174.227:41696:LearnerHandler@648] - 
Unexpected exception causing shutdown while sock still 
openjava.io.FileNotFoundException: 
/cloud/data/zookeeper/data/version-2/acceptedEpoch.tmp (No space left on 
device) at java.io.FileOutputStream.open0(Native Method) at 
java.io.FileOutputStream.open(FileOutputStream.java:270) at 
java.io.FileOutputStream.<init>(FileOutputStream.java:213) at 
java.io.FileOutputStream.<init>(FileOutputStream.java:162) at 
org.apache.zookeeper.common.AtomicFileOutputStream.<init>(AtomicFileOutputStream.java:59)
 at 
org.apache.zookeeper.server.quorum.QuorumPeer.writeLongToFile(QuorumPeer.java:1393)
 at 
org.apache.zookeeper.server.quorum.QuorumPeer.setAcceptedEpoch(QuorumPeer.java:1437)
 at 
org.apache.zookeeper.server.quorum.Leader.getEpochToPropose(Leader.java:885) at 
org.apache.zookeeper.server.quorum.LearnerHandler.run(LearnerHandler.java:358)  
{code}

 # After node A connects to node C, the 
org.apache.zookeeper.server.quorum.Leader#getEpochToPropose method is called in 
the LearnerHandler thread. At this time, the value of waitingForNewEpoch is 
false, and epoch is returned directly.
 # Node B reconnects to the 2888 cluster communication port of node C and 
starts a new LeanerHandler thread. Call the 
org.apache.zookeeper.server.quorum.Leader#getEpochToPropose method in the 
LearnerHandler thread. At this time, the value of waitingForNewEpoch is false, 
and epoch is returned directly.
 # After node A connects to node C, the LeanerHandler thread will then call the 
org.apache.zookeeper.server.quorum.Leader#waitForEpochAck method. At this 
point, selectingFollowers does not include node C, Call the 
selectingFollowers.wait method to wait.
 # After node B connects to node C, the LeanerHandler thread will then call the 
org.apache.zookeeper.server.quorum.Leader#waitForEpochAck method. At this 
point, selectingFollowers does not include node C, Call the 
selectingFollowers.wait method to wait.
{code:java}
public void waitForEpochAck(long id, StateSummary ss) throws IOException, 
InterruptedException {
    synchronized(electingFollowers) {
        if (electionFinished) {
            return;
        }
        if (ss.getCurrentEpoch() != -1) {
            if (ss.isMoreRecentThan(leaderStateSummary)) {
                throw new IOException("Follower is ahead of the leader, leader 
summary: " 
                                                + 
leaderStateSummary.getCurrentEpoch()
                                                + " (current epoch), "
                                                + 
leaderStateSummary.getLastZxid()
                                                + " (last zxid)");
            }
            if (isParticipant(id)) {
                electingFollowers.add(id);
            }
        }
        QuorumVerifier verifier = self.getQuorumVerifier();
        if (electingFollowers.contains(self.getId()) && 
verifier.containsQuorum(electingFollowers)) {
            electionFinished = true;
            electingFollowers.notifyAll();
        } else {                
            long start = Time.currentElapsedTime();
            long cur = start;
            long end = start + self.getInitLimit()*self.getTickTime();
            while(!electionFinished && cur < end) {
                electingFollowers.wait(end - cur);
                cur = Time.currentElapsedTime();
            }
            if (!electionFinished) {
                throw new InterruptedException("Timeout while waiting for epoch 
to be acked by quorum");
            }
        }
    }
} {code}

 # Node C waits for self.getInitLimit()*self.getTickTime() ms in the 
connectingFollowers.wait method. However, since the value of waitingForNewEpoch 
is false, it does not throw a timeout exception and returns the epoch directly.
 # Node C calls the org.apache.zookeeper.server.quorum.Leader#waitForEpochAck 
method. At this time, selectingFollowers contains node C and the size of 
selectingFollowers is greater than n/2. Then call the 
selectingFollowers.notifyAll() method to release the lock.
 # The disk space of node C has been partially released, so subsequent calls to 
setCurrentEpoch have successfully updated the currentEpoch file.
 # The cluster provides services normally for a period of time.
 # Stop C.
 # Start C, bellow exception with message "The accepted epoch, c is less than 
the current epoch, d" is thrown.

{code:java}
2023-12-15 15:57:00,051 [myid:3] - ERROR [main:QuorumPeer@698] - Unable to load 
database on disk
java.io.IOException: The accepted epoch, c is less than the current epoch, d
    at 
org.apache.zookeeper.server.quorum.QuorumPeer.loadDataBase(QuorumPeer.java:695)
    at org.apache.zookeeper.server.quorum.QuorumPeer.start(QuorumPeer.java:636)
    at 
org.apache.zookeeper.server.quorum.QuorumPeerMain.runFromConfig(QuorumPeerMain.java:170)
    at 
org.apache.zookeeper.server.quorum.QuorumPeerMain.initializeAndRun(QuorumPeerMain.java:114)
    at 
org.apache.zookeeper.server.quorum.QuorumPeerMain.main(QuorumPeerMain.java:81)
2023-12-15 15:57:00,132 [myid:3] - ERROR [main:QuorumPeerMain@92] - Unexpected 
exception, exiting abnormally
java.lang.RuntimeException: Unable to run quorum server 
    at 
org.apache.zookeeper.server.quorum.QuorumPeer.loadDataBase(QuorumPeer.java:699)
    at org.apache.zookeeper.server.quorum.QuorumPeer.start(QuorumPeer.java:636)
    at 
org.apache.zookeeper.server.quorum.QuorumPeerMain.runFromConfig(QuorumPeerMain.java:170)
    at 
org.apache.zookeeper.server.quorum.QuorumPeerMain.initializeAndRun(QuorumPeerMain.java:114)
    at 
org.apache.zookeeper.server.quorum.QuorumPeerMain.main(QuorumPeerMain.java:81)
Caused by: java.io.IOException: The accepted epoch, c is less than the current 
epoch, d
    at 
org.apache.zookeeper.server.quorum.QuorumPeer.loadDataBase(QuorumPeer.java:695) 
{code}

  was:
This issue occurred in our abnormal testing environment, where the disk was 
injected with anomalies and frequently filled up.

The the scenario is as follows:
 # Configure three node ZooKeeper cluster, lets say nodes are A, B and C.
 # Start the cluster, and node C becomes the leader.
 # The disk of Node C is injected with a full disk exception.
 # Node C called the org.apache.zookeeper.server.quorum.Leader#lead method.
{code:java}
void lead() throws IOException, InterruptedException {
    self.end_fle = Time.currentElapsedTime();
    long electionTimeTaken = self.end_fle - self.start_fle;
    self.setElectionTimeTaken(electionTimeTaken);
    LOG.info("LEADING - LEADER ELECTION TOOK - {}", electionTimeTaken);
    self.start_fle = 0;
    self.end_fle = 0;

    zk.registerJMX(new LeaderBean(this, zk), self.jmxLocalPeerBean);

    try {
        self.tick.set(0);
        zk.loadData();
        
        leaderStateSummary = new StateSummary(self.getCurrentEpoch(), 
zk.getLastProcessedZxid());

        // Start thread that waits for connection requests from 
        // new followers.
        cnxAcceptor = new LearnerCnxAcceptor();
        cnxAcceptor.start();
        
        readyToStart = true;
        long epoch = getEpochToPropose(self.getId(), self.getAcceptedEpoch());
        
        zk.setZxid(ZxidUtils.makeZxid(epoch, 0));
        
        synchronized(this){
            lastProposed = zk.getZxid();
        }
        
        newLeaderProposal.packet = new QuorumPacket(NEWLEADER, zk.getZxid(),
                null, null);


        if ((newLeaderProposal.packet.getZxid() & 0xffffffffL) != 0) {
            LOG.info("NEWLEADER proposal has Zxid of "
                    + Long.toHexString(newLeaderProposal.packet.getZxid()));
        }
        
        waitForEpochAck(self.getId(), leaderStateSummary);
        self.setCurrentEpoch(epoch);
... {code}

 # Node C, as the leader, will start the LearnerCnxAcceptor thread, and then 
call the org.apache.zookeeper.server.quorum.Leader#getEpochToPropose method. At 
this time, the value of waitingForNewEpoch is true, and the size of 
connectingFollowers is not greater than n/2. Node C directly calls 
connectingFollowers.wait to wait. The maximum waiting time is 
self.getInitLimit()*self.getTickTime() ms.
{code:java}
public long getEpochToPropose(long sid, long lastAcceptedEpoch) throws 
InterruptedException, IOException {
    synchronized(connectingFollowers) {
        if (!waitingForNewEpoch) {
            return epoch;
        }
        if (lastAcceptedEpoch >= epoch) {
            epoch = lastAcceptedEpoch+1;
        }
        if (isParticipant(sid)) {
            connectingFollowers.add(sid);
        }
        QuorumVerifier verifier = self.getQuorumVerifier();
        if (connectingFollowers.contains(self.getId()) && 
                                        
verifier.containsQuorum(connectingFollowers)) {
            self.setAcceptedEpoch(epoch);
            waitingForNewEpoch = false;
            connectingFollowers.notifyAll();
        } else {
            long start = Time.currentElapsedTime();
            long cur = start;
            long end = start + self.getInitLimit()*self.getTickTime();
            while(waitingForNewEpoch && cur < end) {
                connectingFollowers.wait(end - cur);
                cur = Time.currentElapsedTime();
            }
            if (waitingForNewEpoch) {
                throw new InterruptedException("Timeout while waiting for epoch 
from quorum");        
            }
        }
        return epoch;
    }
} {code}

 # Node B connects to the 2888 communication port of node C and starts a new 
LeanerHandler thread. 
 # Node A connects to the 2888 communication port of node C and starts a new 
LeanerHandler thread.
 # After node B connects to node C, call the 
org.apache.zookeeper.server.quorum.Leader#getEpochToPropose method in the 
LearnerHandler thread.At this point, the value of waitingForNewEpoch is true, 
and the size of connectingFollowers is greater than n/2. Then, set the value of 
waitingForNewEpoch to false. Due to the disk of node C being full, calling 
setAcceptedEpoch to write the acceptedEpoch value failed with an IO exception. 
Node C fails to update the acceptedEpoch file and did not successfully call the 
connectingFollowers.notifyAll() method. This will cause node C to wait at 
connectingFollowers.wait, with a maximum wait of 
self.getInitLimit()*self.getTickTime() ms. Due to an IO exception thrown, the 
socket connection between node B and node C was disconnected, and the 
LeanerHandler thread exited.
{code:java}
2023-12-15 15:08:35,572 [myid:3] - ERROR 
[LearnerHandler-/192.168.174.227:41696:LearnerHandler@648] - Unexpected 
exception causing shutdown while sock still open2023-12-15 15:08:35,572 
[myid:3] - ERROR [LearnerHandler-/192.168.174.227:41696:LearnerHandler@648] - 
Unexpected exception causing shutdown while sock still 
openjava.io.FileNotFoundException: 
/cloud/data/zookeeper/data/version-2/acceptedEpoch.tmp (No space left on 
device) at java.io.FileOutputStream.open0(Native Method) at 
java.io.FileOutputStream.open(FileOutputStream.java:270) at 
java.io.FileOutputStream.<init>(FileOutputStream.java:213) at 
java.io.FileOutputStream.<init>(FileOutputStream.java:162) at 
org.apache.zookeeper.common.AtomicFileOutputStream.<init>(AtomicFileOutputStream.java:59)
 at 
org.apache.zookeeper.server.quorum.QuorumPeer.writeLongToFile(QuorumPeer.java:1393)
 at 
org.apache.zookeeper.server.quorum.QuorumPeer.setAcceptedEpoch(QuorumPeer.java:1437)
 at 
org.apache.zookeeper.server.quorum.Leader.getEpochToPropose(Leader.java:885) at 
org.apache.zookeeper.server.quorum.LearnerHandler.run(LearnerHandler.java:358) 
{code}

 # After node A connects to node C, the 
org.apache.zookeeper.server.quorum.Leader#getEpochToPropose method is called in 
the LearnerHandler thread. At this time, the value of waitingForNewEpoch is 
false, and epoch is returned directly.
 # Node B reconnects to the 2888 cluster communication port of node C and 
starts a new LeanerHandler thread. Call the 
org.apache.zookeeper.server.quorum.Leader#getEpochToPropose method in the 
LearnerHandler thread. At this time, the value of waitingForNewEpoch is false, 
and epoch is returned directly.
 # After node A connects to node C, the LeanerHandler thread will then call the 
org.apache.zookeeper.server.quorum.Leader#waitForEpochAck method. At this 
point, selectingFollowers does not include node C, Call the 
selectingFollowers.wait method to wait.
 # After node B connects to node C, the LeanerHandler thread will then call the 
org.apache.zookeeper.server.quorum.Leader#waitForEpochAck method. At this 
point, selectingFollowers does not include node C, Call the 
selectingFollowers.wait method to wait.
{code:java}
public void waitForEpochAck(long id, StateSummary ss) throws IOException, 
InterruptedException {
    synchronized(electingFollowers) {
        if (electionFinished) {
            return;
        }
        if (ss.getCurrentEpoch() != -1) {
            if (ss.isMoreRecentThan(leaderStateSummary)) {
                throw new IOException("Follower is ahead of the leader, leader 
summary: " 
                                                + 
leaderStateSummary.getCurrentEpoch()
                                                + " (current epoch), "
                                                + 
leaderStateSummary.getLastZxid()
                                                + " (last zxid)");
            }
            if (isParticipant(id)) {
                electingFollowers.add(id);
            }
        }
        QuorumVerifier verifier = self.getQuorumVerifier();
        if (electingFollowers.contains(self.getId()) && 
verifier.containsQuorum(electingFollowers)) {
            electionFinished = true;
            electingFollowers.notifyAll();
        } else {                
            long start = Time.currentElapsedTime();
            long cur = start;
            long end = start + self.getInitLimit()*self.getTickTime();
            while(!electionFinished && cur < end) {
                electingFollowers.wait(end - cur);
                cur = Time.currentElapsedTime();
            }
            if (!electionFinished) {
                throw new InterruptedException("Timeout while waiting for epoch 
to be acked by quorum");
            }
        }
    }
} {code}

 # Node C waits for self.getInitLimit()*self.getTickTime() ms in the 
connectingFollowers.wait method. However, since the value of waitingForNewEpoch 
is false, it does not throw a timeout exception and returns the epoch directly.
 # Node C calls the org.apache.zookeeper.server.quorum.Leader#waitForEpochAck 
method. At this time, selectingFollowers contains node C and the size of 
selectingFollowers is greater than n/2. Then call the 
selectingFollowers.notifyAll() method to release the lock.
 # The disk space of node C has been partially released, so subsequent calls to 
setCurrentEpoch have successfully updated the currentEpoch file.
 # The cluster provides services normally for a period of time.
 # Stop C.
 # Start C, bellow exception with message "The accepted epoch, c is less than 
the current epoch, d" is thrown.
{code:java}
2023-12-15 15:57:00,051 [myid:3] - ERROR [main:QuorumPeer@698] - Unable to load 
database on disk
java.io.IOException: The accepted epoch, c is less than the current epoch, d
    at 
org.apache.zookeeper.server.quorum.QuorumPeer.loadDataBase(QuorumPeer.java:695)
    at org.apache.zookeeper.server.quorum.QuorumPeer.start(QuorumPeer.java:636)
    at 
org.apache.zookeeper.server.quorum.QuorumPeerMain.runFromConfig(QuorumPeerMain.java:170)
    at 
org.apache.zookeeper.server.quorum.QuorumPeerMain.initializeAndRun(QuorumPeerMain.java:114)
    at 
org.apache.zookeeper.server.quorum.QuorumPeerMain.main(QuorumPeerMain.java:81)
2023-12-15 15:57:00,132 [myid:3] - ERROR [main:QuorumPeerMain@92] - Unexpected 
exception, exiting abnormally
java.lang.RuntimeException: Unable to run quorum server 
    at 
org.apache.zookeeper.server.quorum.QuorumPeer.loadDataBase(QuorumPeer.java:699)
    at org.apache.zookeeper.server.quorum.QuorumPeer.start(QuorumPeer.java:636)
    at 
org.apache.zookeeper.server.quorum.QuorumPeerMain.runFromConfig(QuorumPeerMain.java:170)
    at 
org.apache.zookeeper.server.quorum.QuorumPeerMain.initializeAndRun(QuorumPeerMain.java:114)
    at 
org.apache.zookeeper.server.quorum.QuorumPeerMain.main(QuorumPeerMain.java:81)
Caused by: java.io.IOException: The accepted epoch, c is less than the current 
epoch, d
    at 
org.apache.zookeeper.server.quorum.QuorumPeer.loadDataBase(QuorumPeer.java:695) 
{code}


> ZooKeeper not starting because the accepted epoch is less than the current 
> epoch.
> ---------------------------------------------------------------------------------
>
>                 Key: ZOOKEEPER-4781
>                 URL: https://issues.apache.org/jira/browse/ZOOKEEPER-4781
>             Project: ZooKeeper
>          Issue Type: Bug
>          Components: server
>    Affects Versions: 3.5.10, 3.4.14, 3.6.4, 3.7.2, 3.8.3, 3.9.1
>            Reporter: zhanglu153
>            Priority: Major
>         Attachments: 0001-zk-leader-acceptedEpoch-zk-zk.patch
>
>
> This issue occurred in our abnormal testing environment, where the disk was 
> injected with anomalies and frequently filled up.
> The the scenario is as follows:
>  # Configure three node ZooKeeper cluster, lets say nodes are A, B and C.
>  # Start the cluster, and node C becomes the leader.
>  # The disk of Node C is injected with a full disk exception.
>  # Node C called the org.apache.zookeeper.server.quorum.Leader#lead method.
> {code:java}
> void lead() throws IOException, InterruptedException {
>     self.end_fle = Time.currentElapsedTime();
>     long electionTimeTaken = self.end_fle - self.start_fle;
>     self.setElectionTimeTaken(electionTimeTaken);
>     LOG.info("LEADING - LEADER ELECTION TOOK - {}", electionTimeTaken);
>     self.start_fle = 0;
>     self.end_fle = 0;
>     zk.registerJMX(new LeaderBean(this, zk), self.jmxLocalPeerBean);
>     try {
>         self.tick.set(0);
>         zk.loadData();
>         
>         leaderStateSummary = new StateSummary(self.getCurrentEpoch(), 
> zk.getLastProcessedZxid());
>         // Start thread that waits for connection requests from 
>         // new followers.
>         cnxAcceptor = new LearnerCnxAcceptor();
>         cnxAcceptor.start();
>         
>         readyToStart = true;
>         long epoch = getEpochToPropose(self.getId(), self.getAcceptedEpoch());
>         
>         zk.setZxid(ZxidUtils.makeZxid(epoch, 0));
>         
>         synchronized(this){
>             lastProposed = zk.getZxid();
>         }
>         
>         newLeaderProposal.packet = new QuorumPacket(NEWLEADER, zk.getZxid(),
>                 null, null);
>         if ((newLeaderProposal.packet.getZxid() & 0xffffffffL) != 0) {
>             LOG.info("NEWLEADER proposal has Zxid of "
>                     + Long.toHexString(newLeaderProposal.packet.getZxid()));
>         }
>         
>         waitForEpochAck(self.getId(), leaderStateSummary);
>         self.setCurrentEpoch(epoch);
> ... {code}
>  # Node C, as the leader, will start the LearnerCnxAcceptor thread, and then 
> call the org.apache.zookeeper.server.quorum.Leader#getEpochToPropose method. 
> At this time, the value of waitingForNewEpoch is true, and the size of 
> connectingFollowers is not greater than n/2. Node C directly calls 
> connectingFollowers.wait to wait. The maximum waiting time is 
> self.getInitLimit()*self.getTickTime() ms.
> {code:java}
> public long getEpochToPropose(long sid, long lastAcceptedEpoch) throws 
> InterruptedException, IOException {
>     synchronized(connectingFollowers) {
>         if (!waitingForNewEpoch) {
>             return epoch;
>         }
>         if (lastAcceptedEpoch >= epoch) {
>             epoch = lastAcceptedEpoch+1;
>         }
>         if (isParticipant(sid)) {
>             connectingFollowers.add(sid);
>         }
>         QuorumVerifier verifier = self.getQuorumVerifier();
>         if (connectingFollowers.contains(self.getId()) && 
>                                         
> verifier.containsQuorum(connectingFollowers)) {
>             self.setAcceptedEpoch(epoch);
>             waitingForNewEpoch = false;
>             connectingFollowers.notifyAll();
>         } else {
>             long start = Time.currentElapsedTime();
>             long cur = start;
>             long end = start + self.getInitLimit()*self.getTickTime();
>             while(waitingForNewEpoch && cur < end) {
>                 connectingFollowers.wait(end - cur);
>                 cur = Time.currentElapsedTime();
>             }
>             if (waitingForNewEpoch) {
>                 throw new InterruptedException("Timeout while waiting for 
> epoch from quorum");        
>             }
>         }
>         return epoch;
>     }
> }{code}
>  # Node B connects to the 2888 communication port of node C and starts a new 
> LeanerHandler thread. 
>  # Node A connects to the 2888 communication port of node C and starts a new 
> LeanerHandler thread.
>  # After node B connects to node C, call the 
> org.apache.zookeeper.server.quorum.Leader#getEpochToPropose method in the 
> LearnerHandler thread.At this point, the value of waitingForNewEpoch is true, 
> and the size of connectingFollowers is greater than n/2. Then, set the value 
> of waitingForNewEpoch to false. Due to the disk of node C being full, calling 
> setAcceptedEpoch to write the acceptedEpoch value failed with an IO 
> exception. Node C fails to update the acceptedEpoch file and did not 
> successfully call the connectingFollowers.notifyAll() method. This will cause 
> node C to wait at connectingFollowers.wait, with a maximum wait of 
> self.getInitLimit()*self.getTickTime() ms. Due to an IO exception thrown, the 
> socket connection between node B and node C was disconnected, and the 
> LeanerHandler thread exited.
> {code:java}
> 2023-12-15 15:08:35,572 [myid:3] - ERROR 
> [LearnerHandler-/192.168.174.227:41696:LearnerHandler@648] - Unexpected 
> exception causing shutdown while sock still open2023-12-15 15:08:35,572 
> [myid:3] - ERROR [LearnerHandler-/192.168.174.227:41696:LearnerHandler@648] - 
> Unexpected exception causing shutdown while sock still 
> openjava.io.FileNotFoundException: 
> /cloud/data/zookeeper/data/version-2/acceptedEpoch.tmp (No space left on 
> device) at java.io.FileOutputStream.open0(Native Method) at 
> java.io.FileOutputStream.open(FileOutputStream.java:270) at 
> java.io.FileOutputStream.<init>(FileOutputStream.java:213) at 
> java.io.FileOutputStream.<init>(FileOutputStream.java:162) at 
> org.apache.zookeeper.common.AtomicFileOutputStream.<init>(AtomicFileOutputStream.java:59)
>  at 
> org.apache.zookeeper.server.quorum.QuorumPeer.writeLongToFile(QuorumPeer.java:1393)
>  at 
> org.apache.zookeeper.server.quorum.QuorumPeer.setAcceptedEpoch(QuorumPeer.java:1437)
>  at 
> org.apache.zookeeper.server.quorum.Leader.getEpochToPropose(Leader.java:885) 
> at 
> org.apache.zookeeper.server.quorum.LearnerHandler.run(LearnerHandler.java:358)
>   {code}
>  # After node A connects to node C, the 
> org.apache.zookeeper.server.quorum.Leader#getEpochToPropose method is called 
> in the LearnerHandler thread. At this time, the value of waitingForNewEpoch 
> is false, and epoch is returned directly.
>  # Node B reconnects to the 2888 cluster communication port of node C and 
> starts a new LeanerHandler thread. Call the 
> org.apache.zookeeper.server.quorum.Leader#getEpochToPropose method in the 
> LearnerHandler thread. At this time, the value of waitingForNewEpoch is 
> false, and epoch is returned directly.
>  # After node A connects to node C, the LeanerHandler thread will then call 
> the org.apache.zookeeper.server.quorum.Leader#waitForEpochAck method. At this 
> point, selectingFollowers does not include node C, Call the 
> selectingFollowers.wait method to wait.
>  # After node B connects to node C, the LeanerHandler thread will then call 
> the org.apache.zookeeper.server.quorum.Leader#waitForEpochAck method. At this 
> point, selectingFollowers does not include node C, Call the 
> selectingFollowers.wait method to wait.
> {code:java}
> public void waitForEpochAck(long id, StateSummary ss) throws IOException, 
> InterruptedException {
>     synchronized(electingFollowers) {
>         if (electionFinished) {
>             return;
>         }
>         if (ss.getCurrentEpoch() != -1) {
>             if (ss.isMoreRecentThan(leaderStateSummary)) {
>                 throw new IOException("Follower is ahead of the leader, 
> leader summary: " 
>                                                 + 
> leaderStateSummary.getCurrentEpoch()
>                                                 + " (current epoch), "
>                                                 + 
> leaderStateSummary.getLastZxid()
>                                                 + " (last zxid)");
>             }
>             if (isParticipant(id)) {
>                 electingFollowers.add(id);
>             }
>         }
>         QuorumVerifier verifier = self.getQuorumVerifier();
>         if (electingFollowers.contains(self.getId()) && 
> verifier.containsQuorum(electingFollowers)) {
>             electionFinished = true;
>             electingFollowers.notifyAll();
>         } else {                
>             long start = Time.currentElapsedTime();
>             long cur = start;
>             long end = start + self.getInitLimit()*self.getTickTime();
>             while(!electionFinished && cur < end) {
>                 electingFollowers.wait(end - cur);
>                 cur = Time.currentElapsedTime();
>             }
>             if (!electionFinished) {
>                 throw new InterruptedException("Timeout while waiting for 
> epoch to be acked by quorum");
>             }
>         }
>     }
> } {code}
>  # Node C waits for self.getInitLimit()*self.getTickTime() ms in the 
> connectingFollowers.wait method. However, since the value of 
> waitingForNewEpoch is false, it does not throw a timeout exception and 
> returns the epoch directly.
>  # Node C calls the org.apache.zookeeper.server.quorum.Leader#waitForEpochAck 
> method. At this time, selectingFollowers contains node C and the size of 
> selectingFollowers is greater than n/2. Then call the 
> selectingFollowers.notifyAll() method to release the lock.
>  # The disk space of node C has been partially released, so subsequent calls 
> to setCurrentEpoch have successfully updated the currentEpoch file.
>  # The cluster provides services normally for a period of time.
>  # Stop C.
>  # Start C, bellow exception with message "The accepted epoch, c is less than 
> the current epoch, d" is thrown.
> {code:java}
> 2023-12-15 15:57:00,051 [myid:3] - ERROR [main:QuorumPeer@698] - Unable to 
> load database on disk
> java.io.IOException: The accepted epoch, c is less than the current epoch, d
>     at 
> org.apache.zookeeper.server.quorum.QuorumPeer.loadDataBase(QuorumPeer.java:695)
>     at 
> org.apache.zookeeper.server.quorum.QuorumPeer.start(QuorumPeer.java:636)
>     at 
> org.apache.zookeeper.server.quorum.QuorumPeerMain.runFromConfig(QuorumPeerMain.java:170)
>     at 
> org.apache.zookeeper.server.quorum.QuorumPeerMain.initializeAndRun(QuorumPeerMain.java:114)
>     at 
> org.apache.zookeeper.server.quorum.QuorumPeerMain.main(QuorumPeerMain.java:81)
> 2023-12-15 15:57:00,132 [myid:3] - ERROR [main:QuorumPeerMain@92] - 
> Unexpected exception, exiting abnormally
> java.lang.RuntimeException: Unable to run quorum server 
>     at 
> org.apache.zookeeper.server.quorum.QuorumPeer.loadDataBase(QuorumPeer.java:699)
>     at 
> org.apache.zookeeper.server.quorum.QuorumPeer.start(QuorumPeer.java:636)
>     at 
> org.apache.zookeeper.server.quorum.QuorumPeerMain.runFromConfig(QuorumPeerMain.java:170)
>     at 
> org.apache.zookeeper.server.quorum.QuorumPeerMain.initializeAndRun(QuorumPeerMain.java:114)
>     at 
> org.apache.zookeeper.server.quorum.QuorumPeerMain.main(QuorumPeerMain.java:81)
> Caused by: java.io.IOException: The accepted epoch, c is less than the 
> current epoch, d
>     at 
> org.apache.zookeeper.server.quorum.QuorumPeer.loadDataBase(QuorumPeer.java:695)
>  {code}



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to