@@ -475,4 +475,32 @@ void testGetMaxPerRoute() {
475475 Assertions .assertEquals (max , pool .getMaxPerRoute (route ));
476476 }
477477 }
478+
479+ @ Test
480+ void testExpiredEntryDeallocatesSlot () throws Exception {
481+ final HttpConnection conn1 = Mockito .mock (HttpConnection .class );
482+
483+ try (final LaxConnPool <String , HttpConnection > pool = new LaxConnPool <>(1 )) {
484+ // Lease, assign, expire, then release back to available
485+ final Future <PoolEntry <String , HttpConnection >> future1 = pool .lease ("somehost" , null );
486+ final PoolEntry <String , HttpConnection > entry1 = future1 .get ();
487+ entry1 .assignConnection (conn1 );
488+ entry1 .updateExpiry (TimeValue .of (1 , TimeUnit .MILLISECONDS ));
489+ pool .release (entry1 , true );
490+
491+ Thread .sleep (10 );
492+
493+ // Lease again — the expired entry should be discarded and the slot freed
494+ final Future <PoolEntry <String , HttpConnection >> future2 = pool .lease ("somehost" , null );
495+ Assertions .assertTrue (future2 .isDone ());
496+ final PoolEntry <String , HttpConnection > entry2 = future2 .get ();
497+ Assertions .assertNotNull (entry2 );
498+
499+ // The expired entry was discarded, so a new entry was allocated.
500+ // With maxPerRoute=1, this only works if the expired entry's slot was deallocated.
501+ final PoolStats stats = pool .getStats ("somehost" );
502+ Assertions .assertEquals (1 , stats .getLeased ());
503+ Assertions .assertEquals (0 , stats .getAvailable ());
504+ }
505+ }
478506}
0 commit comments