/* * 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.assertTrue; import java.util.LinkedList; import java.util.Queue; import java.util.concurrent.TimeUnit; import org.junit.Before; import org.junit.Test; public class QueuedKeyedResourcePoolTest extends KeyedResourcePoolTest { protected QueuedKeyedResourcePool<String, TestResource> queuedPool; @Before @Override public void setUp() { super.setUp(); this.queuedPool = new QueuedKeyedResourcePool<String, TestResource>(factory, config); super.pool = queuedPool; TestResourceRequest.usedResourceCount.set(0); TestResourceRequest.handledTimeoutCount.set(0); TestResourceRequest.handledExceptionCount.set(0); } @Test public void testQueuingOccurs() throws Exception { Queue<TestResource> resources = new LinkedList<TestResource>(); Queue<TestResourceRequest> resourceRequests = new LinkedList<TestResourceRequest>(); long deadlineNs = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(KeyedResourcePoolTest.TIMEOUT_MS); for(int i = 0; i < POOL_SIZE * 2; i++) { resourceRequests.add(new TestResourceRequest(deadlineNs, resources)); } assertEquals(0, this.factory.getCreated()); assertEquals(0, this.queuedPool.getTotalResourceCount()); assertEquals(0, this.queuedPool.getCheckedInResourceCount()); assertEquals(0, this.queuedPool.getRegisteredResourceRequestCount()); assertEquals(0, resources.size()); // Submit initial POOL_SIZE requests for(int i = 0; i < POOL_SIZE; i++) { this.queuedPool.registerResourceRequest("a", resourceRequests.poll()); } // Confirm initial requests were handled in nonblocking manner assertEquals(POOL_SIZE, this.factory.getCreated()); assertEquals(POOL_SIZE, this.queuedPool.getTotalResourceCount()); assertEquals(0, this.queuedPool.getCheckedInResourceCount()); assertEquals(0, this.queuedPool.getRegisteredResourceRequestCount()); assertEquals(POOL_SIZE, resources.size()); // Submit additional POOL_SIZE requests for(int i = 0; i < POOL_SIZE; i++) { this.queuedPool.registerResourceRequest("a", resourceRequests.poll()); } // Confirm additional requests are queued assertEquals(POOL_SIZE, this.factory.getCreated()); assertEquals(POOL_SIZE, this.queuedPool.getTotalResourceCount()); assertEquals(0, this.queuedPool.getCheckedInResourceCount()); assertEquals(POOL_SIZE, this.queuedPool.getRegisteredResourceRequestCount()); assertEquals(POOL_SIZE, resources.size()); // Check in initial resources and confirm that this consumes queued // resource requests for(int i = 0; i < POOL_SIZE; i++) { this.queuedPool.checkin("a", resources.poll()); assertEquals(POOL_SIZE, this.factory.getCreated()); assertEquals(POOL_SIZE, this.queuedPool.getTotalResourceCount()); assertEquals(0, this.queuedPool.getCheckedInResourceCount()); assertEquals(POOL_SIZE - i - 1, this.queuedPool.getRegisteredResourceRequestCount()); assertEquals(POOL_SIZE, resources.size()); } // Check in additional resources for(int i = 0; i < POOL_SIZE; i++) { this.queuedPool.checkin("a", resources.poll()); assertEquals(POOL_SIZE, this.factory.getCreated()); assertEquals(POOL_SIZE, this.queuedPool.getTotalResourceCount()); assertEquals(i + 1, this.queuedPool.getCheckedInResourceCount()); assertEquals(0, this.queuedPool.getRegisteredResourceRequestCount()); assertEquals(POOL_SIZE - i - 1, resources.size()); } assertEquals(POOL_SIZE, this.factory.getCreated()); assertEquals(POOL_SIZE, this.queuedPool.getTotalResourceCount()); assertEquals(POOL_SIZE, this.queuedPool.getCheckedInResourceCount()); assertEquals(0, this.queuedPool.getRegisteredResourceRequestCount()); assertEquals(0, resources.size()); assertEquals(POOL_SIZE * 2, TestResourceRequest.usedResourceCount.get()); assertEquals(0, TestResourceRequest.handledTimeoutCount.get()); assertEquals(0, TestResourceRequest.handledExceptionCount.get()); } @Test public void testQueuingStats() throws Exception { Queue<TestResource> resources = new LinkedList<TestResource>(); Queue<TestResourceRequest> resourceRequests = new LinkedList<TestResourceRequest>(); long deadlineNs = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(KeyedResourcePoolTest.TIMEOUT_MS); for(int i = 0; i < POOL_SIZE * 10001; i++) { resourceRequests.add(new TestResourceRequest(deadlineNs, resources)); } assertEquals(0, this.factory.getCreated()); assertEquals(0, this.queuedPool.getTotalResourceCount()); assertEquals(0, this.queuedPool.getCheckedInResourceCount()); assertEquals(0, this.queuedPool.getRegisteredResourceRequestCount()); assertEquals(0, resources.size()); // Submit initial POOL_SIZE requests for(int i = 0; i < POOL_SIZE; i++) { this.queuedPool.registerResourceRequest("a", resourceRequests.poll()); } // Confirm initial requests were handled in nonblocking manner assertEquals(POOL_SIZE, this.factory.getCreated()); assertEquals(POOL_SIZE, this.queuedPool.getTotalResourceCount()); assertEquals(0, this.queuedPool.getCheckedInResourceCount()); assertEquals(0, this.queuedPool.getRegisteredResourceRequestCount()); assertEquals(POOL_SIZE, resources.size()); // Register five order of magnitude more resource requests. for(int i = 0; i < POOL_SIZE * 10000; i++) { this.queuedPool.registerResourceRequest("a", resourceRequests.poll()); } long startNs = System.nanoTime(); assertEquals(POOL_SIZE * 10000, this.queuedPool.getRegisteredResourceRequestCount()); long durationNs = System.nanoTime() - startNs; // 20 ms is an arbitrary time limit: // POOL_SIZE * 10000 resource requests / 20 ms // = 50000 resource requests/20ms // = 2500 resource requests/ms assertTrue("O(n) count of queue is too slow: " + durationNs + " ns.", durationNs < TimeUnit.MILLISECONDS.toNanos(20)); // Confirm additional requests are queued assertEquals(POOL_SIZE, this.factory.getCreated()); assertEquals(POOL_SIZE, this.queuedPool.getTotalResourceCount()); assertEquals(0, this.queuedPool.getCheckedInResourceCount()); assertEquals(POOL_SIZE, resources.size()); } @Test public void testTimeoutInQueue() throws Exception { Queue<TestResource> resources = new LinkedList<TestResource>(); Queue<TestResourceRequest> resourceRequests = new LinkedList<TestResourceRequest>(); long deadlineNs = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(KeyedResourcePoolTest.TIMEOUT_MS); for(int i = 0; i < POOL_SIZE * 2; i++) { resourceRequests.add(new TestResourceRequest(deadlineNs, resources)); } assertEquals(0, this.factory.getCreated()); assertEquals(0, this.queuedPool.getTotalResourceCount()); assertEquals(0, this.queuedPool.getCheckedInResourceCount()); assertEquals(0, this.queuedPool.getRegisteredResourceRequestCount()); assertEquals(0, resources.size()); // Submit initial POOL_SIZE requests for(int i = 0; i < POOL_SIZE; i++) { this.queuedPool.registerResourceRequest("a", resourceRequests.poll()); } // Submit additional POOL_SIZE requests that queue up. for(int i = 0; i < POOL_SIZE; i++) { this.queuedPool.registerResourceRequest("a", resourceRequests.poll()); } // Force deadline (timeout) to expire TimeUnit.MILLISECONDS.sleep(KeyedResourcePoolTest.TIMEOUT_MS * 2); // Check in one initial resource and confirm that this causes all // enqueued requests to be timed out. this.queuedPool.checkin("a", resources.poll()); assertEquals(POOL_SIZE, this.factory.getCreated()); assertEquals(POOL_SIZE, this.queuedPool.getTotalResourceCount()); assertEquals(1, this.queuedPool.getCheckedInResourceCount()); assertEquals(0, this.queuedPool.getRegisteredResourceRequestCount()); assertEquals(POOL_SIZE - 1, resources.size()); assertEquals(POOL_SIZE, TestResourceRequest.usedResourceCount.get()); assertEquals(POOL_SIZE, TestResourceRequest.handledTimeoutCount.get()); assertEquals(0, TestResourceRequest.handledExceptionCount.get()); // Check in remaining (initial) resources for(int i = 0; i < POOL_SIZE - 1; i++) { this.queuedPool.checkin("a", resources.poll()); } assertEquals(POOL_SIZE, this.factory.getCreated()); assertEquals(POOL_SIZE, this.queuedPool.getTotalResourceCount()); assertEquals(POOL_SIZE, this.queuedPool.getCheckedInResourceCount()); assertEquals(0, this.queuedPool.getRegisteredResourceRequestCount()); assertEquals(0, resources.size()); assertEquals(POOL_SIZE, TestResourceRequest.usedResourceCount.get()); assertEquals(POOL_SIZE, TestResourceRequest.handledTimeoutCount.get()); assertEquals(0, TestResourceRequest.handledExceptionCount.get()); } @Test public void testExceptionInQueue() throws Exception { Exception toThrow = new Exception("An exception!"); this.factory.setCreateException(toThrow); Queue<TestResource> resources = new LinkedList<TestResource>(); long deadlineNs = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(KeyedResourcePoolTest.TIMEOUT_MS); TestResourceRequest trr = new TestResourceRequest(deadlineNs, resources); assertEquals(0, this.factory.getCreated()); assertEquals(0, this.queuedPool.getTotalResourceCount()); assertEquals(0, this.queuedPool.getCheckedInResourceCount()); assertEquals(0, this.queuedPool.getRegisteredResourceRequestCount()); assertEquals(0, resources.size()); this.queuedPool.registerResourceRequest("a", trr); assertEquals(0, this.factory.getCreated()); assertEquals(0, this.queuedPool.getTotalResourceCount()); assertEquals(0, this.queuedPool.getCheckedInResourceCount()); assertEquals(0, this.queuedPool.getRegisteredResourceRequestCount()); assertEquals(0, resources.size()); assertEquals(0, TestResourceRequest.usedResourceCount.get()); assertEquals(0, TestResourceRequest.handledTimeoutCount.get()); assertEquals(1, TestResourceRequest.handledExceptionCount.get()); } }