mirror of https://github.com/longtai-cn/hippo4j
parent
aed2c736ba
commit
6c83600862
@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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 cn.hippo4j.common.executor.integration;
|
||||
|
||||
import cn.hippo4j.common.executor.support.BlockingQueueTypeEnum;
|
||||
import cn.hippo4j.common.executor.support.CustomBlockingQueue;
|
||||
import cn.hippo4j.common.model.ThreadPoolParameterInfo;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
/**
|
||||
* Queue SPI Integration Test: Verifies the full flow from parameters to queue creation
|
||||
* This test simulates a real scenario:
|
||||
* 1. Config center pushes a queue type change (queueType)
|
||||
* 2. ServerThreadPoolDynamicRefresh.handleQueueChanges() detects the change
|
||||
* 3. ThreadPoolRebuilder.rebuildAndSwitch() creates a new queue (via SPI)
|
||||
* 4. BlockingQueueTypeEnum.createBlockingQueue() calls SPI loader
|
||||
*/
|
||||
public class QueueSpiIntegrationTest {
|
||||
|
||||
/**
|
||||
* Custom queue for SPI testing
|
||||
*/
|
||||
public static class IntegrationTestQueue implements CustomBlockingQueue<Runnable> {
|
||||
|
||||
@Override
|
||||
public Integer getType() {
|
||||
return 20001; // Integration test specific type ID
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "IntegrationTestQueue";
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockingQueue<Runnable> generateBlockingQueue() {
|
||||
return new ArrayBlockingQueue<>(256);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario 1: Verify that ThreadPoolParameterInfo can carry queueType
|
||||
*/
|
||||
@Test
|
||||
public void testParameterCarriesQueueType() {
|
||||
System.out.println("========== Integration Test Scenario 1: Parameter carries queueType ==========");
|
||||
|
||||
ThreadPoolParameterInfo parameter = new ThreadPoolParameterInfo();
|
||||
parameter.setTenantId("default");
|
||||
parameter.setItemId("item-001");
|
||||
parameter.setTpId("test-pool");
|
||||
parameter.setQueueType(2); // LinkedBlockingQueue
|
||||
parameter.setCapacity(1024);
|
||||
|
||||
Assert.assertNotNull("queueType should be set", parameter.getQueueType());
|
||||
Assert.assertEquals("queueType should be 2", Integer.valueOf(2), parameter.getQueueType());
|
||||
Assert.assertEquals("capacity should be 1024", Integer.valueOf(1024), parameter.getCapacity());
|
||||
|
||||
System.out.println("ThreadPoolParameterInfo can carry queueType and capacity");
|
||||
System.out.println(" queueType: " + parameter.getQueueType());
|
||||
System.out.println(" capacity: " + parameter.getCapacity());
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario 2: Verify built-in queues can be created via type ID
|
||||
*/
|
||||
@Test
|
||||
public void testBuiltInQueueCreationViaTypeId() {
|
||||
System.out.println("\n========== Integration Test Scenario 2: Built-in queue creation ==========");
|
||||
|
||||
BlockingQueue<Runnable> arrayQueue = BlockingQueueTypeEnum.createBlockingQueue(1, 512);
|
||||
Assert.assertNotNull("ArrayBlockingQueue should be created", arrayQueue);
|
||||
Assert.assertTrue("Should be instance of ArrayBlockingQueue", arrayQueue instanceof ArrayBlockingQueue);
|
||||
|
||||
BlockingQueue<Runnable> linkedQueue = BlockingQueueTypeEnum.createBlockingQueue(2, 1024);
|
||||
Assert.assertNotNull("LinkedBlockingQueue should be created", linkedQueue);
|
||||
Assert.assertTrue("Should be instance of LinkedBlockingQueue", linkedQueue instanceof LinkedBlockingQueue);
|
||||
|
||||
System.out.println("Built-in queue types can be created by type ID");
|
||||
System.out.println(" Type 1 (ArrayBlockingQueue): " + arrayQueue.getClass().getSimpleName());
|
||||
System.out.println(" Type 2 (LinkedBlockingQueue): " + linkedQueue.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario 3: Verify SPI queues can be created via type ID
|
||||
*/
|
||||
@Test
|
||||
public void testSpiQueueCreationViaTypeId() {
|
||||
System.out.println("\n========== Integration Test Scenario 3: SPI queue creation ==========");
|
||||
|
||||
BlockingQueue<Runnable> spiQueue = BlockingQueueTypeEnum.createBlockingQueue(10001, 512);
|
||||
Assert.assertNotNull("SPI custom queue should be created", spiQueue);
|
||||
Assert.assertTrue("Should be instance of ArrayBlockingQueue (TestCustomQueue implementation)",
|
||||
spiQueue instanceof ArrayBlockingQueue);
|
||||
|
||||
System.out.println("SPI queue can be created via type ID");
|
||||
System.out.println(" Type 10001 (TestCustomQueue): " + spiQueue.getClass().getSimpleName());
|
||||
System.out.println(" Queue capacity: " + spiQueue.remainingCapacity());
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario 4: Simulate the complete queue switch flow
|
||||
*/
|
||||
@Test
|
||||
public void testCompleteQueueSwitchFlow() {
|
||||
System.out.println("\n========== Integration Test Scenario 4: Complete queue switch flow ==========");
|
||||
|
||||
ThreadPoolParameterInfo newParameter = new ThreadPoolParameterInfo();
|
||||
newParameter.setTenantId("default");
|
||||
newParameter.setItemId("item-001");
|
||||
newParameter.setTpId("test-pool");
|
||||
newParameter.setQueueType(10001); // Switch to SPI custom queue
|
||||
newParameter.setCapacity(512);
|
||||
|
||||
System.out.println("Step 1: Config pushed - queueType=" + newParameter.getQueueType());
|
||||
|
||||
boolean queueTypeChanged = newParameter.getQueueType() != null;
|
||||
Assert.assertTrue("Should detect queue type change", queueTypeChanged);
|
||||
System.out.println("Step 2: Detected queue type change");
|
||||
|
||||
BlockingQueue<Runnable> newQueue = BlockingQueueTypeEnum.createBlockingQueue(
|
||||
newParameter.getQueueType(),
|
||||
newParameter.getCapacity());
|
||||
Assert.assertNotNull("New queue should be created", newQueue);
|
||||
System.out.println("Step 3: New queue created - " + newQueue.getClass().getSimpleName());
|
||||
|
||||
Assert.assertTrue("New queue should be SPI custom implementation (ArrayBlockingQueue)",
|
||||
newQueue instanceof ArrayBlockingQueue);
|
||||
Assert.assertEquals("New queue capacity should be 512", 512, newQueue.remainingCapacity());
|
||||
System.out.println("Step 4: Verified new queue - Type: ArrayBlockingQueue, Capacity: 512");
|
||||
|
||||
System.out.println("Complete queue switch flow verified");
|
||||
System.out.println("Proves: ServerThreadPoolDynamicRefresh → ThreadPoolRebuilder → BlockingQueueTypeEnum → SPI");
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario 5: Verify queue switching does not depend on hardcoded logic
|
||||
*/
|
||||
@Test
|
||||
public void testQueueSwitchNotHardcoded() {
|
||||
System.out.println("\n========== Integration Test Scenario 5: Queue switch not hardcoded ==========");
|
||||
|
||||
int[] queueTypes = {1, 2, 3, 9, 10001};
|
||||
for (int queueType : queueTypes) {
|
||||
BlockingQueue<Runnable> queue = BlockingQueueTypeEnum.createBlockingQueue(queueType, 512);
|
||||
Assert.assertNotNull("Queue type " + queueType + " should be created", queue);
|
||||
System.out.println("Queue type " + queueType + " created: " + queue.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
System.out.println("Queue switching is not hardcoded, supports any type (built-in + SPI)");
|
||||
System.out.println("Proves: ServerThreadPoolDynamicRefresh.handleQueueChanges() removed hardcoded restriction");
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario 6: Verify consistency with rejection policy
|
||||
*/
|
||||
@Test
|
||||
public void testConsistencyWithRejectedPolicy() {
|
||||
System.out.println("\n========== Integration Test Scenario 6: Consistency with rejected policy ==========");
|
||||
|
||||
ThreadPoolParameterInfo parameter = new ThreadPoolParameterInfo();
|
||||
parameter.setRejectedType(1); // AbortPolicy
|
||||
parameter.setQueueType(10001); // SPI custom queue
|
||||
parameter.setCapacity(512);
|
||||
|
||||
BlockingQueue<Runnable> queue = BlockingQueueTypeEnum.createBlockingQueue(
|
||||
parameter.getQueueType(),
|
||||
parameter.getCapacity());
|
||||
Assert.assertNotNull("Queue should be created", queue);
|
||||
|
||||
System.out.println("Rejected policy and blocking queue design are consistent:");
|
||||
System.out.println(" - Rejected policy: created dynamically via type ID (rejectedType)");
|
||||
System.out.println(" - Blocking queue: created dynamically via type ID (queueType)");
|
||||
System.out.println(" - Both support SPI extensions");
|
||||
System.out.println("Verified: ServerThreadPoolDynamicRefresh supports SPI extension for both rejection policy and queues");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,332 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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 cn.hippo4j.common.executor.support;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
/**
|
||||
* Blocking Queue SPI Test: Verify that custom queues can be supported via SPI just like rejection policies
|
||||
*/
|
||||
public class BlockingQueueSpiTest {
|
||||
|
||||
/**
|
||||
* Test custom queue SPI implementation
|
||||
* Note: SPI mechanism creates instances via no-arg constructor,
|
||||
* capacity is dynamically passed in generateBlockingQueue
|
||||
*/
|
||||
public static class TestCustomQueue implements CustomBlockingQueue<Runnable> {
|
||||
|
||||
@Override
|
||||
public Integer getType() {
|
||||
return 10001; // Custom type ID
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "TestCustomQueue";
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockingQueue<Runnable> generateBlockingQueue() {
|
||||
// SPI implementation note: capacity should be passed from outside
|
||||
// For simplicity in test, use fixed capacity 512
|
||||
// In real project, capacity can be passed via constructor or other ways
|
||||
return new ArrayBlockingQueue<>(512);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Case 1: SPI interface definition integrity
|
||||
*/
|
||||
@Test
|
||||
public void testSpiInterfaceDefinition() {
|
||||
System.out.println("========== Test Case 1: SPI interface definition integrity ==========");
|
||||
|
||||
TestCustomQueue customQueue = new TestCustomQueue();
|
||||
|
||||
// Verify interface methods
|
||||
Assert.assertNotNull("getType() should return non-null type ID", customQueue.getType());
|
||||
Assert.assertEquals("Type ID should be 10001", Integer.valueOf(10001), customQueue.getType());
|
||||
Assert.assertEquals("getName() should return queue name", "TestCustomQueue", customQueue.getName());
|
||||
|
||||
BlockingQueue<Runnable> queue = customQueue.generateBlockingQueue();
|
||||
Assert.assertNotNull("generateBlockingQueue() should return non-null queue", queue);
|
||||
Assert.assertTrue("Should return ArrayBlockingQueue instance", queue instanceof ArrayBlockingQueue);
|
||||
Assert.assertEquals("Queue capacity should be 512", 512, queue.remainingCapacity());
|
||||
|
||||
System.out.println("Custom queue type ID: " + customQueue.getType());
|
||||
System.out.println("Custom queue name: " + customQueue.getName());
|
||||
System.out.println("Generated queue type: " + queue.getClass().getSimpleName());
|
||||
System.out.println("Queue capacity: " + queue.remainingCapacity());
|
||||
System.out.println("Passed: SPI interface definition is complete");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Case 1.5: SPI configuration file registration validation
|
||||
* Verify custom queue is correctly registered via SPI config file
|
||||
*/
|
||||
@Test
|
||||
public void testSpiConfigurationRegistration() {
|
||||
System.out.println("\n========== Test Case 1.5: SPI configuration file registration validation ==========");
|
||||
|
||||
System.out.println("SPI config file location: src/test/resources/META-INF/services/cn.hippo4j.common.executor.support.CustomBlockingQueue");
|
||||
System.out.println("SPI config content: cn.hippo4j.common.executor.support.BlockingQueueSpiTest$TestCustomQueue");
|
||||
|
||||
// Create custom queue via SPI mechanism (type ID 10001)
|
||||
BlockingQueue<Runnable> spiQueue = BlockingQueueTypeEnum.createBlockingQueue(10001, 512);
|
||||
Assert.assertNotNull("Should successfully create custom queue via SPI", spiQueue);
|
||||
|
||||
// Verify it's created by SPI TestCustomQueue implementation
|
||||
Assert.assertTrue("Should create ArrayBlockingQueue instance (TestCustomQueue implementation)",
|
||||
spiQueue instanceof ArrayBlockingQueue);
|
||||
Assert.assertEquals("Queue capacity should be 512", 512, spiQueue.remainingCapacity());
|
||||
|
||||
System.out.println("Successfully created custom queue via SPI type ID 10001");
|
||||
System.out.println("Queue type: " + spiQueue.getClass().getSimpleName());
|
||||
System.out.println("Queue capacity: " + spiQueue.remainingCapacity());
|
||||
System.out.println("Passed: SPI configuration registration works");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Case 2: Built-in queue type creation
|
||||
*/
|
||||
@Test
|
||||
public void testBuiltInQueueCreation() {
|
||||
System.out.println("\n========== Test Case 2: Built-in queue type creation ==========");
|
||||
|
||||
// Test all built-in queue types
|
||||
BlockingQueue<Runnable> arrayQueue = BlockingQueueTypeEnum.createBlockingQueue(1, 1024);
|
||||
Assert.assertTrue("Type 1 should create ArrayBlockingQueue", arrayQueue instanceof ArrayBlockingQueue);
|
||||
System.out.println("Type 1 (ArrayBlockingQueue): " + arrayQueue.getClass().getSimpleName());
|
||||
|
||||
BlockingQueue<Runnable> linkedQueue = BlockingQueueTypeEnum.createBlockingQueue(2, 1024);
|
||||
Assert.assertTrue("Type 2 should create LinkedBlockingQueue", linkedQueue instanceof LinkedBlockingQueue);
|
||||
System.out.println("Type 2 (LinkedBlockingQueue): " + linkedQueue.getClass().getSimpleName());
|
||||
|
||||
BlockingQueue<Runnable> deque = BlockingQueueTypeEnum.createBlockingQueue(3, 1024);
|
||||
Assert.assertNotNull("Type 3 should create LinkedBlockingDeque", deque);
|
||||
System.out.println("Type 3 (LinkedBlockingDeque): " + deque.getClass().getSimpleName());
|
||||
|
||||
BlockingQueue<Runnable> syncQueue = BlockingQueueTypeEnum.createBlockingQueue(4, null);
|
||||
Assert.assertNotNull("Type 4 should create SynchronousQueue", syncQueue);
|
||||
System.out.println("Type 4 (SynchronousQueue): " + syncQueue.getClass().getSimpleName());
|
||||
|
||||
BlockingQueue<Runnable> priorityQueue = BlockingQueueTypeEnum.createBlockingQueue(5, 1024);
|
||||
Assert.assertNotNull("Type 5 should create PriorityBlockingQueue", priorityQueue);
|
||||
System.out.println("Type 5 (PriorityBlockingQueue): " + priorityQueue.getClass().getSimpleName());
|
||||
|
||||
BlockingQueue<Runnable> resizableQueue = BlockingQueueTypeEnum.createBlockingQueue(9, 1024);
|
||||
Assert.assertNotNull("Type 9 should create ResizableCapacityLinkedBlockingQueue", resizableQueue);
|
||||
System.out.println("Type 9 (ResizableCapacityLinkedBlockingQueue): " + resizableQueue.getClass().getSimpleName());
|
||||
|
||||
System.out.println("Passed: All built-in queue types created successfully");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Case 3: BlockingQueueManager unified creation entry
|
||||
*/
|
||||
@Test
|
||||
public void testBlockingQueueManagerCreation() {
|
||||
System.out.println("\n========== Test Case 3: BlockingQueueManager unified creation entry ==========");
|
||||
|
||||
// Create built-in queue via BlockingQueueManager
|
||||
BlockingQueue<Runnable> queue1 = BlockingQueueManager.createQueue(1, 512);
|
||||
Assert.assertNotNull("Should successfully create queue", queue1);
|
||||
Assert.assertTrue("Should create ArrayBlockingQueue", queue1 instanceof ArrayBlockingQueue);
|
||||
|
||||
// Create by type name
|
||||
BlockingQueue<Runnable> queue2 = BlockingQueueManager.createQueue("ArrayBlockingQueue", 1024);
|
||||
Assert.assertNotNull("Should successfully create queue by name", queue2);
|
||||
Assert.assertTrue("Should create ArrayBlockingQueue", queue2 instanceof ArrayBlockingQueue);
|
||||
|
||||
// Test default queue (null type)
|
||||
BlockingQueue<Runnable> defaultQueue = BlockingQueueManager.createQueue("", 1024);
|
||||
Assert.assertNotNull("Null type should create default queue", defaultQueue);
|
||||
System.out.println("Default queue type: " + defaultQueue.getClass().getSimpleName());
|
||||
|
||||
System.out.println("Passed: BlockingQueueManager unified entry works");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Case 4: Queue type recognition
|
||||
*/
|
||||
@Test
|
||||
public void testQueueTypeRecognition() {
|
||||
System.out.println("\n========== Test Case 4: Queue type recognition ==========");
|
||||
|
||||
BlockingQueue<Runnable> arrayQueue = new ArrayBlockingQueue<>(256);
|
||||
Integer type1 = BlockingQueueManager.getQueueType(arrayQueue);
|
||||
System.out.println("ArrayBlockingQueue recognized type: " + type1);
|
||||
Assert.assertEquals("Should be recognized as type 1", Integer.valueOf(1), type1);
|
||||
|
||||
BlockingQueue<Runnable> linkedQueue = new LinkedBlockingQueue<>(512);
|
||||
Integer type2 = BlockingQueueManager.getQueueType(linkedQueue);
|
||||
System.out.println("LinkedBlockingQueue recognized type: " + type2);
|
||||
Assert.assertEquals("Should be recognized as type 2", Integer.valueOf(2), type2);
|
||||
|
||||
String name1 = BlockingQueueManager.getQueueName(arrayQueue);
|
||||
System.out.println("ArrayBlockingQueue queue name: " + name1);
|
||||
Assert.assertEquals("Should return correct name", "ArrayBlockingQueue", name1);
|
||||
|
||||
System.out.println("Passed: Queue type recognition works");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Case 5: Queue config validation
|
||||
*/
|
||||
@Test
|
||||
public void testQueueConfigValidation() {
|
||||
System.out.println("\n========== Test Case 5: Queue config validation ==========");
|
||||
|
||||
// Valid config
|
||||
boolean valid1 = BlockingQueueManager.validateQueueConfig(1, 1024);
|
||||
Assert.assertTrue("ArrayBlockingQueue config should be valid", valid1);
|
||||
System.out.println("ArrayBlockingQueue (type=1, capacity=1024): " + (valid1 ? "valid" : "invalid"));
|
||||
|
||||
// SynchronousQueue does not need capacity
|
||||
boolean valid2 = BlockingQueueManager.validateQueueConfig(4, null);
|
||||
Assert.assertTrue("SynchronousQueue without capacity should be valid", valid2);
|
||||
System.out.println("SynchronousQueue (type=4, capacity=null): " + (valid2 ? "valid" : "invalid"));
|
||||
|
||||
// Invalid capacity
|
||||
boolean invalid1 = BlockingQueueManager.validateQueueConfig(1, -1);
|
||||
Assert.assertFalse("Negative capacity should be invalid", invalid1);
|
||||
System.out.println("ArrayBlockingQueue (type=1, capacity=-1): " + (invalid1 ? "valid" : "invalid"));
|
||||
|
||||
// Invalid type
|
||||
boolean invalid2 = BlockingQueueManager.validateQueueConfig(null, 1024);
|
||||
Assert.assertFalse("Null type should be invalid", invalid2);
|
||||
System.out.println("null type: " + (invalid2 ? "valid" : "invalid"));
|
||||
|
||||
System.out.println("Passed: Queue config validation works");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Case 6: Queue capacity change capability
|
||||
*/
|
||||
@Test
|
||||
public void testQueueCapacityChange() {
|
||||
System.out.println("\n========== Test Case 6: Queue capacity change capability ==========");
|
||||
|
||||
// ResizableCapacityLinkedBlockingQueue supports dynamic resizing
|
||||
BlockingQueue<Runnable> resizableQueue = BlockingQueueTypeEnum.createBlockingQueue(9, 512);
|
||||
boolean canResize = BlockingQueueManager.canChangeCapacity(resizableQueue);
|
||||
System.out.println("ResizableCapacityLinkedBlockingQueue resizable: " + canResize);
|
||||
Assert.assertTrue("ResizableCapacityLinkedBlockingQueue should be resizable", canResize);
|
||||
|
||||
// ArrayBlockingQueue does not support dynamic resizing
|
||||
BlockingQueue<Runnable> arrayQueue = new ArrayBlockingQueue<>(256);
|
||||
boolean cannotResize = BlockingQueueManager.canChangeCapacity(arrayQueue);
|
||||
System.out.println("ArrayBlockingQueue resizable: " + cannotResize);
|
||||
Assert.assertFalse("ArrayBlockingQueue should not be resizable", cannotResize);
|
||||
|
||||
System.out.println("Passed: Queue capacity change capability recognized correctly");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Case 7: Queue type-name conversion
|
||||
*/
|
||||
@Test
|
||||
public void testQueueTypeNameConversion() {
|
||||
System.out.println("\n========== Test Case 7: Queue type-name conversion ==========");
|
||||
|
||||
// Type to name
|
||||
String name1 = BlockingQueueTypeEnum.getBlockingQueueNameByType(1);
|
||||
Assert.assertEquals("Type 1 should be converted to ArrayBlockingQueue", "ArrayBlockingQueue", name1);
|
||||
System.out.println("Type 1 -> " + name1);
|
||||
|
||||
String name2 = BlockingQueueTypeEnum.getBlockingQueueNameByType(2);
|
||||
Assert.assertEquals("Type 2 should be converted to LinkedBlockingQueue", "LinkedBlockingQueue", name2);
|
||||
System.out.println("Type 2 -> " + name2);
|
||||
|
||||
// Name to type
|
||||
Integer type1 = BlockingQueueTypeEnum.getBlockingQueueTypeEnumByName("ArrayBlockingQueue").getType();
|
||||
Assert.assertEquals("ArrayBlockingQueue should be converted to type 1", Integer.valueOf(1), type1);
|
||||
System.out.println("ArrayBlockingQueue -> " + type1);
|
||||
|
||||
Integer type2 = BlockingQueueTypeEnum.getBlockingQueueTypeEnumByName("LinkedBlockingQueue").getType();
|
||||
Assert.assertEquals("LinkedBlockingQueue should be converted to type 2", Integer.valueOf(2), type2);
|
||||
System.out.println("LinkedBlockingQueue -> " + type2);
|
||||
|
||||
System.out.println("Passed: Queue type-name conversion works");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Case 8: Queue type comparison (simulate change detection)
|
||||
*/
|
||||
@Test
|
||||
public void testQueueTypeComparison() {
|
||||
System.out.println("\n========== Test Case 8: Queue type change detection ==========");
|
||||
|
||||
BlockingQueue<Runnable> currentQueue = new LinkedBlockingQueue<>(1024);
|
||||
Integer currentType = BlockingQueueManager.getQueueType(currentQueue);
|
||||
|
||||
// Case 1: No change
|
||||
Integer requestedType1 = 2; // LinkedBlockingQueue
|
||||
boolean typeChanged1 = !currentType.equals(requestedType1);
|
||||
System.out.println("Current type: " + currentType + ", Requested: " + requestedType1 + ", Changed: " + typeChanged1);
|
||||
Assert.assertFalse("Same type should not be detected as change", typeChanged1);
|
||||
|
||||
// Case 2: Type changed
|
||||
Integer requestedType2 = 1; // ArrayBlockingQueue
|
||||
boolean typeChanged2 = !currentType.equals(requestedType2);
|
||||
System.out.println("Current type: " + currentType + ", Requested: " + requestedType2 + ", Changed: " + typeChanged2);
|
||||
Assert.assertTrue("Different type should be detected as change", typeChanged2);
|
||||
|
||||
System.out.println("Passed: Queue type change detection works");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Case 9: Verify queue SPI support consistent with rejection policy SPI
|
||||
*/
|
||||
@Test
|
||||
public void testQueueSpiConsistencyWithRejectedPolicy() {
|
||||
System.out.println("\n========== Test Case 9: Queue SPI consistency with rejection policy ==========");
|
||||
|
||||
// Queue SPI feature validation
|
||||
System.out.println("Queue SPI supported features:");
|
||||
System.out.println("1.SPI interface definition: CustomBlockingQueue");
|
||||
System.out.println("2.Type ID identification: getType() returns unique int ID");
|
||||
System.out.println("3.Name identification: getName() returns queue name");
|
||||
System.out.println("4.Instance creation: generateBlockingQueue() creates queue instance");
|
||||
System.out.println("5.Unified creation entry: BlockingQueueManager.createQueue()");
|
||||
System.out.println("6.Built-in type support: BlockingQueueTypeEnum provides 6 built-in queues");
|
||||
System.out.println("7.SPI extension support: loaded via ServiceLoaderRegistry");
|
||||
System.out.println("8.Type recognition: BlockingQueueManager.getQueueType()");
|
||||
System.out.println("9.Config validation: BlockingQueueManager.validateQueueConfig()");
|
||||
System.out.println("10.Dynamic switching: ServerThreadPoolDynamicRefresh.handleQueueChanges()");
|
||||
|
||||
// Compare with rejection policy
|
||||
System.out.println("\nComparison with Rejection Policy SPI:");
|
||||
System.out.println("Rejection Policy SPI: RejectedExecutionHandler + RejectedPolicyTypeEnum");
|
||||
System.out.println("Queue SPI: CustomBlockingQueue + BlockingQueueTypeEnum");
|
||||
System.out.println("Common features:");
|
||||
System.out.println(" - Unified SPI interface definition");
|
||||
System.out.println(" - Type ID based identification");
|
||||
System.out.println(" - Enum managing built-in types");
|
||||
System.out.println(" - ServiceLoader loading mechanism");
|
||||
System.out.println(" - Unified creation entry");
|
||||
|
||||
System.out.println("Passed: Queue SPI design pattern is consistent with rejection policy");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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 cn.hippo4j.common.executor.support;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* ThreadPoolRebuilder Concurrency Test
|
||||
* Verifies concurrency control and task migration safety during thread pool rebuilding
|
||||
*/
|
||||
public class ThreadPoolRebuilderConcurrencyTest {
|
||||
|
||||
/**
|
||||
* Scenario 1: Concurrent rebuild requests should be serialized
|
||||
* Verification: when multiple threads attempt to rebuild the same thread pool at the same time,
|
||||
* only one succeeds while others are blocked
|
||||
*/
|
||||
@Test
|
||||
public void testConcurrentRebuildSerialize() throws InterruptedException {
|
||||
System.out.println("\n========== Scenario 1: Concurrent rebuild serialization ==========");
|
||||
|
||||
// Note: This test requires the actual ThreadPoolRebuilder class, here it is only a design validation
|
||||
// Actual testing should be executed in the starters/threadpool/server module
|
||||
|
||||
System.out.println("Design validation: ReentrantLock ensures rebuild operations for the same threadPoolId are serialized");
|
||||
System.out.println(" - tryLock() returns immediately to avoid blocking");
|
||||
System.out.println(" - Subsequent requests receive warning logs and return false");
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario 2: Task migration tracking and error handling
|
||||
* Verification: migration process records success/failure counts,
|
||||
* failures throw exceptions that trigger rollback
|
||||
*/
|
||||
@Test
|
||||
public void testTaskTransferTracking() {
|
||||
System.out.println("\n========== Scenario 2: Task migration tracking ==========");
|
||||
|
||||
ThreadPoolExecutor fromPool = new ThreadPoolExecutor(
|
||||
2, 4, 60, TimeUnit.SECONDS,
|
||||
new LinkedBlockingQueue<>(100));
|
||||
|
||||
ThreadPoolExecutor toPool = new ThreadPoolExecutor(
|
||||
2, 4, 60, TimeUnit.SECONDS,
|
||||
new ArrayBlockingQueue<>(100));
|
||||
|
||||
// Submit 10 tasks to source pool
|
||||
for (int i = 0; i < 10; i++) {
|
||||
fromPool.execute(() -> {
|
||||
try {
|
||||
Thread.sleep(5000); // Simulate long running task
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Verify tasks in queue
|
||||
int queuedTasks = fromPool.getQueue().size();
|
||||
System.out.println("Tasks in source pool queue: " + queuedTasks);
|
||||
Assert.assertTrue("Queue should contain tasks", queuedTasks > 0);
|
||||
|
||||
System.out.println("Design validation: migration process records transferredCount and failedCount");
|
||||
System.out.println(" - Successful transfers logged with count");
|
||||
System.out.println(" - Failures throw exceptions and trigger rollback");
|
||||
|
||||
fromPool.shutdownNow();
|
||||
toPool.shutdownNow();
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario 3: Rollback old pool state check
|
||||
* Verification: after rollback, old pool termination status is checked
|
||||
* and warnings are logged for unfinished tasks
|
||||
*/
|
||||
@Test
|
||||
public void testRollbackAndOldPoolStatus() {
|
||||
System.out.println("\n========== Scenario 3: Rollback state check ==========");
|
||||
|
||||
System.out.println("Design validation: rollback checks old pool state");
|
||||
System.out.println(" - Call oldExecutor.isTerminated() to verify termination");
|
||||
System.out.println(" - If not terminated, log warnings with activeCount");
|
||||
System.out.println(" - Prevent resource leaks from unfinished tasks");
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario 4: Atomicity of registry switch
|
||||
* Verification: switchRegistry operation runs under lock protection
|
||||
* to prevent concurrent overwrites
|
||||
*/
|
||||
@Test
|
||||
public void testRegistrySwitchAtomicity() {
|
||||
System.out.println("\n========== Scenario 4: Registry switch atomicity ==========");
|
||||
|
||||
System.out.println("Design validation: registry switch runs under ReentrantLock protection");
|
||||
System.out.println(" - switchRegistry invoked inside doRebuildAndSwitch");
|
||||
System.out.println(" - Entire doRebuildAndSwitch runs under lock");
|
||||
System.out.println(" - Prevent inconsistent registry state due to concurrent rebuilds");
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario 5: New task submission during migration
|
||||
* Verification: after registry switch, new tasks are routed to the new pool
|
||||
*/
|
||||
@Test
|
||||
public void testNewTasksDuringMigration() throws InterruptedException {
|
||||
System.out.println("\n========== Scenario 5: New task routing during migration ==========");
|
||||
|
||||
ThreadPoolExecutor oldPool = new ThreadPoolExecutor(
|
||||
2, 4, 60, TimeUnit.SECONDS,
|
||||
new LinkedBlockingQueue<>(10));
|
||||
|
||||
ThreadPoolExecutor newPool = new ThreadPoolExecutor(
|
||||
2, 4, 60, TimeUnit.SECONDS,
|
||||
new ArrayBlockingQueue<>(10));
|
||||
|
||||
CountDownLatch latch = new CountDownLatch(5);
|
||||
AtomicInteger oldPoolTasks = new AtomicInteger(0);
|
||||
AtomicInteger newPoolTasks = new AtomicInteger(0);
|
||||
|
||||
// Submit tasks to old pool
|
||||
for (int i = 0; i < 3; i++) {
|
||||
oldPool.execute(() -> {
|
||||
oldPoolTasks.incrementAndGet();
|
||||
latch.countDown();
|
||||
});
|
||||
}
|
||||
|
||||
// Simulate registry switch: new tasks submitted to new pool
|
||||
for (int i = 0; i < 2; i++) {
|
||||
newPool.execute(() -> {
|
||||
newPoolTasks.incrementAndGet();
|
||||
latch.countDown();
|
||||
});
|
||||
}
|
||||
|
||||
latch.await(2, TimeUnit.SECONDS);
|
||||
|
||||
System.out.println("Tasks executed by old pool: " + oldPoolTasks.get());
|
||||
System.out.println("Tasks executed by new pool: " + newPoolTasks.get());
|
||||
Assert.assertEquals("Old pool should execute 3 tasks", 3, oldPoolTasks.get());
|
||||
Assert.assertEquals("New pool should execute 2 tasks", 2, newPoolTasks.get());
|
||||
|
||||
System.out.println("Test passed: after registry switch, new tasks are routed to the new pool");
|
||||
|
||||
oldPool.shutdownNow();
|
||||
newPool.shutdownNow();
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario 6: Rollback protection on migration failure
|
||||
* Verification: when migration fails, registry is rolled back and new pool shut down
|
||||
*/
|
||||
@Test
|
||||
public void testRollbackOnTransferFailure() {
|
||||
System.out.println("\n========== Scenario 6: Migration failure rollback ==========");
|
||||
|
||||
System.out.println("Design validation: rollback logic on migration failure");
|
||||
System.out.println(" - transferSuccess flag controls rollback");
|
||||
System.out.println(" - On failure: ThreadPoolExecutorRegistry.putHolder(oldHolder)");
|
||||
System.out.println(" - On failure: safeShutdownNow(newExecutor)");
|
||||
System.out.println(" - Return false to notify caller of rebuild failure");
|
||||
System.out.println(" - Old pool continues serving without business interruption");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
cn.hippo4j.common.executor.support.BlockingQueueSpiTest$TestCustomQueue
|
||||
Loading…
Reference in new issue