/** * 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.server.realtime; import org.apache.helix.AccessOption; import org.apache.helix.BaseDataAccessor; import org.apache.helix.HelixManager; import org.apache.helix.ZNRecord; import org.apache.zookeeper.data.Stat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; // Helix keeps the old controller around for 30s before electing a new one, so we will keep getting // the old controller as leader, and it will keep returning NOT_LEADER. // Singleton class. public class ControllerLeaderLocator { private static ControllerLeaderLocator _instance = null; public static final Logger LOGGER = LoggerFactory.getLogger(ControllerLeaderLocator.class); private final HelixManager _helixManager; private final String _clusterName; // Co-ordinates of the last known controller leader. private volatile String _controllerLeaderHostPort = null; // Should we refresh the controller leader co-ordinates? private volatile boolean _refresh = true; ControllerLeaderLocator(HelixManager helixManager) { _helixManager = helixManager; _clusterName = helixManager.getClusterName(); } /** * To be called once when the server starts * @param helixManager should already be started */ public static void create(HelixManager helixManager) { if (_instance != null) { // We create multiple server instances in the hybrid cluster integration tests, so allow the call to create an // instance even if there is already one. LOGGER.warn("Already created"); return; } _instance = new ControllerLeaderLocator(helixManager); } public static ControllerLeaderLocator getInstance() { if (_instance == null) { throw new RuntimeException("Not yet created"); } return _instance; } /** * Locate the controller leader so that we can send LLC segment completion requests to it. * * @return The host:port string of the current controller leader. */ public synchronized String getControllerLeader() { if (!_refresh) { return _controllerLeaderHostPort; } BaseDataAccessor<ZNRecord> dataAccessor = _helixManager.getHelixDataAccessor().getBaseDataAccessor(); Stat stat = new Stat(); ZNRecord znRecord = dataAccessor.get("/" + _clusterName + "/CONTROLLER/LEADER", stat, AccessOption.THROW_EXCEPTION_IFNOTEXIST); String leader = znRecord.getId(); int index = leader.lastIndexOf('_'); _controllerLeaderHostPort = leader.substring(0, index) + ":" + leader.substring(index + 1); _refresh = false; return _controllerLeaderHostPort; } public synchronized void refreshControllerLeader() { _refresh = true; } }