|
76 | 76 | import java.util.concurrent.ConcurrentLinkedDeque; |
77 | 77 |
|
78 | 78 | import static org.junit.Assert.assertEquals; |
| 79 | +import static org.junit.Assert.assertNotNull; |
79 | 80 | import static org.junit.Assert.assertTrue; |
80 | 81 | import static org.mockito.ArgumentMatchers.any; |
81 | 82 | import static org.mockito.ArgumentMatchers.eq; |
@@ -1623,4 +1624,71 @@ private List<ByteBuffer> FakedFirstFetchTsBlockResult() { |
1623 | 1624 |
|
1624 | 1625 | return Collections.singletonList(tsBlock); |
1625 | 1626 | } |
| 1627 | + |
| 1628 | + // Regression test for graceful shutdown |
| 1629 | + @Test(timeout = 5000) |
| 1630 | + public void testCloseNotifiesWaitingThreads() throws Exception { |
| 1631 | + SessionPool pool = |
| 1632 | + new SessionPool.Builder() |
| 1633 | + .host("localhost") |
| 1634 | + .port(6667) |
| 1635 | + .user("root") |
| 1636 | + .password("root") |
| 1637 | + .maxSize(1) |
| 1638 | + .waitToGetSessionTimeoutInMs(10000) |
| 1639 | + // notifyAll() |
| 1640 | + .build(); |
| 1641 | + |
| 1642 | + try { |
| 1643 | + Session mockSession = Mockito.mock(Session.class); |
| 1644 | + ConcurrentLinkedDeque<ISession> queue = |
| 1645 | + (ConcurrentLinkedDeque<ISession>) Whitebox.getInternalState(pool, "queue"); |
| 1646 | + queue.push(mockSession); |
| 1647 | + Whitebox.setInternalState(pool, "size", 1); |
| 1648 | + |
| 1649 | + ISession occupiedSession = (ISession) Whitebox.invokeMethod(pool, "getSession"); |
| 1650 | + assertEquals(mockSession, occupiedSession); |
| 1651 | + assertEquals(0, queue.size()); |
| 1652 | + |
| 1653 | + final Exception[] caughtException = {null}; |
| 1654 | + |
| 1655 | + Thread waiterThread = |
| 1656 | + new Thread( |
| 1657 | + () -> { |
| 1658 | + try { |
| 1659 | + Whitebox.invokeMethod(pool, "getSession"); |
| 1660 | + } catch (Exception e) { |
| 1661 | + caughtException[0] = e; |
| 1662 | + } |
| 1663 | + }); |
| 1664 | + waiterThread.start(); |
| 1665 | + |
| 1666 | + Thread.sleep(100); |
| 1667 | + |
| 1668 | + long closeStartTime = System.currentTimeMillis(); |
| 1669 | + pool.close(); |
| 1670 | + long closeEndTime = System.currentTimeMillis(); |
| 1671 | + |
| 1672 | + waiterThread.join(1000); |
| 1673 | + |
| 1674 | + assertNotNull("Waiter thread should have caught an exception", caughtException[0]); |
| 1675 | + assertTrue( |
| 1676 | + "Exception should be IoTDBConnectionException", |
| 1677 | + caughtException[0] instanceof IoTDBConnectionException); |
| 1678 | + assertTrue( |
| 1679 | + "Exception message should indicate pool is closed", |
| 1680 | + caughtException[0].getMessage().contains("closed")); |
| 1681 | + |
| 1682 | + long closeDuration = closeEndTime - closeStartTime; |
| 1683 | + assertTrue( |
| 1684 | + "close() should complete quickly, but took " + closeDuration + "ms", |
| 1685 | + closeDuration < 1000); |
| 1686 | + |
| 1687 | + } finally { |
| 1688 | + try { |
| 1689 | + pool.close(); |
| 1690 | + } catch (Exception e) { |
| 1691 | + } |
| 1692 | + } |
| 1693 | + } |
1626 | 1694 | } |
0 commit comments