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