/** * 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.controller.helix.core.sharding; import com.linkedin.pinot.common.utils.ZkStarter; import com.linkedin.pinot.controller.helix.ControllerRequestURLBuilder; import com.linkedin.pinot.core.indexsegment.generator.SegmentVersion; import com.linkedin.pinot.integration.tests.UploadRefreshDeleteIntegrationTest; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.io.FileUtils; import org.apache.helix.manager.zk.ZKHelixAdmin; import org.apache.helix.model.IdealState; import org.json.JSONObject; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import static org.testng.Assert.assertEquals; /** * Integration test for the balance num segment assignment strategy */ public class BalanceNumSegmentAssignmentStrategyIntegrationTest extends UploadRefreshDeleteIntegrationTest { private ZKHelixAdmin _helixAdmin; private final String serverTenant = "DefaultTenant_OFFLINE"; private final String hostName = "1.2.3.4"; private final int basePort = 1234; @BeforeClass public void setUp() throws Exception { // Start zk and controller startZk(); startController(); // Start one server and one broker instance startServer(); startBroker(); // Create eight dummy server instances for(int i = 0; i < 8; ++i) { JSONObject serverInstance = new JSONObject(); serverInstance.put("host", hostName); serverInstance.put("port", Integer.toString(basePort + i)); serverInstance.put("tag", serverTenant); serverInstance.put("type", "server"); sendPostRequest(ControllerRequestURLBuilder.baseUrl(CONTROLLER_BASE_API_URL).forInstanceCreate(), serverInstance.toString()); } // Create Helix connection _helixAdmin = new ZKHelixAdmin(ZkStarter.DEFAULT_ZK_STR); // BaseClass @BeforeMethod will setup tablex } @AfterClass public void tearDown() { // Close the Helix connection _helixAdmin.close(); // Stop everything stopServer(); stopBroker(); stopController(); stopZk(); // Delete temporary directory FileUtils.deleteQuietly(_tmpDir); } @Test(dataProvider = "configProvider") public void testSegmentAssignmentStrategy(String tableName, SegmentVersion version) throws Exception { // Upload nine segments for(int i = 0; i < 9; ++i) { generateAndUploadRandomSegment(tableName + "_" + i, 10); } // Count the number of segments per instance Map<String, Integer> segmentsPerInstance = getSegmentsPerInstance(tableName); // Check that each instance has exactly three segments assigned for (String instanceName : segmentsPerInstance.keySet()) { int segmentCountForInstance = segmentsPerInstance.get(instanceName); assertEquals(segmentCountForInstance, 3, "Instance " + instanceName + " did not have the expected number of segments assigned"); } } private Map<String, Integer> getSegmentsPerInstance(String tableName) { Map<String, Integer> segmentsPerInstance = new HashMap<String, Integer>(); IdealState idealState = _helixAdmin.getResourceIdealState(getHelixClusterName(), tableName + "_OFFLINE"); for (String partitionName : idealState.getPartitionSet()) { for (String instanceName : idealState.getInstanceSet(partitionName)) { if (!segmentsPerInstance.containsKey(instanceName)) { segmentsPerInstance.put(instanceName, 1); } else { segmentsPerInstance.put(instanceName, segmentsPerInstance.get(instanceName) + 1); } } } return segmentsPerInstance; } @DataProvider(name = "tableNameProvider") public Object[][] configProvider() { Object[][] configs = { { "disabledInstancesTable", SegmentVersion.v3} }; return configs; } @Test(dataProvider = "tableNameProvider") public void testNoAssignmentToDisabledInstances(String tableName, SegmentVersion version) throws Exception { List<String> instances = _helixAdmin.getInstancesInClusterWithTag(getHelixClusterName(), serverTenant); List<String> disabledInstances = new ArrayList<>(); // disable 6 instances assertEquals(instances.size(), 9); for (int i = 0; i < 6; i++) { _helixAdmin.enableInstance(getHelixClusterName(), instances.get(i), false); } // Thread.sleep(100000); for (int i = 0; i < 6; i++) { generateAndUploadRandomSegment(tableName + "_" + i, 10); } Map<String, Integer> segmentsPerInstance = getSegmentsPerInstance(tableName); // size is 3 since we disabled 6 instances assertEquals(segmentsPerInstance.size(), 3); for (Map.Entry<String, Integer> instanceEntry : segmentsPerInstance.entrySet()) { assertEquals(instanceEntry.getValue().intValue(), 6); } // re-enable instances since these tests are usually "setup once" and run multiple tests type for (int i = 0; i < 6; i++) { _helixAdmin.enableInstance(getHelixClusterName(), instances.get(i), true); } } @Test(enabled = false) public void testRefresh(String tableName, SegmentVersion version) { // Ignore this inherited test } @Test(enabled = false) public void testRetry(String tableName, SegmentVersion version) { // Ignore this inherited test } }