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