1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.client;
19
20 import java.io.IOException;
21 import java.util.Map.Entry;
22 import java.util.concurrent.ConcurrentHashMap;
23 import java.util.concurrent.ConcurrentMap;
24
25 import org.apache.commons.lang.mutable.MutableBoolean;
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28 import org.apache.hadoop.conf.Configuration;
29 import org.apache.hadoop.hbase.HConstants;
30 import org.apache.hadoop.hbase.ServerName;
31 import org.apache.hadoop.hbase.classification.InterfaceAudience;
32 import org.apache.hadoop.hbase.exceptions.ClientExceptionsUtil;
33 import org.apache.hadoop.hbase.exceptions.PreemptiveFastFailException;
34 import org.apache.hadoop.hbase.ipc.CallTimeoutException;
35 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
36 import org.apache.hadoop.ipc.RemoteException;
37
38 import com.google.common.annotations.VisibleForTesting;
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67 @InterfaceAudience.Private
68 class PreemptiveFastFailInterceptor extends RetryingCallerInterceptor {
69
70 private static final Log LOG = LogFactory
71 .getLog(PreemptiveFastFailInterceptor.class);
72
73
74
75 protected final long fastFailThresholdMilliSec;
76
77
78
79 protected final ConcurrentMap<ServerName, FailureInfo> repeatedFailuresMap =
80 new ConcurrentHashMap<ServerName, FailureInfo>();
81
82
83
84
85 protected final long failureMapCleanupIntervalMilliSec;
86
87 protected volatile long lastFailureMapCleanupTimeMilliSec;
88
89
90
91
92 private long fastFailClearingTimeMilliSec;
93
94 private final ThreadLocal<MutableBoolean> threadRetryingInFastFailMode =
95 new ThreadLocal<MutableBoolean>();
96
97 public PreemptiveFastFailInterceptor(Configuration conf) {
98 this.fastFailThresholdMilliSec = conf.getLong(
99 HConstants.HBASE_CLIENT_FAST_FAIL_THREASHOLD_MS,
100 HConstants.HBASE_CLIENT_FAST_FAIL_THREASHOLD_MS_DEFAULT);
101 this.failureMapCleanupIntervalMilliSec = conf.getLong(
102 HConstants.HBASE_CLIENT_FAST_FAIL_CLEANUP_MS_DURATION_MS,
103 HConstants.HBASE_CLIENT_FAST_FAIL_CLEANUP_DURATION_MS_DEFAULT);
104 lastFailureMapCleanupTimeMilliSec = EnvironmentEdgeManager.currentTime();
105 }
106
107 public void intercept(FastFailInterceptorContext context)
108 throws PreemptiveFastFailException {
109 context.setFailureInfo(repeatedFailuresMap.get(context.getServer()));
110 if (inFastFailMode(context.getServer()) && !currentThreadInFastFailMode()) {
111
112
113 context.setRetryDespiteFastFailMode(shouldRetryInspiteOfFastFail(context
114 .getFailureInfo()));
115 if (!context.isRetryDespiteFastFailMode()) {
116 LOG.debug("Throwing PFFE : " + context.getFailureInfo() + " tries : "
117 + context.getTries());
118 throw new PreemptiveFastFailException(
119 context.getFailureInfo().numConsecutiveFailures.get(),
120 context.getFailureInfo().timeOfFirstFailureMilliSec,
121 context.getFailureInfo().timeOfLatestAttemptMilliSec, context.getServer(),
122 context.getGuaranteedClientSideOnly().isTrue());
123 }
124 }
125 context.setDidTry(true);
126 }
127
128 public void handleFailure(FastFailInterceptorContext context,
129 Throwable t) throws IOException {
130 handleThrowable(t, context.getServer(),
131 context.getCouldNotCommunicateWithServer(),
132 context.getGuaranteedClientSideOnly());
133 }
134
135 public void updateFailureInfo(FastFailInterceptorContext context) {
136 updateFailureInfoForServer(context.getServer(), context.getFailureInfo(),
137 context.didTry(), context.getCouldNotCommunicateWithServer()
138 .booleanValue(), context.isRetryDespiteFastFailMode());
139 }
140
141
142
143
144
145
146
147
148
149
150
151
152 @VisibleForTesting
153 protected void handleFailureToServer(ServerName serverName, Throwable t) {
154 if (serverName == null || t == null) {
155 return;
156 }
157 long currentTime = EnvironmentEdgeManager.currentTime();
158 FailureInfo fInfo = repeatedFailuresMap.get(serverName);
159 if (fInfo == null) {
160 fInfo = new FailureInfo(currentTime);
161 FailureInfo oldfInfo = repeatedFailuresMap.putIfAbsent(serverName, fInfo);
162
163 if (oldfInfo != null) {
164 fInfo = oldfInfo;
165 }
166 }
167 fInfo.timeOfLatestAttemptMilliSec = currentTime;
168 fInfo.numConsecutiveFailures.incrementAndGet();
169 }
170
171 public void handleThrowable(Throwable t1, ServerName serverName,
172 MutableBoolean couldNotCommunicateWithServer,
173 MutableBoolean guaranteedClientSideOnly) throws IOException {
174 Throwable t2 = ClientExceptionsUtil.translatePFFE(t1);
175 boolean isLocalException = !(t2 instanceof RemoteException);
176
177 if ((isLocalException && ClientExceptionsUtil.isConnectionException(t2))) {
178 couldNotCommunicateWithServer.setValue(true);
179 guaranteedClientSideOnly.setValue(!(t2 instanceof CallTimeoutException));
180 handleFailureToServer(serverName, t2);
181 }
182 }
183
184
185
186
187
188
189
190
191
192 protected void occasionallyCleanupFailureInformation() {
193 long now = System.currentTimeMillis();
194 if (!(now > lastFailureMapCleanupTimeMilliSec
195 + failureMapCleanupIntervalMilliSec))
196 return;
197
198
199
200
201 StringBuilder sb = new StringBuilder();
202 for (Entry<ServerName, FailureInfo> entry : repeatedFailuresMap.entrySet()) {
203 if (now > entry.getValue().timeOfLatestAttemptMilliSec
204 + failureMapCleanupIntervalMilliSec) {
205 repeatedFailuresMap.remove(entry.getKey());
206 } else if (now > entry.getValue().timeOfFirstFailureMilliSec
207 + this.fastFailClearingTimeMilliSec) {
208
209 LOG.error(entry.getKey()
210 + " been failing for a long time. clearing out."
211 + entry.getValue().toString());
212 repeatedFailuresMap.remove(entry.getKey());
213 } else {
214 sb.append(entry.getKey().toString()).append(" failing ")
215 .append(entry.getValue().toString()).append("\n");
216 }
217 }
218 if (sb.length() > 0) {
219 LOG.warn("Preemptive failure enabled for : " + sb.toString());
220 }
221 lastFailureMapCleanupTimeMilliSec = now;
222 }
223
224
225
226
227
228
229
230
231
232
233 private boolean inFastFailMode(ServerName server) {
234 FailureInfo fInfo = repeatedFailuresMap.get(server);
235
236
237
238 return (fInfo != null &&
239 EnvironmentEdgeManager.currentTime() >
240 (fInfo.timeOfFirstFailureMilliSec + this.fastFailThresholdMilliSec));
241 }
242
243
244
245
246
247
248
249 private boolean currentThreadInFastFailMode() {
250 return (this.threadRetryingInFastFailMode.get() != null && (this.threadRetryingInFastFailMode
251 .get().booleanValue() == true));
252 }
253
254
255
256
257
258
259
260
261
262
263
264
265 protected boolean shouldRetryInspiteOfFastFail(FailureInfo fInfo) {
266
267
268
269
270 if (fInfo != null
271 && fInfo.exclusivelyRetringInspiteOfFastFail.compareAndSet(false, true)) {
272 MutableBoolean threadAlreadyInFF = this.threadRetryingInFastFailMode
273 .get();
274 if (threadAlreadyInFF == null) {
275 threadAlreadyInFF = new MutableBoolean();
276 this.threadRetryingInFastFailMode.set(threadAlreadyInFF);
277 }
278 threadAlreadyInFF.setValue(true);
279 return true;
280 } else {
281 return false;
282 }
283 }
284
285
286
287
288
289
290
291
292
293
294
295 private void updateFailureInfoForServer(ServerName server,
296 FailureInfo fInfo, boolean didTry, boolean couldNotCommunicate,
297 boolean retryDespiteFastFailMode) {
298 if (server == null || fInfo == null || didTry == false)
299 return;
300
301
302
303 if (couldNotCommunicate == false) {
304 LOG.info("Clearing out PFFE for server " + server);
305 repeatedFailuresMap.remove(server);
306 } else {
307
308 long currentTime = System.currentTimeMillis();
309 fInfo.timeOfLatestAttemptMilliSec = currentTime;
310
311
312 if (retryDespiteFastFailMode) {
313 fInfo.exclusivelyRetringInspiteOfFastFail.set(false);
314 threadRetryingInFastFailMode.get().setValue(false);
315 }
316 }
317
318 occasionallyCleanupFailureInformation();
319 }
320
321 @Override
322 public void intercept(RetryingCallerInterceptorContext context)
323 throws PreemptiveFastFailException {
324 if (context instanceof FastFailInterceptorContext) {
325 intercept((FastFailInterceptorContext) context);
326 }
327 }
328
329 @Override
330 public void handleFailure(RetryingCallerInterceptorContext context,
331 Throwable t) throws IOException {
332 if (context instanceof FastFailInterceptorContext) {
333 handleFailure((FastFailInterceptorContext) context, t);
334 }
335 }
336
337 @Override
338 public void updateFailureInfo(RetryingCallerInterceptorContext context) {
339 if (context instanceof FastFailInterceptorContext) {
340 updateFailureInfo((FastFailInterceptorContext) context);
341 }
342 }
343
344 @Override
345 public RetryingCallerInterceptorContext createEmptyContext() {
346 return new FastFailInterceptorContext();
347 }
348
349 protected boolean isServerInFailureMap(ServerName serverName) {
350 return this.repeatedFailuresMap.containsKey(serverName);
351 }
352
353 @Override
354 public String toString() {
355 return "PreemptiveFastFailInterceptor";
356 }
357 }