GroupAvailabilityCalculator { public Set<Integer> nodesThatShouldBeDown(ClusterState state) { if (distribution == null) { return Collections.emptySet(); } if (isFlatCluster(distribution.getRootGroup())) { return new HashSet<>(); } InsufficientAvailabilityGroupVisitor visitor = new InsufficientAvailabilityGroupVisitor(state); distribution.visitGroups(visitor); return visitor.implicitlyDownNodeIndices(); } private  GroupAvailabilityCalculator(Distribution distribution,
                                        double minNodeRatioPerGroup); static Builder builder(); Set<Integer> nodesThatShouldBeDown(ClusterState state);  }
@Test public void flat_cluster_does_not_implicitly_take_down_nodes() { GroupAvailabilityCalculator calc = calcForFlatCluster(5, 0.99); assertThat(calc.nodesThatShouldBeDown(clusterState( "distributor:5 storage:5 .1.s:d .2.s:d")), equalTo(emptySet())); }
@Test public void group_node_down_edge_implicitly_marks_down_rest_of_nodes_in_group() { GroupAvailabilityCalculator calc = calcForHierarchicCluster( DistributionBuilder.withGroups(3).eachWithNodeCount(2), 0.51); assertThat(calc.nodesThatShouldBeDown(clusterState( "distributor:6 storage:6 .4.s:d")), equalTo(indices(5))); }
@Test public void min_ratio_per_group_is_closed_interval() { GroupAvailabilityCalculator calc = calcForHierarchicCluster( DistributionBuilder.withGroups(3).eachWithNodeCount(2), 0.50); assertThat(calc.nodesThatShouldBeDown(clusterState( "distributor:6 storage:6 .4.s:d")), equalTo(emptySet())); }
@Test public void retired_node_is_counted_as_down() { GroupAvailabilityCalculator calc = calcForHierarchicCluster( DistributionBuilder.withGroups(3).eachWithNodeCount(2), 0.99); assertThat(calc.nodesThatShouldBeDown(clusterState( "distributor:6 storage:6 .1.s:r")), equalTo(indices(0))); }
@Test public void initializing_node_not_counted_as_down() { GroupAvailabilityCalculator calc = calcForHierarchicCluster( DistributionBuilder.withGroups(3).eachWithNodeCount(2), 0.99); assertThat(calc.nodesThatShouldBeDown(clusterState( "distributor:6 storage:6 .4.s:i")), equalTo(emptySet())); }
@Test public void maintenance_node_not_counted_as_down() { GroupAvailabilityCalculator calc = calcForHierarchicCluster( DistributionBuilder.withGroups(3).eachWithNodeCount(2), 0.99); assertThat(calc.nodesThatShouldBeDown(clusterState( "distributor:6 storage:6 .4.s:m")), equalTo(emptySet())); }
@Test public void existing_maintenance_node_not_implicitly_downed_when_group_taken_down() { GroupAvailabilityCalculator calc = calcForHierarchicCluster( DistributionBuilder.withGroups(3).eachWithNodeCount(3), 0.99); assertThat(calc.nodesThatShouldBeDown(clusterState( "distributor:9 storage:9 .4.s:m .5.s:d")), equalTo(indices(3))); }
@Test public void existing_retired_node_not_implicitly_downed_when_group_taken_down() { GroupAvailabilityCalculator calc = calcForHierarchicCluster( DistributionBuilder.withGroups(3).eachWithNodeCount(3), 0.99); assertThat(calc.nodesThatShouldBeDown(clusterState( "distributor:9 storage:9 .4.s:r .5.s:d")), equalTo(indices(3))); }
@Test public void down_to_down_edge_keeps_group_down() { GroupAvailabilityCalculator calc = calcForHierarchicCluster( DistributionBuilder.withGroups(2).eachWithNodeCount(4), 0.76); assertThat(calc.nodesThatShouldBeDown(clusterState( "distributor:8 storage:8 .1.s:d")), equalTo(indices(0, 2, 3))); assertThat(calc.nodesThatShouldBeDown(clusterState( "distributor:8 storage:8 .1.s:d .2.s:d")), equalTo(indices(0, 3))); }
@Test public void implicitly_downed_node_at_state_end_is_counted_as_explicitly_down() { GroupAvailabilityCalculator calc = calcForHierarchicCluster( DistributionBuilder.withGroups(3).eachWithNodeCount(2), 0.99); assertThat(calc.nodesThatShouldBeDown(clusterState( "distributor:6 storage:5")), equalTo(indices(4))); }
@Test public void non_uniform_group_sizes_are_supported() { GroupAvailabilityCalculator calc = calcForHierarchicCluster( DistributionBuilder.withGroupNodes(1, 2, 3, 4), 0.67); assertThat(calc.nodesThatShouldBeDown(clusterState( "distributor:10 storage:10")), equalTo(emptySet())); assertThat(calc.nodesThatShouldBeDown(clusterState( "distributor:10 storage:10 .0.s:d")), equalTo(emptySet())); assertThat(calc.nodesThatShouldBeDown(clusterState( "distributor:10 storage:10 .1.s:d")), equalTo(indices(2))); assertThat(calc.nodesThatShouldBeDown(clusterState( "distributor:10 storage:10 .3.s:d")), equalTo(indices(4, 5))); assertThat(calc.nodesThatShouldBeDown(clusterState( "distributor:10 storage:10 .7.s:d")), equalTo(emptySet())); assertThat(calc.nodesThatShouldBeDown(clusterState( "distributor:10 storage:9 .7.s:d")), equalTo(indices(6, 8))); }
@Test public void min_ratio_of_zero_never_takes_down_groups_implicitly() { GroupAvailabilityCalculator calc = calcForHierarchicCluster( DistributionBuilder.withGroups(2).eachWithNodeCount(4), 0.0); assertThat(calc.nodesThatShouldBeDown(clusterState( "distributor:8 storage:8")), equalTo(emptySet())); assertThat(calc.nodesThatShouldBeDown(clusterState( "distributor:8 storage:8 .0.s:d .4.s:d")), equalTo(emptySet())); assertThat(calc.nodesThatShouldBeDown(clusterState( "distributor:8 storage:8 .0.s:d .1.s:d .4.s:d .5.s:d")), equalTo(emptySet())); assertThat(calc.nodesThatShouldBeDown(clusterState( "distributor:8 storage:8 .0.s:d .1.s:d .2.s:d .4.s:d .5.s:d .6.s:d")), equalTo(emptySet())); }