@Override
    public void close() throws Exception {
        if (running.compareAndSet(true, false)) {
            LOG.info("Closing {}.", this);

            curatorFramework.getConnectionStateListenable().removeListener(listener);

            Exception exception = null;

            try {
                treeCache.close();
            } catch (Exception e) {
                exception = e;
            }

            try {
                leaderLatch.close();
            } catch (Exception e) {
                exception = ExceptionUtils.firstOrSuppressed(e, exception);
            }

            ExceptionUtils.tryRethrowException(exception);
        }
    }
@Test
    void testLeaderElectionWithMultipleDrivers() throws Exception {
        final CuratorFrameworkWithUnhandledErrorListener curatorFramework = startCuratorFramework();

        try {
            Set<ElectionDriver> electionDrivers =
                    Stream.generate(
                                    () ->
                                            createLeaderElectionDriver(
                                                    curatorFramework.asCuratorFramework(),
                                                    testingFatalErrorHandlerResource
                                                            .getTestingFatalErrorHandler()))
                            .limit(3)
                            .collect(Collectors.toSet());

            while (!electionDrivers.isEmpty()) {
                final CompletableFuture<Object> anyLeader =
                        CompletableFuture.anyOf(
                                electionDrivers.stream()
                                        .map(ElectionDriver::getLeadershipFuture)
                                        .toArray(CompletableFuture[]::new));

                // wait for any leader
                anyLeader.join();

                final Map<Boolean, Set<ElectionDriver>> leaderAndRest =
                        electionDrivers.stream()
                                .collect(
                                        Collectors.partitioningBy(
                                                ElectionDriver::hasLeadership, Collectors.toSet()));

                assertThat(leaderAndRest.get(true)).hasSize(1);

                // TODO: remove this line after CURATOR-645 is resolved
                // CURATOR-645 covers a bug in the LeaderLatch implementation that causes a race
                // condition if a child node, participating in the leader election, is removed too
                // fast. This results in a different code branch being executed which triggers a
                // reset of the LeaderLatch instead of re-collecting the children to determine the
                // next leader.
                // The issue occurs because LeaderLatch#checkLeadership is not executed
                // transactionally, i.e. retrieving the children and setting up the watcher for the
                // predecessor is not done atomically. This leads to the race condition where a
                // children (the previous leader's node) is removed before setting up the watcher
                // which results in an invalid handling of the situation using reset.
                // Adding some sleep here (simulating the leader actually doing something) will
                // reduce the risk of falling into the race condition because it will give the
                // concurrently running LeaderLatch instances more time to set up the watchers
                // properly.
                Thread.sleep(100);

                Iterables.getOnlyElement(leaderAndRest.get(true)).close();

                electionDrivers = leaderAndRest.get(false);
            }
        } finally {
            curatorFramework.close();
        }
    }