1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase;
20
21 import java.util.ArrayList;
22 import java.util.HashMap;
23 import java.util.LinkedHashMap;
24 import java.util.Map.Entry;
25 import java.util.concurrent.ScheduledFuture;
26 import java.util.concurrent.ScheduledThreadPoolExecutor;
27 import java.util.concurrent.ThreadFactory;
28 import java.util.concurrent.atomic.AtomicInteger;
29
30 import com.google.common.annotations.VisibleForTesting;
31 import org.apache.commons.logging.Log;
32 import org.apache.commons.logging.LogFactory;
33 import org.apache.hadoop.hbase.ScheduledChore.ChoreServicer;
34 import org.apache.hadoop.hbase.classification.InterfaceAudience;
35 import org.apache.hadoop.hbase.classification.InterfaceStability;
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56 @InterfaceAudience.Public
57 @InterfaceStability.Stable
58 public class ChoreService implements ChoreServicer {
59 private static final Log LOG = LogFactory.getLog(ChoreService.class);
60
61
62
63
64 @InterfaceAudience.Private
65 public final static int MIN_CORE_POOL_SIZE = 1;
66
67
68
69
70 private final ScheduledThreadPoolExecutor scheduler;
71
72
73
74
75 private final HashMap<ScheduledChore, ScheduledFuture<?>> scheduledChores;
76
77
78
79
80
81
82
83 private final HashMap<ScheduledChore, Boolean> choresMissingStartTime;
84
85
86
87
88
89
90
91 private final String coreThreadPoolPrefix;
92
93
94
95
96
97
98 @InterfaceAudience.Private
99 @VisibleForTesting
100 public ChoreService(final String coreThreadPoolPrefix) {
101 this(coreThreadPoolPrefix, MIN_CORE_POOL_SIZE, false);
102 }
103
104
105
106
107
108
109
110 public ChoreService(final String coreThreadPoolPrefix, final boolean jitter) {
111 this(coreThreadPoolPrefix, MIN_CORE_POOL_SIZE, jitter);
112 }
113
114
115
116
117
118
119
120
121
122
123 public ChoreService(final String coreThreadPoolPrefix, int corePoolSize, boolean jitter) {
124 this.coreThreadPoolPrefix = coreThreadPoolPrefix;
125 if (corePoolSize < MIN_CORE_POOL_SIZE) {
126 corePoolSize = MIN_CORE_POOL_SIZE;
127 }
128
129 final ThreadFactory threadFactory = new ChoreServiceThreadFactory(coreThreadPoolPrefix);
130 if (jitter) {
131 scheduler = new JitterScheduledThreadPoolExecutorImpl(corePoolSize, threadFactory, 0.1);
132 } else {
133 scheduler = new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
134 }
135
136 scheduler.setRemoveOnCancelPolicy(true);
137 scheduledChores = new HashMap<ScheduledChore, ScheduledFuture<?>>();
138 choresMissingStartTime = new HashMap<ScheduledChore, Boolean>();
139 }
140
141
142
143
144
145
146
147
148 public synchronized boolean scheduleChore(ScheduledChore chore) {
149 if (chore == null) {
150 return false;
151 }
152
153 try {
154 chore.setChoreServicer(this);
155 ScheduledFuture<?> future =
156 scheduler.scheduleAtFixedRate(chore, chore.getInitialDelay(), chore.getPeriod(),
157 chore.getTimeUnit());
158 scheduledChores.put(chore, future);
159 return true;
160 } catch (Exception exception) {
161 if (LOG.isInfoEnabled()) {
162 LOG.info("Could not successfully schedule chore: " + chore.getName());
163 }
164 return false;
165 }
166 }
167
168
169
170
171
172 private synchronized void rescheduleChore(ScheduledChore chore) {
173 if (chore == null) return;
174
175 if (scheduledChores.containsKey(chore)) {
176 ScheduledFuture<?> future = scheduledChores.get(chore);
177 future.cancel(false);
178 }
179 scheduleChore(chore);
180 }
181
182 @InterfaceAudience.Private
183 @Override
184 public synchronized void cancelChore(ScheduledChore chore) {
185 cancelChore(chore, true);
186 }
187
188 @InterfaceAudience.Private
189 @Override
190 public synchronized void cancelChore(ScheduledChore chore, boolean mayInterruptIfRunning) {
191 if (chore != null && scheduledChores.containsKey(chore)) {
192 ScheduledFuture<?> future = scheduledChores.get(chore);
193 future.cancel(mayInterruptIfRunning);
194 scheduledChores.remove(chore);
195
196
197
198 if (choresMissingStartTime.containsKey(chore)) {
199 choresMissingStartTime.remove(chore);
200 requestCorePoolDecrease();
201 }
202 }
203 }
204
205 @InterfaceAudience.Private
206 @Override
207 public synchronized boolean isChoreScheduled(ScheduledChore chore) {
208 return chore != null && scheduledChores.containsKey(chore)
209 && !scheduledChores.get(chore).isDone();
210 }
211
212 @InterfaceAudience.Private
213 @Override
214 public synchronized boolean triggerNow(ScheduledChore chore) {
215 if (chore == null) {
216 return false;
217 } else {
218 rescheduleChore(chore);
219 return true;
220 }
221 }
222
223
224
225
226 int getNumberOfScheduledChores() {
227 return scheduledChores.size();
228 }
229
230
231
232
233
234 int getNumberOfChoresMissingStartTime() {
235 return choresMissingStartTime.size();
236 }
237
238
239
240
241 int getCorePoolSize() {
242 return scheduler.getCorePoolSize();
243 }
244
245
246
247
248
249 static class ChoreServiceThreadFactory implements ThreadFactory {
250 private final String threadPrefix;
251 private final static String THREAD_NAME_SUFFIX = "_ChoreService_";
252 private AtomicInteger threadNumber = new AtomicInteger(1);
253
254
255
256
257 public ChoreServiceThreadFactory(final String threadPrefix) {
258 this.threadPrefix = threadPrefix;
259 }
260
261 @Override
262 public Thread newThread(Runnable r) {
263 Thread thread =
264 new Thread(r, threadPrefix + THREAD_NAME_SUFFIX + threadNumber.getAndIncrement());
265 thread.setDaemon(true);
266 return thread;
267 }
268 }
269
270
271
272
273
274
275
276 private synchronized boolean requestCorePoolIncrease() {
277
278
279
280 if (scheduler.getCorePoolSize() < scheduledChores.size()) {
281 scheduler.setCorePoolSize(scheduler.getCorePoolSize() + 1);
282 printChoreServiceDetails("requestCorePoolIncrease");
283 return true;
284 }
285 return false;
286 }
287
288
289
290
291
292
293 private synchronized void requestCorePoolDecrease() {
294 if (scheduler.getCorePoolSize() > MIN_CORE_POOL_SIZE) {
295 scheduler.setCorePoolSize(scheduler.getCorePoolSize() - 1);
296 printChoreServiceDetails("requestCorePoolDecrease");
297 }
298 }
299
300 @InterfaceAudience.Private
301 @Override
302 public synchronized void onChoreMissedStartTime(ScheduledChore chore) {
303 if (chore == null || !scheduledChores.containsKey(chore)) return;
304
305
306
307
308 if (!choresMissingStartTime.containsKey(chore) || !choresMissingStartTime.get(chore)) {
309 choresMissingStartTime.put(chore, requestCorePoolIncrease());
310 }
311
312
313
314
315
316 rescheduleChore(chore);
317 printChoreDetails("onChoreMissedStartTime", chore);
318 }
319
320
321
322
323
324
325 public synchronized void shutdown() {
326 scheduler.shutdownNow();
327 if (LOG.isInfoEnabled()) {
328 LOG.info("Chore service for: " + coreThreadPoolPrefix + " had " + scheduledChores.keySet()
329 + " on shutdown");
330 }
331 cancelAllChores(true);
332 scheduledChores.clear();
333 choresMissingStartTime.clear();
334 }
335
336
337
338
339 public boolean isShutdown() {
340 return scheduler.isShutdown();
341 }
342
343
344
345
346 public boolean isTerminated() {
347 return scheduler.isTerminated();
348 }
349
350 private void cancelAllChores(final boolean mayInterruptIfRunning) {
351 ArrayList<ScheduledChore> choresToCancel = new ArrayList<ScheduledChore>();
352
353
354
355 for (ScheduledChore chore : scheduledChores.keySet()) {
356 choresToCancel.add(chore);
357 }
358 for (ScheduledChore chore : choresToCancel) {
359 cancelChore(chore, mayInterruptIfRunning);
360 }
361 choresToCancel.clear();
362 }
363
364
365
366
367 private void printChoreDetails(final String header, ScheduledChore chore) {
368 LinkedHashMap<String, String> output = new LinkedHashMap<String, String>();
369 output.put(header, "");
370 output.put("Chore name: ", chore.getName());
371 output.put("Chore period: ", Integer.toString(chore.getPeriod()));
372 output.put("Chore timeBetweenRuns: ", Long.toString(chore.getTimeBetweenRuns()));
373
374 for (Entry<String, String> entry : output.entrySet()) {
375 if (LOG.isTraceEnabled()) LOG.trace(entry.getKey() + entry.getValue());
376 }
377 }
378
379
380
381
382 private void printChoreServiceDetails(final String header) {
383 LinkedHashMap<String, String> output = new LinkedHashMap<String, String>();
384 output.put(header, "");
385 output.put("ChoreService corePoolSize: ", Integer.toString(getCorePoolSize()));
386 output.put("ChoreService scheduledChores: ", Integer.toString(getNumberOfScheduledChores()));
387 output.put("ChoreService missingStartTimeCount: ",
388 Integer.toString(getNumberOfChoresMissingStartTime()));
389
390 for (Entry<String, String> entry : output.entrySet()) {
391 if (LOG.isTraceEnabled()) LOG.trace(entry.getKey() + entry.getValue());
392 }
393 }
394 }