1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.util;
19  
20  import static org.junit.Assert.assertFalse;
21  import static org.junit.Assert.fail;
22  
23  import java.util.ArrayList;
24  import java.util.concurrent.CountDownLatch;
25  import java.util.concurrent.atomic.AtomicBoolean;
26  
27  import org.apache.hadoop.hbase.MediumTests;
28  import org.junit.Test;
29  import org.junit.experimental.categories.Category;
30  
31  /**
32   * This tests some race conditions that can happen
33   * occasionally, but not every time.
34   */
35  @Category(MediumTests.class)
36  public class TestSizeBasedThrottler {
37  
38    private static final int REPEATS = 100;
39  
40    private Thread makeThread(final SizeBasedThrottler throttler,
41        final AtomicBoolean failed, final int delta,
42        final int limit, final CountDownLatch latch) {
43  
44      Thread ret = new Thread(new Runnable() {
45  
46        @Override
47        public void run() {
48          try {
49            latch.await();
50            if (throttler.increase(delta) > limit) {
51              failed.set(true);
52            }
53            throttler.decrease(delta);
54          } catch (Exception e) {
55            failed.set(true);
56          }
57        }
58      });
59  
60      ret.start();
61      return ret;
62    }
63  
64    private void runGenericTest(int threshold, int delta, int maxValueAllowed,
65        int numberOfThreads, long timeout) {
66      SizeBasedThrottler throttler = new SizeBasedThrottler(threshold);
67      AtomicBoolean failed = new AtomicBoolean(false);
68  
69      ArrayList<Thread> threads = new ArrayList<Thread>(numberOfThreads);
70      CountDownLatch latch = new CountDownLatch(1);
71      long timeElapsed = 0;
72  
73      for (int i = 0; i < numberOfThreads; ++i) {
74        threads.add(makeThread(throttler, failed, delta, maxValueAllowed, latch));
75      }
76  
77      latch.countDown();
78      for (Thread t : threads) {
79        try {
80          long beforeJoin = System.currentTimeMillis();
81          t.join(timeout - timeElapsed);
82          timeElapsed += System.currentTimeMillis() - beforeJoin;
83          if (t.isAlive() || timeElapsed >= timeout) {
84            fail("Timeout reached.");
85          }
86        } catch (InterruptedException e) {
87          fail("Got InterruptedException");
88        }
89      }
90  
91      assertFalse(failed.get());
92    }
93  
94    @Test
95    public void testSmallIncreases(){
96      for (int i = 0; i < REPEATS; ++i) {
97        runGenericTest(
98            10, // threshold
99            1,  // delta
100           15, // fail if throttler's value
101               // exceeds 15
102           1000, // use 1000 threads
103           500 // wait for 500ms
104           );
105     }
106   }
107 
108   @Test
109   public void testBigIncreases() {
110     for (int i = 0; i < REPEATS; ++i) {
111       runGenericTest(
112           1, // threshold
113           2, // delta
114           4, // fail if throttler's value
115              // exceeds 4
116           1000, // use 1000 threads
117           500 // wait for 500ms
118           );
119     }
120   }
121 
122   @Test
123   public void testIncreasesEqualToThreshold(){
124     for (int i = 0; i < REPEATS; ++i) {
125       runGenericTest(
126           1, // threshold
127           1, // delta
128           2, // fail if throttler's value
129              // exceeds 2
130           1000, // use 1000 threads
131           500 // wait for 500ms
132           );
133     }
134   }
135 }