1 /*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2002, 2014 Oracle and/or its affiliates. All rights reserved. 5 * 6 */ 7 package com.sleepycat.je.rep.impl.node; 8 9 import java.util.concurrent.CountDownLatch; 10 import java.util.concurrent.TimeUnit; 11 12 import com.sleepycat.je.rep.elections.Proposer.Proposal; 13 14 /** 15 * Ensures that a VLSN is not advanced at this node while an election is in 16 * progress. Note that this is difficult, if not impossible to achieve 17 * efficiently in a distributed environment across the entire group, when 18 * communications may not always be reliable. So, the implementation really 19 * represents a good faith effort to freeze the VLSN. JE HA itself should be 20 * able to make forward progress in the event of such a failure. 21 * 22 * The class coordinates three threads: the acceptor, the learner, and the 23 * replay thread. There is exactly one instance of each thread per replication 24 * node, so it coordinates the activity of these three threads. 25 * 26 * The typical serialized sequence of calls is therefore: 27 * 28 * latch.freeze() -- invoked in response to a Promise by an Acceptor 29 * latch.vlsnEvent() -- one or more of them in response to ongoing election 30 * latch.awaitThaw() -- by the replica thread waiting for the freeze to lift 31 * 32 * Both vlsnEvent() and awaitThaw() are NOPs in the absence of a freeze. 33 * 34 * @see <a href="https://sleepycat.oracle.com/trac/wiki/ElectionsImplementation#FreezingVLSNs">Freezing VLSNs</a> 35 */ 36 public class CommitFreezeLatch { 37 38 /* The current frozen promise/vlsn pair */ 39 private Proposal proposal = null; 40 41 /* Statistics */ 42 private int freezeCount = 0; 43 private int awaitTimeoutCount = 0; 44 private int awaitElectionCount = 0; 45 46 /* The latch used internally. */ 47 private CountDownLatch latch = null; 48 /* The end time of the freeze. */ 49 private long freezeEnd = 0; 50 private long timeOut = DEFAULT_LATCH_TIMEOUT; 51 52 private static long DEFAULT_LATCH_TIMEOUT = 5000; // ms 53 getAwaitTimeoutCount()54 public int getAwaitTimeoutCount() { 55 return awaitTimeoutCount; 56 } 57 getAwaitElectionCount()58 public int getAwaitElectionCount() { 59 return awaitElectionCount; 60 } 61 getFreezeCount()62 public int getFreezeCount() { 63 return freezeCount; 64 } 65 getTimeOut()66 public long getTimeOut() { 67 return timeOut; 68 } 69 setTimeOut(long timeOut)70 public void setTimeOut(long timeOut) { 71 this.timeOut = timeOut; 72 } 73 74 /** 75 * Initiates or extends a freeze on a VLSN in response to a new election 76 * that is in progress. It's invoked by the Acceptor thread. 77 * 78 * @param freezeProposal identifies the election that is provoking the freeze 79 */ freeze(Proposal freezeProposal)80 public synchronized void freeze(Proposal freezeProposal) { 81 if ((proposal != null) && (freezeProposal.compareTo(proposal) <= 0)) { 82 // Older proposal ignore it. 83 return; 84 } 85 if (latch != null) { 86 /* Enable waiters who will reacquire the new latch below. */ 87 latch.countDown(); 88 } 89 latch = new CountDownLatch(1); 90 proposal = freezeProposal; 91 freezeEnd = System.currentTimeMillis() + timeOut; 92 return; 93 } 94 95 /** 96 * Invoked by the Learner thread whenever it receives an election result. 97 * The freeze on the VLSN is only lifted if the proposal associated with 98 * the event is current, that is, it represents a proposal that is newer 99 * than the one used to establish the freeze. 100 * 101 * @param listenerProposal identifies the election that just concluded 102 */ vlsnEvent(Proposal listenerProposal)103 public synchronized void vlsnEvent(Proposal listenerProposal) { 104 if (proposal == null) { 105 // No VLSN to unfreeze 106 return; 107 } 108 if (listenerProposal.compareTo(this.proposal) >= 0) { 109 latch.countDown(); 110 } 111 } 112 113 /** 114 * Clears the latch freeing any waiters. 115 */ clearLatch()116 public synchronized void clearLatch() { 117 if (latch != null) { 118 latch.countDown(); 119 } 120 latch = null; 121 proposal = null; 122 freezeEnd = 0; 123 } 124 125 /** 126 * Used to wait for an event that unfreezes the VLSN. In our case this 127 * event is a message to the Learner agent announcing the result of an 128 * election. Note that the latch must be re-initialized after a return from 129 * this await method. 130 * 131 * This method is invoked by the Replay thread. Completion of an awaitThaw 132 * always results in the freeze being lifted. 133 * 134 * @return true if the await was satisfied due to completion of an 135 * election, false if no freeze was in effect, or the latch was timed out. 136 * 137 * @throws InterruptedException 138 */ awaitThaw()139 public boolean awaitThaw() 140 throws InterruptedException { 141 142 CountDownLatch awaitLatch; 143 long awaitTimeout; 144 145 synchronized (this) { 146 /* Copy out the values of interest */ 147 awaitLatch = latch; 148 if (awaitLatch == null) { 149 return false; 150 } 151 awaitTimeout = this.freezeEnd - System.currentTimeMillis(); 152 } 153 freezeCount++; 154 155 boolean done = awaitLatch.await(awaitTimeout, TimeUnit.MILLISECONDS); 156 157 synchronized (this) { 158 if (done) { 159 awaitElectionCount++; 160 clearLatch(); 161 return true; 162 } 163 if (this.freezeEnd - System.currentTimeMillis() <= 0) { 164 awaitTimeoutCount++; 165 /* freeze end was not extended, election completed. */ 166 clearLatch(); 167 return false; 168 } 169 } 170 /* Re-acquire the new latch and wait for the extended timeout. */ 171 return awaitThaw(); 172 } 173 } 174