1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.procedure;
19
20 import com.google.common.collect.MapMaker;
21 import java.io.IOException;
22 import java.util.Collection;
23 import java.util.HashSet;
24 import java.util.List;
25 import java.util.Set;
26 import java.util.concurrent.ConcurrentMap;
27 import java.util.concurrent.ExecutorService;
28 import java.util.concurrent.RejectedExecutionException;
29 import java.util.concurrent.SynchronousQueue;
30 import java.util.concurrent.ThreadPoolExecutor;
31 import java.util.concurrent.TimeUnit;
32 import org.apache.commons.logging.Log;
33 import org.apache.commons.logging.LogFactory;
34 import org.apache.hadoop.hbase.DaemonThreadFactory;
35 import org.apache.hadoop.hbase.classification.InterfaceAudience;
36 import org.apache.hadoop.hbase.errorhandling.ForeignException;
37 import org.apache.hadoop.hbase.errorhandling.ForeignExceptionDispatcher;
38
39
40
41
42
43
44
45 @InterfaceAudience.Private
46 public class ProcedureCoordinator {
47 private static final Log LOG = LogFactory.getLog(ProcedureCoordinator.class);
48
49 final static long KEEP_ALIVE_MILLIS_DEFAULT = 5000;
50 final static long TIMEOUT_MILLIS_DEFAULT = 60000;
51 final static long WAKE_MILLIS_DEFAULT = 500;
52
53 private final ProcedureCoordinatorRpcs rpcs;
54 private final ExecutorService pool;
55 private final long wakeTimeMillis;
56 private final long timeoutMillis;
57
58
59 private final ConcurrentMap<String, Procedure> procedures =
60 new MapMaker().concurrencyLevel(4).weakValues().makeMap();
61
62
63
64
65
66
67
68
69
70
71 public ProcedureCoordinator(ProcedureCoordinatorRpcs rpcs, ThreadPoolExecutor pool) {
72 this(rpcs, pool, TIMEOUT_MILLIS_DEFAULT, WAKE_MILLIS_DEFAULT);
73 }
74
75
76
77
78
79
80
81
82
83
84
85 public ProcedureCoordinator(ProcedureCoordinatorRpcs rpcs, ThreadPoolExecutor pool,
86 long timeoutMillis, long wakeTimeMillis) {
87 this.timeoutMillis = timeoutMillis;
88 this.wakeTimeMillis = wakeTimeMillis;
89 this.rpcs = rpcs;
90 this.pool = pool;
91 this.rpcs.start(this);
92 }
93
94
95
96
97
98
99
100 public static ThreadPoolExecutor defaultPool(String coordName, int opThreads) {
101 return defaultPool(coordName, opThreads, KEEP_ALIVE_MILLIS_DEFAULT);
102 }
103
104
105
106
107
108
109
110
111 public static ThreadPoolExecutor defaultPool(String coordName, int opThreads,
112 long keepAliveMillis) {
113 return new ThreadPoolExecutor(1, opThreads, keepAliveMillis, TimeUnit.MILLISECONDS,
114 new SynchronousQueue<Runnable>(),
115 new DaemonThreadFactory("(" + coordName + ")-proc-coordinator-pool"));
116 }
117
118
119
120
121
122 public void close() throws IOException {
123
124 pool.shutdownNow();
125 rpcs.close();
126 }
127
128
129
130
131
132
133
134
135
136
137 boolean submitProcedure(Procedure proc) {
138
139 if (proc == null) {
140 return false;
141 }
142 String procName = proc.getName();
143
144
145 Procedure oldProc = procedures.get(procName);
146 if (oldProc != null) {
147
148 try {
149 if (!oldProc.isCompleted()) {
150 LOG.warn("Procedure " + procName + " currently running. Rejecting new request");
151 return false;
152 } else {
153 LOG.debug("Procedure " + procName
154 + " was in running list but was completed. Accepting new attempt.");
155 if (!procedures.remove(procName, oldProc)) {
156 LOG.warn("Procedure " + procName
157 + " has been resubmitted by another thread. Rejecting this request.");
158 return false;
159 }
160 }
161 } catch (ForeignException e) {
162 LOG.debug("Procedure " + procName
163 + " was in running list but has exception. Accepting new attempt.");
164 if (!procedures.remove(procName, oldProc)) {
165 LOG.warn("Procedure " + procName
166 + " has been resubmitted by another thread. Rejecting this request.");
167 return false;
168 }
169 }
170 }
171
172
173 try {
174 if (this.procedures.putIfAbsent(procName, proc) == null) {
175 LOG.debug("Submitting procedure " + procName);
176 this.pool.submit(proc);
177 return true;
178 } else {
179 LOG.error("Another thread has submitted procedure '" + procName + "'. Ignoring this attempt.");
180 return false;
181 }
182 } catch (RejectedExecutionException e) {
183 LOG.warn("Procedure " + procName + " rejected by execution pool. Propagating error.", e);
184
185 this.procedures.remove(procName, proc);
186
187 proc.receive(new ForeignException(procName, e));
188 }
189 return false;
190 }
191
192
193
194
195
196
197
198
199 void rpcConnectionFailure(final String message, final IOException cause) {
200 Collection<Procedure> toNotify = procedures.values();
201
202 boolean isTraceEnabled = LOG.isTraceEnabled();
203 LOG.debug("received connection failure: " + message, cause);
204 for (Procedure proc : toNotify) {
205 if (proc == null) {
206 continue;
207 }
208
209 if (isTraceEnabled) {
210 LOG.trace("connection failure - notify procedure: " + proc.getName());
211 }
212 proc.receive(new ForeignException(proc.getName(), cause));
213 }
214 }
215
216
217
218
219
220
221 public void abortProcedure(String procName, ForeignException reason) {
222 LOG.debug("abort procedure " + procName, reason);
223
224 Procedure proc = procedures.get(procName);
225 if (proc == null) {
226 return;
227 }
228 proc.receive(reason);
229 }
230
231
232
233
234
235
236
237
238 Procedure createProcedure(ForeignExceptionDispatcher fed, String procName, byte[] procArgs,
239 List<String> expectedMembers) {
240
241 return new Procedure(this, fed, wakeTimeMillis, timeoutMillis,
242 procName, procArgs, expectedMembers);
243 }
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258 public Procedure startProcedure(ForeignExceptionDispatcher fed, String procName, byte[] procArgs,
259 List<String> expectedMembers) {
260 Procedure proc = createProcedure(fed, procName, procArgs, expectedMembers);
261 if (!this.submitProcedure(proc)) {
262 LOG.error("Failed to submit procedure '" + procName + "'");
263 return null;
264 }
265 return proc;
266 }
267
268
269
270
271
272
273
274 void memberAcquiredBarrier(String procName, final String member) {
275 Procedure proc = procedures.get(procName);
276 if (proc == null) {
277 LOG.warn("Member '"+ member +"' is trying to acquire an unknown procedure '"+ procName +"'");
278 return;
279 }
280 if (LOG.isTraceEnabled()) {
281 LOG.trace("Member '"+ member +"' acquired procedure '"+ procName +"'");
282 }
283 proc.barrierAcquiredByMember(member);
284 }
285
286
287
288
289
290
291
292
293 void memberFinishedBarrier(String procName, final String member, byte[] dataFromMember) {
294 Procedure proc = procedures.get(procName);
295 if (proc == null) {
296 LOG.warn("Member '"+ member +"' is trying to release an unknown procedure '"+ procName +"'");
297 return;
298 }
299 if (LOG.isTraceEnabled()) {
300 LOG.trace("Member '"+ member +"' released procedure '"+ procName +"'");
301 }
302 proc.barrierReleasedByMember(member, dataFromMember);
303 }
304
305
306
307
308 ProcedureCoordinatorRpcs getRpcs() {
309 return rpcs;
310 }
311
312
313
314
315
316
317
318 public Procedure getProcedure(String name) {
319 return procedures.get(name);
320 }
321
322
323
324
325 public Set<String> getProcedureNames() {
326 return new HashSet<String>(procedures.keySet());
327 }
328 }