io.cassandrareaper.service.ClusterRepairSchedulerTest.java Source code

Java tutorial

Introduction

Here is the source code for io.cassandrareaper.service.ClusterRepairSchedulerTest.java

Source

/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.cassandrareaper.service;

import io.cassandrareaper.AppContext;
import io.cassandrareaper.ReaperApplicationConfiguration;
import io.cassandrareaper.ReaperException;
import io.cassandrareaper.core.Cluster;
import io.cassandrareaper.core.RepairSchedule;
import io.cassandrareaper.core.RepairUnit;
import io.cassandrareaper.jmx.JmxConnectionFactory;
import io.cassandrareaper.jmx.JmxProxy;
import io.cassandrareaper.storage.MemoryStorage;

import java.time.Duration;
import java.util.Collection;
import java.util.Collections;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.apache.cassandra.repair.RepairParallelism;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;

import static java.lang.String.format;
import static org.fest.assertions.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public final class ClusterRepairSchedulerTest {

    private static final Cluster CLUSTER = new Cluster("cluster1", null, Collections.singleton("127.0.0.1"));
    private static final DateTime TWO_HOURS_AGO = DateTime.now().minusHours(2);
    private static final Duration DELAY_BEFORE_SCHEDULE = Duration.ofMinutes(4);
    private AppContext context;
    private ClusterRepairScheduler clusterRepairAuto;
    private JmxProxy jmxProxy;

    @Before
    public void setup() throws ReaperException {
        context = new AppContext();
        context.storage = new MemoryStorage();
        context.config = TestRepairConfiguration.defaultConfigBuilder()
                .withAutoScheduling(TestRepairConfiguration.defaultAutoSchedulingConfigBuilder().thatIsEnabled()
                        .withTimeBeforeFirstSchedule(DELAY_BEFORE_SCHEDULE).build())
                .build();
        context.jmxConnectionFactory = mock(JmxConnectionFactory.class);
        clusterRepairAuto = new ClusterRepairScheduler(context);
        jmxProxy = mock(JmxProxy.class);

        when(context.jmxConnectionFactory.connectAny(CLUSTER, context.config.getJmxConnectionTimeoutInSeconds()))
                .thenReturn(jmxProxy);

        when(context.jmxConnectionFactory.connectAny(Mockito.any(), Mockito.anyCollection(), Mockito.anyInt()))
                .thenReturn(jmxProxy);
    }

    @Test
    public void schedulesRepairForAllKeyspacesInAllClusters() throws Exception {
        context.storage.addCluster(CLUSTER);
        when(jmxProxy.getKeyspaces()).thenReturn(Lists.newArrayList("keyspace1", "keyspace3"));
        when(jmxProxy.getTableNamesForKeyspace("keyspace1")).thenReturn(Sets.newHashSet("table1"));
        when(jmxProxy.getTableNamesForKeyspace("keyspace3")).thenReturn(Sets.newHashSet("table1"));

        clusterRepairAuto.scheduleRepairs(CLUSTER);

        assertThat(context.storage.getAllRepairSchedules()).hasSize(2);
        assertThatClusterRepairSchedules(context.storage.getRepairSchedulesForCluster(CLUSTER.getName()))
                .hasScheduleCount(2).repairScheduleForKeyspace("keyspace1").hasSameConfigItemsAs(context.config)
                .hasOwner("auto-scheduling").hasNextActivationDateCloseTo(timeOfFirstSchedule()).andThen()
                .repairScheduleForKeyspace("keyspace3").hasSameConfigItemsAs(context.config)
                .hasOwner("auto-scheduling").hasNextActivationDateCloseTo(timeOfFirstSchedule());
    }

    @Test
    public void removeSchedulesForKeyspaceThatNoLongerExists() throws Exception {
        context.storage.addCluster(CLUSTER);
        context.storage.addRepairSchedule(aRepairSchedule(CLUSTER, "keyspace1", TWO_HOURS_AGO));
        context.storage.addRepairSchedule(aRepairSchedule(CLUSTER, "keyspace2", TWO_HOURS_AGO));

        when(jmxProxy.getKeyspaces()).thenReturn(Lists.newArrayList("keyspace1"));

        clusterRepairAuto.scheduleRepairs(CLUSTER);

        assertThat(context.storage.getAllRepairSchedules()).hasSize(1);
        assertThatClusterRepairSchedules(context.storage.getRepairSchedulesForCluster(CLUSTER.getName()))
                .hasScheduleCount(1).repairScheduleForKeyspace("keyspace1").hasCreationTimeCloseTo(TWO_HOURS_AGO);
    }

    @Test
    public void addSchedulesForNewKeyspace() throws Exception {
        context.storage.addCluster(CLUSTER);
        context.storage.addRepairSchedule(aRepairSchedule(CLUSTER, "keyspace1", TWO_HOURS_AGO));

        when(jmxProxy.getKeyspaces()).thenReturn(Lists.newArrayList("keyspace1", "keyspace2"));
        when(jmxProxy.getTableNamesForKeyspace("keyspace1")).thenReturn(Sets.newHashSet("table1"));
        when(jmxProxy.getTableNamesForKeyspace("keyspace2")).thenReturn(Sets.newHashSet("table2"));

        clusterRepairAuto.scheduleRepairs(CLUSTER);

        assertThat(context.storage.getAllRepairSchedules()).hasSize(2);
        assertThatClusterRepairSchedules(context.storage.getRepairSchedulesForCluster(CLUSTER.getName()))
                .hasScheduleCount(2).repairScheduleForKeyspace("keyspace1").hasCreationTime(TWO_HOURS_AGO).andThen()
                .repairScheduleForKeyspace("keyspace2").hasCreationTimeCloseTo(DateTime.now());
    }

    @Test
    public void doesNotScheduleRepairForSystemKeyspaces() throws Exception {
        context.storage.addCluster(CLUSTER);
        when(jmxProxy.getKeyspaces())
                .thenReturn(Lists.newArrayList("system", "system_auth", "system_traces", "keyspace2"));
        when(jmxProxy.getTableNamesForKeyspace("keyspace2")).thenReturn(Sets.newHashSet("table1"));

        clusterRepairAuto.scheduleRepairs(CLUSTER);
        assertThat(context.storage.getAllRepairSchedules()).hasSize(1);
        assertThatClusterRepairSchedules(context.storage.getRepairSchedulesForCluster(CLUSTER.getName()))
                .hasScheduleCount(1).repairScheduleForKeyspace("keyspace2");
    }

    @Test
    public void doesNotScheduleRepairWhenKeyspaceHasNoTable() throws Exception {
        context.storage.addCluster(CLUSTER);
        when(jmxProxy.getKeyspaces()).thenReturn(Lists.newArrayList("keyspace1"));
        when(jmxProxy.getTableNamesForKeyspace("keyspace1")).thenReturn(Sets.newHashSet());

        clusterRepairAuto.scheduleRepairs(CLUSTER);
        assertThat(context.storage.getAllRepairSchedules()).hasSize(0);
    }

    @Test
    public void spreadsKeyspaceScheduling() throws Exception {
        context.config = TestRepairConfiguration.defaultConfigBuilder()
                .withAutoScheduling(TestRepairConfiguration.defaultAutoSchedulingConfigBuilder().thatIsEnabled()
                        .withTimeBeforeFirstSchedule(DELAY_BEFORE_SCHEDULE)
                        .withScheduleSpreadPeriod(Duration.ofHours(6)).build())
                .build();

        context.storage.addCluster(CLUSTER);
        when(jmxProxy.getKeyspaces())
                .thenReturn(Lists.newArrayList("keyspace1", "keyspace2", "keyspace3", "keyspace4"));
        when(jmxProxy.getTableNamesForKeyspace("keyspace1")).thenReturn(Sets.newHashSet("sometable"));
        when(jmxProxy.getTableNamesForKeyspace("keyspace2")).thenReturn(Sets.newHashSet("sometable"));
        when(jmxProxy.getTableNamesForKeyspace("keyspace4")).thenReturn(Sets.newHashSet("sometable"));

        clusterRepairAuto.scheduleRepairs(CLUSTER);
        assertThatClusterRepairSchedules(context.storage.getRepairSchedulesForCluster(CLUSTER.getName()))
                .hasScheduleCount(3).repairScheduleForKeyspace("keyspace1")
                .hasNextActivationDateCloseTo(timeOfFirstSchedule()).andThen()
                .repairScheduleForKeyspace("keyspace2")
                .hasNextActivationDateCloseTo(timeOfFirstSchedule().plusHours(6)).andThen()
                .repairScheduleForKeyspace("keyspace4")
                .hasNextActivationDateCloseTo(timeOfFirstSchedule().plusHours(12));
    }

    private DateTime timeOfFirstSchedule() {
        return DateTime.now().plus(DELAY_BEFORE_SCHEDULE.toMillis());
    }

    private RepairSchedule.Builder aRepairSchedule(Cluster cluster, String keyspace, DateTime creationTime) {
        RepairUnit repairUnit = context.storage.addRepairUnit(aRepair(cluster, keyspace));
        return new RepairSchedule.Builder(repairUnit.getId(), RepairSchedule.State.ACTIVE, 1, DateTime.now(),
                ImmutableList.of(), 10, RepairParallelism.DATACENTER_AWARE, 0.9, creationTime, 0);
    }

    private RepairUnit.Builder aRepair(Cluster cluster, String keyspace) {
        return new RepairUnit.Builder(cluster.getName(), keyspace, Collections.emptySet(), Boolean.FALSE,
                Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), 1);
    }

    private ClusterRepairScheduleAssertion assertThatClusterRepairSchedules(
            Collection<RepairSchedule> repairSchedules) {
        return new ClusterRepairScheduleAssertion(repairSchedules);
    }

    private class ClusterRepairScheduleAssertion {

        private final Collection<RepairSchedule> repairSchedules;

        ClusterRepairScheduleAssertion(Collection<RepairSchedule> repairSchedules) {
            this.repairSchedules = repairSchedules;
        }

        ClusterRepairScheduleAssertion hasScheduleCount(int size) {
            assertThat(size).isEqualTo(size);
            return this;
        }

        ClusterRepairScheduleAssertion.RepairScheduleAssertion repairScheduleForKeyspace(String keyspace) {
            RepairSchedule keyspaceRepairSchedule = repairSchedules.stream()
                    .filter(repairSchedule -> context.storage.getRepairUnit(repairSchedule.getRepairUnitId())
                            .getKeyspaceName().equals(keyspace))
                    .findFirst().orElseThrow(
                            () -> new AssertionError(format("No repair schedule found for keyspace %s", keyspace)));
            return new ClusterRepairScheduleAssertion.RepairScheduleAssertion(keyspaceRepairSchedule);
        }

        private final class RepairScheduleAssertion {

            private final RepairSchedule repairSchedule;

            RepairScheduleAssertion(RepairSchedule repairSchedule) {
                this.repairSchedule = repairSchedule;
            }

            RepairScheduleAssertion hasSameConfigItemsAs(ReaperApplicationConfiguration config) {

                assertThat(repairSchedule.getDaysBetween()).isEqualTo(config.getScheduleDaysBetween());
                assertThat(repairSchedule.getIntensity()).isEqualTo(config.getRepairIntensity());
                assertThat(repairSchedule.getSegmentCount()).isEqualTo(0);
                assertThat(repairSchedule.getSegmentCountPerNode()).isEqualTo(config.getSegmentCountPerNode());
                assertThat(repairSchedule.getRepairParallelism()).isEqualTo(config.getRepairParallelism());
                return this;
            }

            RepairScheduleAssertion hasNextActivationDateCloseTo(DateTime dateTime) {

                assertThat(repairSchedule.getNextActivation().toDate()).isCloseTo(dateTime.toDate(),
                        Duration.ofSeconds(10).toMillis());

                return this;
            }

            RepairScheduleAssertion hasOwner(String owner) {
                assertThat(repairSchedule.getOwner()).isEqualTo(owner);
                return this;
            }

            RepairScheduleAssertion hasCreationTimeCloseTo(DateTime dateTime) {

                assertThat(repairSchedule.getCreationTime().toDate()).isCloseTo(dateTime.toDate(),
                        Duration.ofSeconds(10).toMillis());

                return this;
            }

            RepairScheduleAssertion hasCreationTime(DateTime dateTime) {
                assertThat(repairSchedule.getCreationTime()).isEqualTo(dateTime);
                return this;
            }

            ClusterRepairScheduleAssertion andThen() {
                return ClusterRepairScheduleAssertion.this;
            }
        }
    }
}