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.Closeable;
21 import java.io.IOException;
22 import java.util.Collection;
23 import java.util.concurrent.ConcurrentMap;
24 import java.util.concurrent.ExecutorService;
25 import java.util.concurrent.Future;
26 import java.util.concurrent.RejectedExecutionException;
27 import java.util.concurrent.SynchronousQueue;
28 import java.util.concurrent.ThreadPoolExecutor;
29 import java.util.concurrent.TimeUnit;
30
31 import org.apache.commons.logging.Log;
32 import org.apache.commons.logging.LogFactory;
33 import org.apache.hadoop.classification.InterfaceAudience;
34 import org.apache.hadoop.classification.InterfaceStability;
35 import org.apache.hadoop.hbase.DaemonThreadFactory;
36 import org.apache.hadoop.hbase.errorhandling.ForeignException;
37
38 import com.google.common.collect.MapMaker;
39
40
41
42
43
44
45
46
47
48
49 @InterfaceAudience.Public
50 @InterfaceStability.Evolving
51 public class ProcedureMember implements Closeable {
52 private static final Log LOG = LogFactory.getLog(ProcedureMember.class);
53
54 final static long KEEP_ALIVE_MILLIS_DEFAULT = 5000;
55
56 private final SubprocedureFactory builder;
57 private final ProcedureMemberRpcs rpcs;
58
59 private final ConcurrentMap<String,Subprocedure> subprocs =
60 new MapMaker().concurrencyLevel(4).weakValues().makeMap();
61 private final ExecutorService pool;
62
63
64
65
66
67
68
69
70 public ProcedureMember(ProcedureMemberRpcs rpcs, ThreadPoolExecutor pool,
71 SubprocedureFactory factory) {
72 this.pool = pool;
73 this.rpcs = rpcs;
74 this.builder = factory;
75 }
76
77
78
79
80
81
82
83 public static ThreadPoolExecutor defaultPool(String memberName, int procThreads) {
84 return defaultPool(memberName, procThreads, KEEP_ALIVE_MILLIS_DEFAULT);
85 }
86
87
88
89
90
91
92
93
94 public static ThreadPoolExecutor defaultPool(String memberName, int procThreads,
95 long keepAliveMillis) {
96 return new ThreadPoolExecutor(1, procThreads, keepAliveMillis, TimeUnit.MILLISECONDS,
97 new SynchronousQueue<Runnable>(),
98 new DaemonThreadFactory("member: '" + memberName + "' subprocedure-pool"));
99 }
100
101
102
103
104
105
106 ProcedureMemberRpcs getRpcs() {
107 return rpcs;
108 }
109
110
111
112
113
114
115
116
117
118
119 public Subprocedure createSubprocedure(String opName, byte[] data) {
120 return builder.buildSubprocedure(opName, data);
121 }
122
123
124
125
126
127
128
129
130 public boolean submitSubprocedure(Subprocedure subproc) {
131
132 if (subproc == null) {
133 LOG.warn("Submitted null subprocedure, nothing to run here.");
134 return false;
135 }
136
137 String procName = subproc.getName();
138 if (procName == null || procName.length() == 0) {
139 LOG.error("Subproc name cannot be null or the empty string");
140 return false;
141 }
142
143
144 Subprocedure rsub;
145 synchronized (subprocs) {
146 rsub = subprocs.get(procName);
147 }
148 if (rsub != null) {
149 if (!rsub.isComplete()) {
150 LOG.error("Subproc '" + procName + "' is already running. Bailing out");
151 return false;
152 }
153 LOG.warn("A completed old subproc " + procName + " is still present, removing");
154 subprocs.remove(procName);
155 }
156
157 LOG.debug("Submitting new Subprocedure:" + procName);
158
159
160 Future<Void> future = null;
161 try {
162 future = this.pool.submit(subproc);
163 synchronized (subprocs) {
164 subprocs.put(procName, subproc);
165 }
166 return true;
167 } catch (RejectedExecutionException e) {
168
169 String msg = "Subprocedure pool is full!";
170 subproc.cancel(msg, e.getCause());
171
172
173 if (future != null) {
174 future.cancel(true);
175 }
176 }
177
178 LOG.error("Failed to start subprocedure '" + procName + "'");
179 return false;
180 }
181
182
183
184
185
186 public void receivedReachedGlobalBarrier(String procName) {
187 Subprocedure subproc = subprocs.get(procName);
188 if (subproc == null) {
189 LOG.warn("Unexpected reached glabal barrier message for Sub-Procedure '" + procName + "'");
190 return;
191 }
192 subproc.receiveReachedGlobalBarrier();
193 }
194
195
196
197
198 @Override
199 public void close() throws IOException {
200
201 pool.shutdownNow();
202 }
203
204
205
206
207
208
209
210 boolean closeAndWait(long timeoutMs) throws InterruptedException {
211 pool.shutdown();
212 return pool.awaitTermination(timeoutMs, TimeUnit.MILLISECONDS);
213 }
214
215
216
217
218
219
220
221
222
223
224
225 public void controllerConnectionFailure(final String message, final IOException cause) {
226 Collection<Subprocedure> toNotify = subprocs.values();
227 LOG.error(message, cause);
228 for (Subprocedure sub : toNotify) {
229
230 sub.cancel(message, cause);
231 }
232 }
233
234
235
236
237
238
239 public void receiveAbortProcedure(String procName, ForeignException ee) {
240 LOG.debug("Request received to abort procedure " + procName, ee);
241
242 Subprocedure sub = subprocs.get(procName);
243 if (sub == null) {
244 LOG.info("Received abort on procedure with no local subprocedure " + procName +
245 ", ignoring it.", ee);
246 return;
247 }
248 LOG.error("Propagating foreign exception to subprocedure " + sub.getName(), ee);
249 sub.monitor.receive(ee);
250 }
251 }