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.util; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertFalse; 022 023import java.io.IOException; 024import java.util.ArrayList; 025import java.util.Collections; 026import java.util.Iterator; 027import java.util.List; 028import java.util.concurrent.CompletableFuture; 029import java.util.concurrent.CompletionException; 030import java.util.concurrent.ExecutionException; 031import java.util.concurrent.atomic.AtomicInteger; 032import org.apache.hadoop.hbase.HBaseClassTestRule; 033import org.apache.hadoop.hbase.testclassification.MiscTests; 034import org.apache.hadoop.hbase.testclassification.SmallTests; 035import org.apache.hadoop.hbase.util.PoolMap.PoolType; 036import org.junit.ClassRule; 037import org.junit.Test; 038import org.junit.experimental.categories.Category; 039 040@Category({ MiscTests.class, SmallTests.class }) 041public class TestRoundRobinPoolMap extends PoolMapTestBase { 042 043 @ClassRule 044 public static final HBaseClassTestRule CLASS_RULE = 045 HBaseClassTestRule.forClass(TestRoundRobinPoolMap.class); 046 047 @Override 048 protected PoolType getPoolType() { 049 return PoolType.RoundRobin; 050 } 051 052 @Test 053 public void testGetOrCreate() throws IOException { 054 String key = "key"; 055 String value = "value"; 056 String result = poolMap.getOrCreate(key, () -> value); 057 058 assertEquals(value, result); 059 assertEquals(1, poolMap.values().size()); 060 } 061 062 @Test 063 public void testMultipleKeys() throws IOException { 064 for (int i = 0; i < KEY_COUNT; i++) { 065 String key = Integer.toString(i); 066 String value = Integer.toString(2 * i); 067 String result = poolMap.getOrCreate(key, () -> value); 068 069 assertEquals(value, result); 070 } 071 072 assertEquals(KEY_COUNT, poolMap.values().size()); 073 } 074 075 @Test 076 public void testMultipleValues() throws IOException { 077 String key = "key"; 078 079 for (int i = 0; i < POOL_SIZE; i++) { 080 String value = Integer.toString(i); 081 String result = poolMap.getOrCreate(key, () -> value); 082 083 assertEquals(value, result); 084 } 085 086 assertEquals(POOL_SIZE, poolMap.values().size()); 087 } 088 089 @Test 090 public void testRoundRobin() throws IOException { 091 String key = "key"; 092 093 for (int i = 0; i < POOL_SIZE; i++) { 094 String value = Integer.toString(i); 095 poolMap.getOrCreate(key, () -> value); 096 } 097 098 assertEquals(POOL_SIZE, poolMap.values().size()); 099 100 /* pool is filled, get() should return elements round robin order */ 101 for (int i = 0; i < 2 * POOL_SIZE; i++) { 102 String expected = Integer.toString(i % POOL_SIZE); 103 assertEquals(expected, poolMap.getOrCreate(key, () -> { 104 throw new IOException("must not call me"); 105 })); 106 } 107 108 assertEquals(POOL_SIZE, poolMap.values().size()); 109 } 110 111 @Test 112 public void testMultiThreadedRoundRobin() throws ExecutionException, InterruptedException { 113 String key = "key"; 114 AtomicInteger id = new AtomicInteger(); 115 List<String> results = Collections.synchronizedList(new ArrayList<>()); 116 117 Runnable runnable = () -> { 118 try { 119 for (int i = 0; i < POOL_SIZE; i++) { 120 String value = Integer.toString(id.getAndIncrement()); 121 String result = poolMap.getOrCreate(key, () -> value); 122 results.add(result); 123 // Sleep for a short time to ensure a yield. Thread#yield has platform dependent behavior. 124 Thread.sleep(10); 125 } 126 } catch (Exception e) { 127 throw new CompletionException(e); 128 } 129 }; 130 131 CompletableFuture<Void> future1 = CompletableFuture.runAsync(runnable); 132 CompletableFuture<Void> future2 = CompletableFuture.runAsync(runnable); 133 134 /* test for successful completion */ 135 future1.get(); 136 future2.get(); 137 138 assertEquals(POOL_SIZE, poolMap.values().size()); 139 140 /* check every elements occur twice */ 141 Collections.sort(results); 142 Iterator<String> iterator = results.iterator(); 143 144 for (int i = 0; i < POOL_SIZE; i++) { 145 String next1 = iterator.next(); 146 String next2 = iterator.next(); 147 assertEquals(next1, next2); 148 } 149 150 assertFalse(iterator.hasNext()); 151 } 152}