/* * Copyright 2012 LinkedIn, Inc * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package voldemort.utils.pool; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.util.LinkedList; import java.util.Queue; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import voldemort.utils.Time; public class KeyedResourcePoolTest extends KeyedResourcePoolTestBase { protected static int POOL_SIZE = 5; protected static long TIMEOUT_MS = 500; @Before public void setUp() { factory = new TestResourceFactory(); config = new ResourcePoolConfig().setMaxPoolSize(POOL_SIZE) .setTimeout(TIMEOUT_MS, TimeUnit.MILLISECONDS); this.pool = new KeyedResourcePool<String, TestResource>(factory, config); } @Test public void testResourcePoolConfigTimeout() { // Issue 343 assertEquals(config.getTimeout(TimeUnit.MILLISECONDS), TIMEOUT_MS); assertEquals(config.getTimeout(TimeUnit.NANOSECONDS), TIMEOUT_MS * Time.NS_PER_MS); } @Test public void testPoolingOccurs() throws Exception { TestResource r1 = this.pool.checkout("a"); this.pool.checkin("a", r1); TestResource r2 = this.pool.checkout("a"); assertTrue("Checked out value should equal checked in value (found " + r1 + ", " + r2 + ")", r1 == r2); } @Test public void testFullPoolBlocks() throws Exception { for(int i = 0; i < POOL_SIZE; i++) this.pool.checkout("a"); try { this.pool.checkout("a"); fail("Checking out more items than in the pool should timeout."); } catch(TimeoutException e) { // this is good } } @Test public void testExceptionOnDestroy() throws Exception { assertTrue("POOL_SIZE is not big enough", POOL_SIZE >= 2); Exception toThrow = new Exception("An exception! (This exception is expected and so will print out some output to stderr.)"); this.factory.setDestroyException(toThrow); assertEquals(0, this.pool.getTotalResourceCount()); try { TestResource checkedOut = this.pool.checkout("a"); assertFalse(checkedOut.isDestroyed()); assertEquals(1, this.factory.getCreated()); assertEquals(1, this.pool.getTotalResourceCount()); assertEquals(0, this.pool.getCheckedInResourceCount()); this.pool.checkin("a", checkedOut); assertEquals(1, this.factory.getCreated()); assertEquals(1, this.pool.getTotalResourceCount()); assertEquals(1, this.pool.getCheckedInResourceCount()); this.pool.checkout("a"); assertEquals(1, this.factory.getCreated()); assertEquals(1, this.pool.getTotalResourceCount()); assertEquals(0, this.pool.getCheckedInResourceCount()); for(int i = 0; i < POOL_SIZE - 1; i++) { checkedOut = this.pool.checkout("a"); assertFalse(checkedOut.isDestroyed()); } assertEquals(POOL_SIZE, this.factory.getCreated()); assertEquals(POOL_SIZE, this.pool.getTotalResourceCount()); assertEquals(0, this.pool.getCheckedInResourceCount()); assertEquals(0, this.factory.getDestroyed()); checkedOut.invalidate(); try { // pool.checkin should catch and print out the destroy // exception. this.pool.checkin("a", checkedOut); } catch(Exception caught) { fail("No exception expected."); } assertEquals(POOL_SIZE - 1, this.pool.getTotalResourceCount()); assertEquals(0, this.pool.getCheckedInResourceCount()); assertEquals(0, this.factory.getDestroyed()); this.pool.checkout("a"); assertEquals(POOL_SIZE + 1, this.factory.getCreated()); assertEquals(POOL_SIZE, this.pool.getTotalResourceCount()); assertEquals(0, this.pool.getCheckedInResourceCount()); assertEquals(0, this.factory.getDestroyed()); } catch(Exception caught) { fail("No exception expected."); } } @Test public void testExceptionOnCreate() throws Exception { Exception toThrow = new Exception("An exception!"); assertEquals(0, this.pool.getTotalResourceCount()); this.factory.setCreateException(toThrow); try { this.pool.checkout("b"); fail("Expected exception!"); } catch(Exception caught) { assertEquals("The exception thrown by the factory should propagate to the caller.", toThrow, caught); } // failed checkout shouldn't affect count assertEquals(0, this.pool.getTotalResourceCount()); assertEquals(0, this.pool.getCheckedInResourceCount()); } // NEGATIVE Test. See comment in line. @Test public void repeatedCheckins() throws Exception { assertEquals(0, this.pool.getTotalResourceCount()); TestResource resource = this.pool.checkout("a"); assertEquals(1, this.factory.getCreated()); assertEquals(1, this.pool.getTotalResourceCount()); assertEquals(0, this.pool.getCheckedInResourceCount()); this.pool.checkin("a", resource); assertEquals(1, this.factory.getCreated()); assertEquals(1, this.pool.getTotalResourceCount()); assertEquals(1, this.pool.getCheckedInResourceCount()); this.pool.checkin("a", resource); assertEquals(1, this.factory.getCreated()); assertEquals(1, this.pool.getTotalResourceCount()); // KeyedResourcePool does not protect against repeated checkins. It // Should. If it did, then the commented out test below would be // correct. assertEquals(2, this.pool.getCheckedInResourceCount()); // assertEquals(1, this.pool.getCheckedInResourceCount()); } // NEGATIVE Test. See comment in line. @Test public void testExceptionOnFullCheckin() throws Exception { assertEquals(0, this.pool.getTotalResourceCount()); Queue<TestResource> resources = new LinkedList<TestResource>(); for(int i = 0; i < POOL_SIZE; i++) { TestResource resource = this.pool.checkout("a"); resources.add(resource); } assertEquals(POOL_SIZE, this.pool.getTotalResourceCount()); for(int i = 0; i < POOL_SIZE; i++) { this.pool.checkin("a", resources.poll()); } assertEquals(POOL_SIZE, this.pool.getTotalResourceCount()); try { this.factory.createAsync("a", this.pool); } catch(IllegalStateException ise) { // this is good } // KeyedResourcePool does not protect against repeated or extraneous // checkins. If an extraneous checkin occurs, then the checked in // resource is destroyed and the size of the resource pool is reduced by // one (even though it should not be in this exceptional case). assertEquals(POOL_SIZE - 1, this.pool.getTotalResourceCount()); // assertEquals(POOL_SIZE, this.pool.getTotalResourceCount()); } // NEGATIVE Test. See comment in line. @Test public void testCheckinExtraneousResource() throws Exception { assertEquals(0, this.pool.getTotalResourceCount()); TestResource resource = this.pool.checkout("a"); this.pool.checkin("a", resource); this.factory.createAsync("a", this.pool); // KeyedResourcePool should not permit random resources to be checked // in. Until it protects against arbitrary resources being checked in, // it is possible to checkin an extraneous resource. assertEquals(1, this.pool.getTotalResourceCount()); assertEquals(2, this.pool.getCheckedInResourceCount()); } // NEGATIVE Test. See comment in line. @Test public void testNeverCheckin() throws Exception { assertEquals(0, this.pool.getTotalResourceCount()); { this.pool.checkout("a"); } // KeyedResourcePool does not protect against resources being checked // out and never checked back in (or destroyed). assertEquals(1, this.pool.getTotalResourceCount()); assertEquals(0, this.pool.getCheckedInResourceCount()); } @Test public void testInvalidIsDestroyed() throws Exception { TestResource r1 = this.pool.checkout("a"); r1.invalidate(); this.pool.checkin("a", r1); TestResource r2 = this.pool.checkout("a"); assertTrue("Invalid objects should be destroyed.", r1 != r2); assertTrue("Invalid objects should be destroyed.", r1.isDestroyed()); assertEquals(1, this.factory.getDestroyed()); } // After the move to the async model, if a resource is invalid, it will // not be returned to the pool and destroyed on the checkin. So this is // not valid anymore. @Ignore public void testMaxInvalidCreations() throws Exception { this.factory.setCreatedValid(false); try { this.pool.checkout("a"); fail("Exceeded max failed attempts without exception."); } catch(ExcessiveInvalidResourcesException e) { // this is expected } } }