private void durableFailover(int redundancyLevel) {
    // Start server 1
    server1Port = server1VM.invoke(() -> createCacheServer(regionName, true));

    final int server2Port = server2VM.invoke(() -> createCacheServer(regionName, true));

    // Stop server 2
    server2VM.invoke((SerializableRunnableIF) CacheServerTestUtil::closeCache);

    // Start a durable client
    durableClientId = getName() + "_client";

    Pool clientPool;
    if (redundancyLevel == 1) {
      clientPool = getClientPool(NetworkUtils.getServerHostName(), server1Port,
          server2Port, true);
    } else {
      clientPool = getClientPool(NetworkUtils.getServerHostName(), server1Port,
          server2Port, true, 0);
    }

    durableClientVM.invoke(CacheServerTestUtil::disableShufflingOfEndpoints);
    durableClientVM.invoke(() -> createCacheClient(clientPool, regionName,
        getClientDistributedSystemProperties(durableClientId, VERY_LONG_DURABLE_TIMEOUT_SECONDS),
        true));

    registerInterest(durableClientVM, regionName, true, InterestResultPolicy.NONE);

    // Send clientReady message
    durableClientVM.invoke("Send clientReady", new CacheSerializableRunnable() {
      @Override
      public void run2() throws CacheException {
        getCache().readyForEvents();
      }
    });

    // Verify the durable client is connected to server1
    verifyDurableClientPresent(VERY_LONG_DURABLE_TIMEOUT_SECONDS, durableClientId, server1VM);

    // Re-start server2 and wait for it to be up.
    server2VM.invoke(() -> createCacheServer(regionName, true, server2Port));

    if (redundancyLevel == 1) {
      // Verify the durable client is connected to server2
      verifyDurableClientPresent(VERY_LONG_DURABLE_TIMEOUT_SECONDS, durableClientId, server2VM);
    }
    // Start normal publisher client
    publisherClientVM.invoke(() -> createCacheClient(getClientPool(
        NetworkUtils.getServerHostName(), server1Port, server2Port,
        false), regionName));

    // Publish some entries
    publishEntries(publisherClientVM, 0, 1);

    // Verify the durable client received the updates
    checkListenerEvents(1, 1, -1, durableClientVM);

    // Wait to until the HARegionQueue has emptied before disconnecting the Durable client
    // to avoid having left over messages that processed, in particular, the key 0 message
    waitUntilHARegionQueueSizeIsZero(server1VM);

    // Stop the durable client, which discards the known entry
    disconnectDurableClient(true);

    // Put key 1 during client downtime
    publishEntries(publisherClientVM, 1, 1);

    // Re-start the durable client that is kept alive on the server
    restartDurableClient(VERY_LONG_DURABLE_TIMEOUT_SECONDS, clientPool, true);

    // Re-register interest
    registerInterest(durableClientVM, regionName, true, InterestResultPolicy.NONE);

    // Send clientReady message
    sendClientReady(durableClientVM);

    // put key 2
    publishEntries(publisherClientVM, 2, 1);

    // Verify the durable client received the 2 total updates
    checkListenerEvents(2, 1, -1, durableClientVM);

    // Verify that the 0 entry is not present, but that 2 is.
    durableClientVM.invoke("Get", () -> await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> {
      Region<Object, Object> region = getCache().getRegion(regionName);
      assertThat(region).isNotNull();

      assertThat(region.getEntry("0")).isNull();
      assertThat(region.getEntry("2")).isNotNull();
    }));

    // Stop server 1
    server1VM.invoke((SerializableRunnableIF) CacheServerTestUtil::closeCache);

    // Verify durable client failed over if redundancyLevel=0
    if (redundancyLevel == 0) {
      server2VM.invoke(this::verifyClientHasConnected);
    }

    // put key 3
    publishEntries(publisherClientVM, 3, 1);

    // Verify the durable client received the updates after failover
    checkListenerEvents(3, 1, -1, durableClientVM);

    // Stop the durable client
    durableClientVM.invoke((SerializableRunnableIF) CacheServerTestUtil::closeCache);

    // Stop the publisher client
    publisherClientVM.invoke((SerializableRunnableIF) CacheServerTestUtil::closeCache);

    // Stop server 2
    server2VM.invoke((SerializableRunnableIF) CacheServerTestUtil::closeCache);
  }
Test a durable client with 2 servers where the client fails over from one to another server
with a publisher/feeder performing operations and the client verifying updates received.
Redundancy level is set to 1 for this test case.
geode-cq/src/distributedTest/java/org/apache/geode/internal/cache/tier/sockets/DurableClientTestCase.java