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