001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.assertTrue; 023import static org.mockito.Mockito.atLeastOnce; 024import static org.mockito.Mockito.never; 025import static org.mockito.Mockito.spy; 026import static org.mockito.Mockito.verify; 027 028import java.util.concurrent.TimeUnit; 029import org.apache.hadoop.conf.Configuration; 030import org.apache.hadoop.hbase.testclassification.MediumTests; 031import org.apache.hadoop.hbase.testclassification.MiscTests; 032import org.apache.hadoop.hbase.util.Threads; 033import org.junit.After; 034import org.junit.Before; 035import org.junit.ClassRule; 036import org.junit.Rule; 037import org.junit.Test; 038import org.junit.experimental.categories.Category; 039import org.junit.rules.TestName; 040import org.slf4j.Logger; 041import org.slf4j.LoggerFactory; 042 043@Category({ MiscTests.class, MediumTests.class }) 044public class TestChoreService { 045 046 @ClassRule 047 public static final HBaseClassTestRule CLASS_RULE = 048 HBaseClassTestRule.forClass(TestChoreService.class); 049 050 private static final Logger LOG = LoggerFactory.getLogger(TestChoreService.class); 051 052 private static final Configuration CONF = HBaseConfiguration.create(); 053 054 @Rule 055 public TestName name = new TestName(); 056 057 private int initialCorePoolSize = 3; 058 059 private ChoreService service; 060 061 @Before 062 public void setUp() { 063 service = new ChoreService(name.getMethodName(), initialCorePoolSize, false); 064 } 065 066 @After 067 public void tearDown() { 068 shutdownService(service); 069 } 070 071 /** 072 * Straight forward stopper implementation that is used by default when one is not provided 073 */ 074 private static class SampleStopper implements Stoppable { 075 private boolean stopped = false; 076 077 @Override 078 public void stop(String why) { 079 stopped = true; 080 } 081 082 @Override 083 public boolean isStopped() { 084 return stopped; 085 } 086 } 087 088 /** 089 * Sleeps for longer than the scheduled period. This chore always misses its scheduled periodic 090 * executions 091 */ 092 private static class SlowChore extends ScheduledChore { 093 public SlowChore(String name, int period) { 094 this(name, new SampleStopper(), period); 095 } 096 097 public SlowChore(String name, Stoppable stopper, int period) { 098 super(name, stopper, period); 099 } 100 101 @Override 102 protected boolean initialChore() { 103 Threads.sleep(getPeriod() * 2); 104 return true; 105 } 106 107 @Override 108 protected void chore() { 109 Threads.sleep(getPeriod() * 2); 110 } 111 } 112 113 /** 114 * Lightweight ScheduledChore used primarily to fill the scheduling queue in tests 115 */ 116 private static class DoNothingChore extends ScheduledChore { 117 118 public DoNothingChore(String name, int period) { 119 super(name, new SampleStopper(), period); 120 } 121 122 public DoNothingChore(String name, Stoppable stopper, int period) { 123 super(name, stopper, period); 124 } 125 126 @Override 127 protected void chore() { 128 // DO NOTHING 129 } 130 } 131 132 private static class SleepingChore extends ScheduledChore { 133 private int sleepTime; 134 135 public SleepingChore(String name, int chorePeriod, int sleepTime) { 136 this(name, new SampleStopper(), chorePeriod, sleepTime); 137 } 138 139 public SleepingChore(String name, Stoppable stopper, int period, int sleepTime) { 140 super(name, stopper, period); 141 this.sleepTime = sleepTime; 142 } 143 144 @Override 145 protected boolean initialChore() { 146 Threads.sleep(sleepTime); 147 return true; 148 } 149 150 @Override 151 protected void chore() { 152 Threads.sleep(sleepTime); 153 } 154 } 155 156 private static class CountingChore extends ScheduledChore { 157 private int countOfChoreCalls; 158 private boolean outputOnTicks = false; 159 160 public CountingChore(String name, int period) { 161 this(name, new SampleStopper(), period); 162 } 163 164 public CountingChore(String name, Stoppable stopper, int period) { 165 this(name, stopper, period, false); 166 } 167 168 public CountingChore(String name, Stoppable stopper, int period, final boolean outputOnTicks) { 169 super(name, stopper, period); 170 this.countOfChoreCalls = 0; 171 this.outputOnTicks = outputOnTicks; 172 } 173 174 @Override 175 protected boolean initialChore() { 176 countOfChoreCalls++; 177 if (outputOnTicks) { 178 outputTickCount(); 179 } 180 return true; 181 } 182 183 @Override 184 protected void chore() { 185 countOfChoreCalls++; 186 if (outputOnTicks) { 187 outputTickCount(); 188 } 189 } 190 191 private void outputTickCount() { 192 LOG.info("Chore: " + getName() + ". Count of chore calls: " + countOfChoreCalls); 193 } 194 195 public int getCountOfChoreCalls() { 196 return countOfChoreCalls; 197 } 198 } 199 200 /** 201 * A Chore that will try to execute the initial chore a few times before succeeding. Once the 202 * initial chore is complete the chore cancels itself 203 */ 204 public static class FailInitialChore extends ScheduledChore { 205 private int numberOfFailures; 206 private int failureThreshold; 207 208 /** 209 * @param failThreshold Number of times the Chore fails when trying to execute initialChore 210 * before succeeding. 211 */ 212 public FailInitialChore(String name, int period, int failThreshold) { 213 this(name, new SampleStopper(), period, failThreshold); 214 } 215 216 public FailInitialChore(String name, Stoppable stopper, int period, int failThreshold) { 217 super(name, stopper, period); 218 numberOfFailures = 0; 219 failureThreshold = failThreshold; 220 } 221 222 @Override 223 protected boolean initialChore() { 224 if (numberOfFailures < failureThreshold) { 225 numberOfFailures++; 226 return false; 227 } else { 228 return true; 229 } 230 } 231 232 @Override 233 protected void chore() { 234 assertTrue(numberOfFailures == failureThreshold); 235 cancel(false); 236 } 237 } 238 239 @Test 240 public void testInitialChorePrecedence() throws InterruptedException { 241 final int period = 100; 242 final int failureThreshold = 5; 243 ScheduledChore chore = new FailInitialChore("chore", period, failureThreshold); 244 service.scheduleChore(chore); 245 246 int loopCount = 0; 247 boolean brokeOutOfLoop = false; 248 249 while (!chore.isInitialChoreComplete() && chore.isScheduled()) { 250 Thread.sleep(failureThreshold * period); 251 loopCount++; 252 if (loopCount > 3) { 253 brokeOutOfLoop = true; 254 break; 255 } 256 } 257 258 assertFalse(brokeOutOfLoop); 259 } 260 261 @Test 262 public void testCancelChore() throws InterruptedException { 263 final int period = 100; 264 ScheduledChore chore = new DoNothingChore("chore", period); 265 service.scheduleChore(chore); 266 assertTrue(chore.isScheduled()); 267 268 chore.cancel(true); 269 assertFalse(chore.isScheduled()); 270 assertTrue(service.getNumberOfScheduledChores() == 0); 271 } 272 273 @Test 274 public void testScheduledChoreConstruction() { 275 final String NAME = "chore"; 276 final int PERIOD = 100; 277 final long VALID_DELAY = 0; 278 final long INVALID_DELAY = -100; 279 final TimeUnit UNIT = TimeUnit.NANOSECONDS; 280 281 ScheduledChore chore1 = 282 new ScheduledChore(NAME, new SampleStopper(), PERIOD, VALID_DELAY, UNIT) { 283 @Override 284 protected void chore() { 285 // DO NOTHING 286 } 287 }; 288 289 assertEquals("Name construction failed", NAME, chore1.getName()); 290 assertEquals("Period construction failed", PERIOD, chore1.getPeriod()); 291 assertEquals("Initial Delay construction failed", VALID_DELAY, chore1.getInitialDelay()); 292 assertEquals("TimeUnit construction failed", UNIT, chore1.getTimeUnit()); 293 294 ScheduledChore invalidDelayChore = 295 new ScheduledChore(NAME, new SampleStopper(), PERIOD, INVALID_DELAY, UNIT) { 296 @Override 297 protected void chore() { 298 // DO NOTHING 299 } 300 }; 301 302 assertEquals("Initial Delay should be set to 0 when invalid", 0, 303 invalidDelayChore.getInitialDelay()); 304 } 305 306 @Test 307 public void testChoreServiceConstruction() throws InterruptedException { 308 final int corePoolSize = 10; 309 final int defaultCorePoolSize = ChoreService.MIN_CORE_POOL_SIZE; 310 311 ChoreService customInit = 312 new ChoreService("testChoreServiceConstruction_custom", corePoolSize, false); 313 try { 314 assertEquals(corePoolSize, customInit.getCorePoolSize()); 315 } finally { 316 shutdownService(customInit); 317 } 318 319 ChoreService defaultInit = new ChoreService("testChoreServiceConstruction_default"); 320 try { 321 assertEquals(defaultCorePoolSize, defaultInit.getCorePoolSize()); 322 } finally { 323 shutdownService(defaultInit); 324 } 325 326 ChoreService invalidInit = new ChoreService("testChoreServiceConstruction_invalid", -10, false); 327 try { 328 assertEquals(defaultCorePoolSize, invalidInit.getCorePoolSize()); 329 } finally { 330 shutdownService(invalidInit); 331 } 332 } 333 334 @Test 335 public void testFrequencyOfChores() throws InterruptedException { 336 final int period = 100; 337 // Small delta that acts as time buffer (allowing chores to complete if running slowly) 338 final int delta = period / 5; 339 CountingChore chore = new CountingChore("countingChore", period); 340 service.scheduleChore(chore); 341 342 Thread.sleep(10 * period + delta); 343 assertEquals("10 periods have elapsed.", 11, chore.getCountOfChoreCalls()); 344 345 Thread.sleep(10 * period + delta); 346 assertEquals("20 periods have elapsed.", 21, chore.getCountOfChoreCalls()); 347 } 348 349 public void shutdownService(ChoreService service) { 350 service.shutdown(); 351 Waiter.waitFor(CONF, 1000, () -> service.isTerminated()); 352 } 353 354 @Test 355 public void testForceTrigger() throws InterruptedException { 356 final int period = 100; 357 final int delta = period / 10; 358 final CountingChore chore = new CountingChore("countingChore", period); 359 service.scheduleChore(chore); 360 Thread.sleep(10 * period + delta); 361 362 assertEquals("10 periods have elapsed.", 11, chore.getCountOfChoreCalls()); 363 364 // Force five runs of the chore to occur, sleeping between triggers to ensure the 365 // chore has time to run 366 chore.triggerNow(); 367 Thread.sleep(delta); 368 chore.triggerNow(); 369 Thread.sleep(delta); 370 chore.triggerNow(); 371 Thread.sleep(delta); 372 chore.triggerNow(); 373 Thread.sleep(delta); 374 chore.triggerNow(); 375 Thread.sleep(delta); 376 377 assertEquals("Trigger was called 5 times after 10 periods.", 16, chore.getCountOfChoreCalls()); 378 379 Thread.sleep(10 * period + delta); 380 381 // Be loosey-goosey. It used to be '26' but it was a big flakey relying on timing. 382 assertTrue("Expected at least 16 invocations, instead got " + chore.getCountOfChoreCalls(), 383 chore.getCountOfChoreCalls() > 16); 384 } 385 386 @Test 387 public void testCorePoolIncrease() throws InterruptedException { 388 assertEquals("Setting core pool size gave unexpected results.", initialCorePoolSize, 389 service.getCorePoolSize()); 390 391 final int slowChorePeriod = 100; 392 SlowChore slowChore1 = new SlowChore("slowChore1", slowChorePeriod); 393 SlowChore slowChore2 = new SlowChore("slowChore2", slowChorePeriod); 394 SlowChore slowChore3 = new SlowChore("slowChore3", slowChorePeriod); 395 396 service.scheduleChore(slowChore1); 397 service.scheduleChore(slowChore2); 398 service.scheduleChore(slowChore3); 399 400 Thread.sleep(slowChorePeriod * 10); 401 assertEquals("Should not create more pools than scheduled chores", 3, 402 service.getCorePoolSize()); 403 404 SlowChore slowChore4 = new SlowChore("slowChore4", slowChorePeriod); 405 service.scheduleChore(slowChore4); 406 407 Thread.sleep(slowChorePeriod * 10); 408 assertEquals("Chores are missing their start time. Should expand core pool size", 4, 409 service.getCorePoolSize()); 410 411 SlowChore slowChore5 = new SlowChore("slowChore5", slowChorePeriod); 412 service.scheduleChore(slowChore5); 413 414 Thread.sleep(slowChorePeriod * 10); 415 assertEquals("Chores are missing their start time. Should expand core pool size", 5, 416 service.getCorePoolSize()); 417 } 418 419 @Test 420 public void testCorePoolDecrease() throws InterruptedException { 421 final int chorePeriod = 100; 422 // Slow chores always miss their start time and thus the core pool size should be at least as 423 // large as the number of running slow chores 424 SlowChore slowChore1 = new SlowChore("slowChore1", chorePeriod); 425 SlowChore slowChore2 = new SlowChore("slowChore2", chorePeriod); 426 SlowChore slowChore3 = new SlowChore("slowChore3", chorePeriod); 427 428 service.scheduleChore(slowChore1); 429 service.scheduleChore(slowChore2); 430 service.scheduleChore(slowChore3); 431 432 Thread.sleep(chorePeriod * 10); 433 assertEquals("Should not create more pools than scheduled chores", 434 service.getNumberOfScheduledChores(), service.getCorePoolSize()); 435 436 SlowChore slowChore4 = new SlowChore("slowChore4", chorePeriod); 437 service.scheduleChore(slowChore4); 438 Thread.sleep(chorePeriod * 10); 439 assertEquals("Chores are missing their start time. Should expand core pool size", 440 service.getNumberOfScheduledChores(), service.getCorePoolSize()); 441 442 SlowChore slowChore5 = new SlowChore("slowChore5", chorePeriod); 443 service.scheduleChore(slowChore5); 444 Thread.sleep(chorePeriod * 10); 445 assertEquals("Chores are missing their start time. Should expand core pool size", 446 service.getNumberOfScheduledChores(), service.getCorePoolSize()); 447 assertEquals(5, service.getNumberOfChoresMissingStartTime()); 448 449 // Now we begin to cancel the chores that caused an increase in the core thread pool of the 450 // ChoreService. These cancellations should cause a decrease in the core thread pool. 451 slowChore5.cancel(); 452 Thread.sleep(chorePeriod * 10); 453 assertEquals(Math.max(ChoreService.MIN_CORE_POOL_SIZE, service.getNumberOfScheduledChores()), 454 service.getCorePoolSize()); 455 assertEquals(4, service.getNumberOfChoresMissingStartTime()); 456 457 slowChore4.cancel(); 458 Thread.sleep(chorePeriod * 10); 459 assertEquals(Math.max(ChoreService.MIN_CORE_POOL_SIZE, service.getNumberOfScheduledChores()), 460 service.getCorePoolSize()); 461 assertEquals(3, service.getNumberOfChoresMissingStartTime()); 462 463 slowChore3.cancel(); 464 Thread.sleep(chorePeriod * 10); 465 assertEquals(Math.max(ChoreService.MIN_CORE_POOL_SIZE, service.getNumberOfScheduledChores()), 466 service.getCorePoolSize()); 467 assertEquals(2, service.getNumberOfChoresMissingStartTime()); 468 469 slowChore2.cancel(); 470 Thread.sleep(chorePeriod * 10); 471 assertEquals(Math.max(ChoreService.MIN_CORE_POOL_SIZE, service.getNumberOfScheduledChores()), 472 service.getCorePoolSize()); 473 assertEquals(1, service.getNumberOfChoresMissingStartTime()); 474 475 slowChore1.cancel(); 476 Thread.sleep(chorePeriod * 10); 477 assertEquals(Math.max(ChoreService.MIN_CORE_POOL_SIZE, service.getNumberOfScheduledChores()), 478 service.getCorePoolSize()); 479 assertEquals(0, service.getNumberOfChoresMissingStartTime()); 480 } 481 482 @Test 483 public void testNumberOfRunningChores() throws InterruptedException { 484 final int period = 100; 485 final int sleepTime = 5; 486 DoNothingChore dn1 = new DoNothingChore("dn1", period); 487 DoNothingChore dn2 = new DoNothingChore("dn2", period); 488 DoNothingChore dn3 = new DoNothingChore("dn3", period); 489 DoNothingChore dn4 = new DoNothingChore("dn4", period); 490 DoNothingChore dn5 = new DoNothingChore("dn5", period); 491 492 service.scheduleChore(dn1); 493 service.scheduleChore(dn2); 494 service.scheduleChore(dn3); 495 service.scheduleChore(dn4); 496 service.scheduleChore(dn5); 497 498 Thread.sleep(sleepTime); 499 assertEquals("Scheduled chore mismatch", 5, service.getNumberOfScheduledChores()); 500 501 dn1.cancel(); 502 Thread.sleep(sleepTime); 503 assertEquals("Scheduled chore mismatch", 4, service.getNumberOfScheduledChores()); 504 505 dn2.cancel(); 506 dn3.cancel(); 507 dn4.cancel(); 508 Thread.sleep(sleepTime); 509 assertEquals("Scheduled chore mismatch", 1, service.getNumberOfScheduledChores()); 510 511 dn5.cancel(); 512 Thread.sleep(sleepTime); 513 assertEquals("Scheduled chore mismatch", 0, service.getNumberOfScheduledChores()); 514 } 515 516 @Test 517 public void testNumberOfChoresMissingStartTime() throws InterruptedException { 518 final int period = 100; 519 final int sleepTime = 20 * period; 520 // Slow chores sleep for a length of time LONGER than their period. Thus, SlowChores 521 // ALWAYS miss their start time since their execution takes longer than their period 522 SlowChore sc1 = new SlowChore("sc1", period); 523 SlowChore sc2 = new SlowChore("sc2", period); 524 SlowChore sc3 = new SlowChore("sc3", period); 525 SlowChore sc4 = new SlowChore("sc4", period); 526 SlowChore sc5 = new SlowChore("sc5", period); 527 528 service.scheduleChore(sc1); 529 service.scheduleChore(sc2); 530 service.scheduleChore(sc3); 531 service.scheduleChore(sc4); 532 service.scheduleChore(sc5); 533 534 Thread.sleep(sleepTime); 535 assertEquals(5, service.getNumberOfChoresMissingStartTime()); 536 537 sc1.cancel(); 538 Thread.sleep(sleepTime); 539 assertEquals(4, service.getNumberOfChoresMissingStartTime()); 540 541 sc2.cancel(); 542 sc3.cancel(); 543 sc4.cancel(); 544 Thread.sleep(sleepTime); 545 assertEquals(1, service.getNumberOfChoresMissingStartTime()); 546 547 sc5.cancel(); 548 Thread.sleep(sleepTime); 549 assertEquals(0, service.getNumberOfChoresMissingStartTime()); 550 } 551 552 /** 553 * ChoreServices should never have a core pool size that exceeds the number of chores that have 554 * been scheduled with the service. For example, if 4 ScheduledChores are scheduled with a 555 * ChoreService, the number of threads in the ChoreService's core pool should never exceed 4 556 */ 557 @Test 558 public void testMaximumChoreServiceThreads() throws InterruptedException { 559 560 final int period = 100; 561 final int sleepTime = 5 * period; 562 // Slow chores sleep for a length of time LONGER than their period. Thus, SlowChores 563 // ALWAYS miss their start time since their execution takes longer than their period. 564 // Chores that miss their start time will trigger the onChoreMissedStartTime callback 565 // in the ChoreService. This callback will try to increase the number of core pool 566 // threads. 567 SlowChore sc1 = new SlowChore("sc1", period); 568 SlowChore sc2 = new SlowChore("sc2", period); 569 SlowChore sc3 = new SlowChore("sc3", period); 570 SlowChore sc4 = new SlowChore("sc4", period); 571 SlowChore sc5 = new SlowChore("sc5", period); 572 573 service.scheduleChore(sc1); 574 service.scheduleChore(sc2); 575 service.scheduleChore(sc3); 576 service.scheduleChore(sc4); 577 service.scheduleChore(sc5); 578 579 Thread.sleep(sleepTime); 580 assertTrue(service.getCorePoolSize() <= service.getNumberOfScheduledChores()); 581 582 SlowChore sc6 = new SlowChore("sc6", period); 583 SlowChore sc7 = new SlowChore("sc7", period); 584 SlowChore sc8 = new SlowChore("sc8", period); 585 SlowChore sc9 = new SlowChore("sc9", period); 586 SlowChore sc10 = new SlowChore("sc10", period); 587 588 service.scheduleChore(sc6); 589 service.scheduleChore(sc7); 590 service.scheduleChore(sc8); 591 service.scheduleChore(sc9); 592 service.scheduleChore(sc10); 593 594 Thread.sleep(sleepTime); 595 assertTrue(service.getCorePoolSize() <= service.getNumberOfScheduledChores()); 596 } 597 598 @Test 599 public void testChangingChoreServices() throws InterruptedException { 600 final int period = 100; 601 final int sleepTime = 10; 602 ChoreService anotherService = new ChoreService(name.getMethodName() + "_2"); 603 ScheduledChore chore = new DoNothingChore("sample", period); 604 605 try { 606 assertFalse(chore.isScheduled()); 607 assertFalse(service.isChoreScheduled(chore)); 608 assertFalse(anotherService.isChoreScheduled(chore)); 609 assertTrue(chore.getChoreService() == null); 610 611 service.scheduleChore(chore); 612 Thread.sleep(sleepTime); 613 assertTrue(chore.isScheduled()); 614 assertTrue(service.isChoreScheduled(chore)); 615 assertFalse(anotherService.isChoreScheduled(chore)); 616 assertFalse(chore.getChoreService() == null); 617 618 anotherService.scheduleChore(chore); 619 Thread.sleep(sleepTime); 620 assertTrue(chore.isScheduled()); 621 assertFalse(service.isChoreScheduled(chore)); 622 assertTrue(anotherService.isChoreScheduled(chore)); 623 assertFalse(chore.getChoreService() == null); 624 625 chore.cancel(); 626 assertFalse(chore.isScheduled()); 627 assertFalse(service.isChoreScheduled(chore)); 628 assertFalse(anotherService.isChoreScheduled(chore)); 629 assertTrue(chore.getChoreService() == null); 630 } finally { 631 shutdownService(anotherService); 632 } 633 } 634 635 @Test 636 public void testStopperForScheduledChores() throws InterruptedException { 637 Stoppable stopperForGroup1 = new SampleStopper(); 638 Stoppable stopperForGroup2 = new SampleStopper(); 639 final int period = 100; 640 final int delta = period / 10; 641 ScheduledChore chore1_group1 = new DoNothingChore("c1g1", stopperForGroup1, period); 642 ScheduledChore chore2_group1 = new DoNothingChore("c2g1", stopperForGroup1, period); 643 ScheduledChore chore3_group1 = new DoNothingChore("c3g1", stopperForGroup1, period); 644 645 ScheduledChore chore1_group2 = new DoNothingChore("c1g2", stopperForGroup2, period); 646 ScheduledChore chore2_group2 = new DoNothingChore("c2g2", stopperForGroup2, period); 647 ScheduledChore chore3_group2 = new DoNothingChore("c3g2", stopperForGroup2, period); 648 649 service.scheduleChore(chore1_group1); 650 service.scheduleChore(chore2_group1); 651 service.scheduleChore(chore3_group1); 652 service.scheduleChore(chore1_group2); 653 service.scheduleChore(chore2_group2); 654 service.scheduleChore(chore3_group2); 655 656 Thread.sleep(delta); 657 Thread.sleep(10 * period); 658 assertTrue(chore1_group1.isScheduled()); 659 assertTrue(chore2_group1.isScheduled()); 660 assertTrue(chore3_group1.isScheduled()); 661 assertTrue(chore1_group2.isScheduled()); 662 assertTrue(chore2_group2.isScheduled()); 663 assertTrue(chore3_group2.isScheduled()); 664 665 stopperForGroup1.stop("test stopping group 1"); 666 Thread.sleep(period); 667 assertFalse(chore1_group1.isScheduled()); 668 assertFalse(chore2_group1.isScheduled()); 669 assertFalse(chore3_group1.isScheduled()); 670 assertTrue(chore1_group2.isScheduled()); 671 assertTrue(chore2_group2.isScheduled()); 672 assertTrue(chore3_group2.isScheduled()); 673 674 stopperForGroup2.stop("test stopping group 2"); 675 Thread.sleep(period); 676 assertFalse(chore1_group1.isScheduled()); 677 assertFalse(chore2_group1.isScheduled()); 678 assertFalse(chore3_group1.isScheduled()); 679 assertFalse(chore1_group2.isScheduled()); 680 assertFalse(chore2_group2.isScheduled()); 681 assertFalse(chore3_group2.isScheduled()); 682 } 683 684 @Test 685 public void testShutdownCancelsScheduledChores() throws InterruptedException { 686 final int period = 100; 687 ScheduledChore successChore1 = new DoNothingChore("sc1", period); 688 ScheduledChore successChore2 = new DoNothingChore("sc2", period); 689 ScheduledChore successChore3 = new DoNothingChore("sc3", period); 690 assertTrue(service.scheduleChore(successChore1)); 691 assertTrue(successChore1.isScheduled()); 692 assertTrue(service.scheduleChore(successChore2)); 693 assertTrue(successChore2.isScheduled()); 694 assertTrue(service.scheduleChore(successChore3)); 695 assertTrue(successChore3.isScheduled()); 696 697 shutdownService(service); 698 699 assertFalse(successChore1.isScheduled()); 700 assertFalse(successChore2.isScheduled()); 701 assertFalse(successChore3.isScheduled()); 702 } 703 704 @Test 705 public void testShutdownWorksWhileChoresAreExecuting() throws InterruptedException { 706 final int period = 100; 707 final int sleep = 5 * period; 708 ScheduledChore slowChore1 = new SleepingChore("sc1", period, sleep); 709 ScheduledChore slowChore2 = new SleepingChore("sc2", period, sleep); 710 ScheduledChore slowChore3 = new SleepingChore("sc3", period, sleep); 711 assertTrue(service.scheduleChore(slowChore1)); 712 assertTrue(service.scheduleChore(slowChore2)); 713 assertTrue(service.scheduleChore(slowChore3)); 714 715 Thread.sleep(sleep / 2); 716 shutdownService(service); 717 718 assertFalse(slowChore1.isScheduled()); 719 assertFalse(slowChore2.isScheduled()); 720 assertFalse(slowChore3.isScheduled()); 721 assertTrue(service.isShutdown()); 722 723 Thread.sleep(5); 724 assertTrue(service.isTerminated()); 725 } 726 727 @Test 728 public void testShutdownRejectsNewSchedules() throws InterruptedException { 729 final int period = 100; 730 ScheduledChore successChore1 = new DoNothingChore("sc1", period); 731 ScheduledChore successChore2 = new DoNothingChore("sc2", period); 732 ScheduledChore successChore3 = new DoNothingChore("sc3", period); 733 ScheduledChore failChore1 = new DoNothingChore("fc1", period); 734 ScheduledChore failChore2 = new DoNothingChore("fc2", period); 735 ScheduledChore failChore3 = new DoNothingChore("fc3", period); 736 737 assertTrue(service.scheduleChore(successChore1)); 738 assertTrue(successChore1.isScheduled()); 739 assertTrue(service.scheduleChore(successChore2)); 740 assertTrue(successChore2.isScheduled()); 741 assertTrue(service.scheduleChore(successChore3)); 742 assertTrue(successChore3.isScheduled()); 743 744 shutdownService(service); 745 746 assertFalse(service.scheduleChore(failChore1)); 747 assertFalse(failChore1.isScheduled()); 748 assertFalse(service.scheduleChore(failChore2)); 749 assertFalse(failChore2.isScheduled()); 750 assertFalse(service.scheduleChore(failChore3)); 751 assertFalse(failChore3.isScheduled()); 752 } 753 754 /** 755 * for HBASE-25014 756 */ 757 @Test 758 public void testInitialDelay() { 759 SampleStopper stopper = new SampleStopper(); 760 service.scheduleChore(new ScheduledChore("chore", stopper, 1000, 2000) { 761 @Override 762 protected void chore() { 763 stopper.stop("test"); 764 } 765 }); 766 Waiter.waitFor(CONF, 5000, () -> stopper.isStopped()); 767 } 768 769 @Test 770 public void testCleanupWithStopper() { 771 SampleStopper stopper = new SampleStopper(); 772 DoNothingChore chore = spy(new DoNothingChore("chore", stopper, 10)); 773 service.scheduleChore(chore); 774 assertTrue(chore.isScheduled()); 775 verify(chore, never()).cleanup(); 776 stopper.stop("test"); 777 Waiter.waitFor(CONF, 200, () -> !chore.isScheduled()); 778 verify(chore, atLeastOnce()).cleanup(); 779 } 780 781 @Test 782 public void testCleanupWithShutdown() { 783 DoNothingChore chore = spy(new DoNothingChore("chore", 10)); 784 service.scheduleChore(chore); 785 assertTrue(chore.isScheduled()); 786 verify(chore, never()).cleanup(); 787 chore.shutdown(true); 788 Waiter.waitFor(CONF, 200, () -> !chore.isScheduled()); 789 verify(chore, atLeastOnce()).cleanup(); 790 } 791}