@Test
  public void basicTestLifetimeExpire()
      throws CacheException, InterruptedException {
    final String name = getName();

    AsyncInvocation<Void> putAI = null;
    AsyncInvocation<Void> putAI2 = null;

    try {

      // Create two cache servers

      vm0.invoke("Create Cache Server", () -> {
        RegionFactory<Object, Object> factory = getBridgeServerRegionAttributes(null, null);
        factory.addCacheListener(new DelayListener());
        createRegion(name, factory);
        startBridgeServer(0);

      });

      final int port0 = vm0.invoke(ConnectionPoolDUnitTest::getCacheServerPort);
      final String host0 = NetworkUtils.getServerHostName();
      vm1.invoke("Create Cache Server", () -> {
        RegionFactory<Object, Object> factory = getBridgeServerRegionAttributes(null, null);
        factory.addCacheListener(new DelayListener());
        createRegion(name, factory);
        startBridgeServer(0);

      });
      final int port1 = vm1.invoke(ConnectionPoolDUnitTest::getCacheServerPort);
      // we only had to stop it to reserve a port
      vm1.invoke("Stop CacheServer", () -> stopBridgeServer(getCache()));

      // Create one bridge client in this VM

      vm2.invoke("Create region", () -> {
        getLonerSystem();
        getCache();
        RegionFactory<Object, Object> factory = getCache().createRegionFactory();
        factory.setScope(Scope.LOCAL);
        factory.setConcurrencyChecksEnabled(false);
        configureConnectionPoolWithNameAndFactory(factory, host0, new int[] {port0, port1},
            false, -1, 0, null, TEST_POOL_NAME, PoolManager.createFactory(), 100, 500, 500, -1);

        Region<Object, Object> region = createRegion(name, factory);

        // force connections to form
        region.put("keyInit", 0);
        region.put("keyInit2", 0);
      });

      // Launch async thread that puts objects into cache. This thread will execute until
      // the test has ended.
      putAI = vm2.invokeAsync("Put objects", () -> {
        Region<Object, Object> region = getRootRegion().getSubregion(name);
        PoolImpl pool = getPool(region);
        PoolStats stats = pool.getStats();
        baselineLifetimeCheck = stats.getLoadConditioningCheck();
        baselineLifetimeExtensions = stats.getLoadConditioningExtensions();
        baselineLifetimeConnect = stats.getLoadConditioningConnect();
        baselineLifetimeDisconnect = stats.getLoadConditioningDisconnect();
        try {
          int count = 0;
          while (!stopTestLifetimeExpire) {
            count++;
            region.put("keyAI1", count);
          }
        } catch (NoAvailableServersException ex) {
          if (!stopTestLifetimeExpire) {
            throw ex;
          }

        }
      });
      putAI2 = vm2.invokeAsync("Put objects", () -> {
        Region<Object, Object> region = getRootRegion().getSubregion(name);
        try {
          int count = 0;
          while (!stopTestLifetimeExpire) {
            count++;
            region.put("keyAI2", count);
          }
        } catch (NoAvailableServersException ex) {
          if (!stopTestLifetimeExpire) {
            throw ex;
          }
        }
      });

      vm2.invoke("verify1Server", () -> {
        Region<Object, Object> region = getRootRegion().getSubregion(name);
        PoolImpl pool = getPool(region);
        final PoolStats stats = pool.getStats();
        verifyServerCount(pool, 1);

        await().until(() -> stats.getLoadConditioningCheck() >= (10 + baselineLifetimeCheck));

        // make sure no replacements are happening.
        // since we have 2 threads and 2 cnxs and 1 server
        // when lifetimes are up we should only want to connect back to the
        // server we are already connected to and thus just extend our lifetime
        assertThat(stats.getLoadConditioningCheck() >= (10 + baselineLifetimeCheck))
            .describedAs("baselineLifetimeCheck=" + baselineLifetimeCheck
                + " but stats.getLoadConditioningCheck()=" + stats.getLoadConditioningCheck())
            .isTrue();
        baselineLifetimeCheck = stats.getLoadConditioningCheck();
        assertThat(stats.getLoadConditioningExtensions())
            .isGreaterThan(baselineLifetimeExtensions);
        assertThat(stats.getLoadConditioningConnect()).isEqualTo(baselineLifetimeConnect);
        assertThat(stats.getLoadConditioningDisconnect()).isEqualTo(baselineLifetimeDisconnect);
      });

      await().until(putAI::isAlive);
      await().until(putAI2::isAlive);

    } finally {
      vm2.invoke("Stop Putters", () -> stopTestLifetimeExpire = true);

      try {
        if (putAI != null) {
          // Verify that no exception has occurred in the putter thread
          putAI.await();
        }

        if (putAI2 != null) {
          // Verify that no exception has occurred in the putter thread
          putAI.await();
        }
      } finally {
        vm2.invoke("Stop Putters", () -> stopTestLifetimeExpire = false);
        // Close Pool
        vm2.invoke("Close Pool", () -> {
          Region<Object, Object> region = getRootRegion().getSubregion(name);
          String poolName = region.getAttributes().getPoolName();
          region.localDestroyRegion();
          PoolManager.find(poolName).destroy();
        });
      }
    }
  }
Test BridgeServer failover with connectionsPerServer set to 0
geode-core/src/distributedTest/java/org/apache/geode/cache/ConnectionPoolDUnitTest.java