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 }