private void removeTimeoutNodes() {
        assertRunningInMainThread();
        Collection<BlockedNode> removedNodes =
                blocklistTracker.removeTimeoutNodes(System.currentTimeMillis());
        if (!removedNodes.isEmpty()) {
            if (log.isDebugEnabled()) {
                log.debug(
                        "Remove {} timeout blocked nodes, details {}. "
                                + "Total {} blocked nodes currently, details: {}.",
                        removedNodes.size(),
                        removedNodes,
                        blocklistTracker.getAllBlockedNodes().size(),
                        blocklistTracker.getAllBlockedNodes());
            } else {
                log.info(
                        "Remove {} timeout blocked nodes. Total {} blocked nodes currently.",
                        removedNodes.size(),
                        blocklistTracker.getAllBlockedNodes().size());
            }
            blocklistContext.unblockResources(removedNodes);
        }
    }
@Test
    void testRemoveTimeoutNodes() throws Exception {
        final ComponentMainThreadExecutor mainThreadExecutor =
                ComponentMainThreadExecutorServiceAdapter.forSingleThreadExecutor(
                        EXECUTOR_EXTENSION.getExecutor());

        final CompletableFuture<Collection<BlockedNode>> unblockResourcesFuture =
                new CompletableFuture<>();

        final TestBlocklistContext context =
                TestBlocklistContext.newBuilder()
                        .setUnblockResourcesConsumer(unblockResourcesFuture::complete)
                        .build();

        try (DefaultBlocklistHandler handler =
                createDefaultBlocklistHandler(context, mainThreadExecutor)) {
            CompletableFuture.supplyAsync(
                            () -> {
                                BlockedNode blockedNode =
                                        new BlockedNode(
                                                "node",
                                                "cause",
                                                System.currentTimeMillis() + 1000L);
                                handler.addNewBlockedNodes(Collections.singleton(blockedNode));
                                assertThat(handler.getAllBlockedNodeIds()).hasSize(1);
                                return blockedNode;
                            },
                            mainThreadExecutor)
                    // wait for the timeout to occur
                    .thenAcceptBoth(
                            unblockResourcesFuture,
                            (blockedNode, unblockResources) -> {
                                assertThat(handler.getAllBlockedNodeIds()).isEmpty();
                                assertThat(unblockResources).containsExactly(blockedNode);
                            })
                    .get();
        }
    }