View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.regionserver.wal;
19  
20  import java.util.concurrent.ExecutionException;
21  import java.util.concurrent.TimeUnit;
22  
23  import org.apache.hadoop.hbase.classification.InterfaceAudience;
24  import org.apache.hadoop.hbase.exceptions.TimeoutIOException;
25  import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
26  import org.apache.htrace.Span;
27  
28  /**
29   * A Future on a filesystem sync call.  It given to a client or 'Handler' for it to wait on till
30   * the sync completes.
31   *
32   * <p>Handlers coming in call append, append, append, and then do a flush/sync of
33   * the edits they have appended the WAL before returning. Since sync takes a while to
34   * complete, we give the Handlers back this sync future to wait on until the
35   * actual HDFS sync completes. Meantime this sync future goes across the ringbuffer and into a
36   * sync runner thread; when it completes, it finishes up the future, the handler get or failed
37   * check completes and the Handler can then progress.
38   * <p>
39   * This is just a partial implementation of Future; we just implement get and
40   * failure.  Unimplemented methods throw {@link UnsupportedOperationException}.
41   * <p>
42   * There is not a one-to-one correlation between dfs sync invocations and
43   * instances of this class. A single dfs sync call may complete and mark many
44   * SyncFutures as done; i.e. we batch up sync calls rather than do a dfs sync
45   * call every time a Handler asks for it.
46   * <p>
47   * SyncFutures are immutable but recycled. Call #reset(long, Span) before use even
48   * if it the first time, start the sync, then park the 'hitched' thread on a call to
49   * #get().
50   */
51  @InterfaceAudience.Private
52  class SyncFuture {
53    // Implementation notes: I tried using a cyclicbarrier in here for handler and sync threads
54    // to coordinate on but it did not give any obvious advantage and some issues with order in which
55    // events happen.
56    private static final long NOT_DONE = 0;
57  
58    /**
59     * The sequence at which we were added to the ring buffer.
60     */
61    private long ringBufferSequence;
62  
63    /**
64     * The sequence that was set in here when we were marked done. Should be equal
65     * or > ringBufferSequence.  Put this data member into the NOT_DONE state while this
66     * class is in use.  But for the first position on construction, let it be -1 so we can
67     * immediately call {@link #reset(long, Span)} below and it will work.
68     */
69    private long doneSequence = -1;
70  
71    /**
72     * If error, the associated throwable. Set when the future is 'done'.
73     */
74    private Throwable throwable = null;
75  
76    private Thread t;
77  
78    /**
79     * Optionally carry a disconnected scope to the SyncRunner.
80     */
81    private Span span;
82  
83    private boolean forceSync;
84  
85    /**
86     * Call this method to clear old usage and get it ready for new deploy. Call
87     * this method even if it is being used for the first time.
88     *
89     * @param sequence sequenceId from this Future's position in the RingBuffer
90     * @return this
91     */
92    synchronized SyncFuture reset(final long sequence) {
93      return reset(sequence, null);
94    }
95  
96    /**
97     * Call this method to clear old usage and get it ready for new deploy. Call
98     * this method even if it is being used for the first time.
99     *
100    * @param sequence sequenceId from this Future's position in the RingBuffer
101    * @param span curren span, detached from caller. Don't forget to attach it when
102    *             resuming after a call to {@link #get()}.
103    * @return this
104    */
105   synchronized SyncFuture reset(final long sequence, Span span) {
106     if (t != null && t != Thread.currentThread()) throw new IllegalStateException();
107     t = Thread.currentThread();
108     if (!isDone()) throw new IllegalStateException("" + sequence + " " + Thread.currentThread());
109     this.doneSequence = NOT_DONE;
110     this.ringBufferSequence = sequence;
111     this.span = span;
112     this.throwable = null;
113     return this;
114   }
115 
116   @Override
117   public synchronized String toString() {
118     return "done=" + isDone() + ", ringBufferSequence=" + this.ringBufferSequence +
119       " threadID=" + t.getId() + " threadName=" + t.getName();
120   }
121 
122   synchronized long getRingBufferSequence() {
123     return this.ringBufferSequence;
124   }
125 
126   synchronized boolean isForceSync() {
127     return forceSync;
128   }
129 
130   synchronized SyncFuture setForceSync(boolean forceSync) {
131     this.forceSync = forceSync;
132     return this;
133   }
134 
135   /**
136    * Retrieve the {@code span} instance from this Future. EventHandler calls
137    * this method to continue the span. Thread waiting on this Future musn't call
138    * this method until AFTER calling {@link #get()} and the future has been
139    * released back to the originating thread.
140    */
141   synchronized Span getSpan() {
142     return this.span;
143   }
144 
145   /**
146    * Used to re-attach a {@code span} to the Future. Called by the EventHandler
147    * after a it has completed processing and detached the span from its scope.
148    */
149   synchronized void setSpan(Span span) {
150     this.span = span;
151   }
152 
153   /**
154    * @param sequence Sync sequence at which this future 'completed'.
155    * @param t Can be null.  Set if we are 'completing' on error (and this 't' is the error).
156    * @return True if we successfully marked this outstanding future as completed/done.
157    * Returns false if this future is already 'done' when this method called.
158    */
159   synchronized boolean done(final long sequence, final Throwable t) {
160     if (isDone()) return false;
161     this.throwable = t;
162     if (sequence < this.ringBufferSequence) {
163       // Something badly wrong.
164       if (throwable == null) {
165         this.throwable = new IllegalStateException("sequence=" + sequence +
166           ", ringBufferSequence=" + this.ringBufferSequence);
167       }
168     }
169     // Mark done.
170     this.doneSequence = sequence;
171     // Wake up waiting threads.
172     notify();
173     return true;
174   }
175 
176   public boolean cancel(boolean mayInterruptIfRunning) {
177     throw new UnsupportedOperationException();
178   }
179 
180   public synchronized long get(long timeout) throws InterruptedException,
181       ExecutionException, TimeoutIOException {
182     final long done = EnvironmentEdgeManager.currentTime() + timeout;
183     while (!isDone()) {
184       wait(1000);
185       if (EnvironmentEdgeManager.currentTime() >= done) {
186         throw new TimeoutIOException("Failed to get sync result after "
187             + timeout + " ms for ringBufferSequence=" + this.ringBufferSequence
188             + ", WAL system stuck?");
189       }
190     }
191     if (this.throwable != null) throw new ExecutionException(this.throwable);
192     return this.doneSequence;
193   }
194 
195   /**
196    * Returns the thread that owned this sync future, use with caution as we return the reference to
197    * the actual thread object.
198    * @return the associated thread instance.
199    */
200   public Thread getThread() {
201     return t;
202   }
203 
204   public Long get(long timeout, TimeUnit unit)
205   throws InterruptedException, ExecutionException {
206     throw new UnsupportedOperationException();
207   }
208 
209   public boolean isCancelled() {
210     throw new UnsupportedOperationException();
211   }
212 
213   synchronized boolean isDone() {
214     return this.doneSequence != NOT_DONE;
215   }
216 
217   synchronized boolean isThrowable() {
218     return isDone() && getThrowable() != null;
219   }
220 
221   synchronized Throwable getThrowable() {
222     return this.throwable;
223   }
224 }