/**
* 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.client;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import javax.annotation.Nullable;
import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.serialize.BytesPushThroughSerializer;
import static com.linkedin.pinot.client.ExternalViewReader.OFFLINE_SUFFIX;
import static com.linkedin.pinot.client.ExternalViewReader.REALTIME_SUFFIX;
/**
* Maintains a mapping between table name and list of brokers
*/
public class DynamicBrokerSelector implements BrokerSelector, IZkDataListener {
AtomicReference<Map<String, List<String>>> tableToBrokerListMapRef =
new AtomicReference<Map<String, List<String>>>();
AtomicReference<List<String>> allBrokerListRef = new AtomicReference<List<String>>();
private final Random _random = new Random();
private ExternalViewReader evReader;
public DynamicBrokerSelector(String zkServers) {
ZkClient zkClient = new ZkClient(zkServers);
zkClient.setZkSerializer(new BytesPushThroughSerializer());
zkClient.waitUntilConnected(60, TimeUnit.SECONDS);
zkClient.subscribeDataChanges(ExternalViewReader.BROKER_EXTERNAL_VIEW_PATH, this);
evReader = new ExternalViewReader(zkClient);
refresh();
}
private void refresh() {
Map<String, List<String>> tableToBrokerListMap = evReader.getTableToBrokersMap();
tableToBrokerListMapRef.set(tableToBrokerListMap);
Set<String> brokerSet = new HashSet<>();
for (List<String> brokerList : tableToBrokerListMap.values()) {
brokerSet.addAll(brokerList);
}
allBrokerListRef.set(new ArrayList<>(brokerSet));
}
@Override
public @Nullable String selectBroker(String table) {
if (table == null) {
List<String> list = allBrokerListRef.get();
if (list != null && !list.isEmpty()) {
return list.get(_random.nextInt(list.size()));
} else {
return null;
}
}
String tableName = table.replace(OFFLINE_SUFFIX, "").replace(REALTIME_SUFFIX, "");
List<String> list = tableToBrokerListMapRef.get().get(tableName);
if (list != null && !list.isEmpty()) {
return list.get(_random.nextInt(list.size()));
}
return null;
}
@Override
public void handleDataChange(String dataPath, Object data) throws Exception {
refresh();
}
@Override
public void handleDataDeleted(String dataPath) throws Exception {
refresh();
}
}