/** * Copyright (C) 2014-2016 LinkedIn Corp. (pinot-core@linkedin.com) * * 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 com.linkedin.pinot.common.utils; import com.google.common.collect.ImmutableList; import java.util.Collections; import java.util.List; import org.apache.helix.HelixManager; import org.apache.helix.model.ExternalView; import org.apache.helix.model.IdealState; import org.testng.annotations.Test; import static org.testng.Assert.*; /** * Test for the service status. */ public class ServiceStatusTest { private static final ServiceStatus.ServiceStatusCallback ALWAYS_GOOD = new ServiceStatus.ServiceStatusCallback() { @Override public ServiceStatus.Status getServiceStatus() { return ServiceStatus.Status.GOOD; } }; private static final ServiceStatus.ServiceStatusCallback ALWAYS_STARTING = new ServiceStatus.ServiceStatusCallback() { @Override public ServiceStatus.Status getServiceStatus() { return ServiceStatus.Status.STARTING; } }; private static final ServiceStatus.ServiceStatusCallback ALWAYS_BAD = new ServiceStatus.ServiceStatusCallback() { @Override public ServiceStatus.Status getServiceStatus() { return ServiceStatus.Status.BAD; } }; public static final String TABLE_NAME = "myTable_OFFLINE"; public static final String INSTANCE_NAME = "Server_1.2.3.4_1234"; @Test public void testMultipleServiceStatusCallback() { // Only good should return good ServiceStatus.MultipleCallbackServiceStatusCallback onlyGood = new ServiceStatus.MultipleCallbackServiceStatusCallback( ImmutableList.of(ALWAYS_GOOD) ); assertEquals(onlyGood.getServiceStatus(), ServiceStatus.Status.GOOD); // Only bad should return bad ServiceStatus.MultipleCallbackServiceStatusCallback onlyBad = new ServiceStatus.MultipleCallbackServiceStatusCallback( ImmutableList.of(ALWAYS_BAD) ); assertEquals(onlyBad.getServiceStatus(), ServiceStatus.Status.BAD); // Only starting should return starting ServiceStatus.MultipleCallbackServiceStatusCallback onlyStarting = new ServiceStatus.MultipleCallbackServiceStatusCallback( ImmutableList.of(ALWAYS_STARTING) ); assertEquals(onlyStarting.getServiceStatus(), ServiceStatus.Status.STARTING); // Good + starting = starting ServiceStatus.MultipleCallbackServiceStatusCallback goodAndStarting = new ServiceStatus.MultipleCallbackServiceStatusCallback( ImmutableList.of(ALWAYS_GOOD, ALWAYS_STARTING) ); assertEquals(goodAndStarting.getServiceStatus(), ServiceStatus.Status.STARTING); // Good + starting + bad = starting (check for left-to-right evaluation) ServiceStatus.MultipleCallbackServiceStatusCallback goodStartingAndBad = new ServiceStatus.MultipleCallbackServiceStatusCallback( ImmutableList.of(ALWAYS_GOOD, ALWAYS_STARTING, ALWAYS_BAD) ); assertEquals(goodStartingAndBad.getServiceStatus(), ServiceStatus.Status.STARTING); } @Test public void testIdealStateMatch() { TestIdealStateAndExternalViewMatchServiceStatusCallback callback; // No ideal state = STARTING callback = buildTestISEVCallback(); callback.setExternalView(new ExternalView(TABLE_NAME)); assertEquals(callback.getServiceStatus(), ServiceStatus.Status.STARTING); // No external view = STARTING callback = buildTestISEVCallback(); callback.setIdealState(new IdealState(TABLE_NAME)); assertEquals(callback.getServiceStatus(), ServiceStatus.Status.STARTING); // Empty ideal state + empty external view = GOOD callback = buildTestISEVCallback(); callback.setIdealState(new IdealState(TABLE_NAME)); callback.setExternalView(new ExternalView(TABLE_NAME)); assertEquals(callback.getServiceStatus(), ServiceStatus.Status.GOOD); // Once the status is GOOD, it should keep on reporting GOOD no matter what callback.setIdealState(null); callback.setExternalView(null); assertEquals(callback.getServiceStatus(), ServiceStatus.Status.GOOD); // Non empty ideal state + empty external view = STARTING callback = buildTestISEVCallback(); IdealState idealState = new IdealState(TABLE_NAME); idealState.setRebalanceMode(IdealState.RebalanceMode.CUSTOMIZED); idealState.setPartitionState("mySegment", INSTANCE_NAME, "ONLINE"); callback.setIdealState(idealState); callback.setExternalView(new ExternalView(TABLE_NAME)); assertEquals(callback.getServiceStatus(), ServiceStatus.Status.STARTING); // Should be good if the only ideal state is disabled callback.getResourceIdealState(TABLE_NAME).enable(false); assertEquals(callback.getServiceStatus(), ServiceStatus.Status.GOOD); // Should ignore offline segments in ideal state callback = buildTestISEVCallback(); idealState = new IdealState(TABLE_NAME); idealState.setRebalanceMode(IdealState.RebalanceMode.CUSTOMIZED); idealState.setPartitionState("mySegment_1", INSTANCE_NAME, "ONLINE"); idealState.setPartitionState("mySegment_2", INSTANCE_NAME, "OFFLINE"); callback.setIdealState(idealState); ExternalView externalView = new ExternalView(TABLE_NAME); externalView.setState("mySegment_1", INSTANCE_NAME, "ONLINE"); callback.setExternalView(externalView); assertEquals(callback.getServiceStatus(), ServiceStatus.Status.GOOD); // Should ignore segments in error state in external view callback = buildTestISEVCallback(); idealState = new IdealState(TABLE_NAME); idealState.setRebalanceMode(IdealState.RebalanceMode.CUSTOMIZED); idealState.setPartitionState("mySegment_1", INSTANCE_NAME, "ONLINE"); idealState.setPartitionState("mySegment_2", INSTANCE_NAME, "OFFLINE"); callback.setIdealState(idealState); externalView = new ExternalView(TABLE_NAME); externalView.setState("mySegment_1", INSTANCE_NAME, "ERROR"); callback.setExternalView(externalView); assertEquals(callback.getServiceStatus(), ServiceStatus.Status.GOOD); // Should ignore other instances callback = buildTestISEVCallback(); idealState = new IdealState(TABLE_NAME); idealState.setRebalanceMode(IdealState.RebalanceMode.CUSTOMIZED); idealState.setPartitionState("mySegment_1", INSTANCE_NAME, "ONLINE"); idealState.setPartitionState("mySegment_2", INSTANCE_NAME + "2", "ONLINE"); callback.setIdealState(idealState); externalView = new ExternalView(TABLE_NAME); externalView.setState("mySegment_1", INSTANCE_NAME, "ONLINE"); externalView.setState("mySegment_2", INSTANCE_NAME + "2", "OFFLINE"); callback.setExternalView(externalView); assertEquals(callback.getServiceStatus(), ServiceStatus.Status.GOOD); } private TestIdealStateAndExternalViewMatchServiceStatusCallback buildTestISEVCallback() { return new TestIdealStateAndExternalViewMatchServiceStatusCallback(null, "potato", INSTANCE_NAME, Collections.singletonList(TABLE_NAME)); } private static class TestIdealStateAndExternalViewMatchServiceStatusCallback extends ServiceStatus.IdealStateAndExternalViewMatchServiceStatusCallback { private IdealState _idealState; private ExternalView _externalView; public TestIdealStateAndExternalViewMatchServiceStatusCallback(HelixManager helixManager, String clusterName, String instanceName) { super(helixManager, clusterName, instanceName); } public TestIdealStateAndExternalViewMatchServiceStatusCallback(HelixManager helixManager, String clusterName, String instanceName, List<String> resourcesToMonitor) { super(helixManager, clusterName, instanceName, resourcesToMonitor); } @Override public IdealState getResourceIdealState(String resourceName) { return _idealState; } @Override public ExternalView getState(String resourceName) { return _externalView; } public void setIdealState(IdealState idealState) { _idealState = idealState; } public void setExternalView(ExternalView externalView) { _externalView = externalView; } } }