/**
* Copyright 2016 Yahoo Inc.
*
* 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.yahoo.pulsar.broker.loadbalance.impl;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.yahoo.pulsar.broker.PulsarService;
import com.yahoo.pulsar.broker.loadbalance.BrokerFilterBadVersionException;
import org.apache.bookkeeper.mledger.ManagedLedgerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.github.zafarkhaja.semver.Version;
import com.yahoo.pulsar.broker.BrokerData;
import com.yahoo.pulsar.broker.loadbalance.BrokerFilter;
import com.yahoo.pulsar.broker.BundleData;
import com.yahoo.pulsar.broker.ServiceConfiguration;
import com.yahoo.pulsar.broker.loadbalance.LoadData;
public class BrokerVersionFilter implements BrokerFilter {
private static final Logger LOG = LoggerFactory.getLogger(BrokerVersionFilter.class);
/**
* Get the most recent broker version number from the load reports of all the running brokers. The version
* number is from the build artifact in the pom and got added to the package when it was built by Maven
*
* @param brokers
* The brokers to choose the latest version string from.
* @param loadData
* The load data from the leader broker (contains the load reports which in turn contain the version string).
* @return The most recent broker version
* @throws BrokerFilterBadVersionException
* If the most recent version is undefined (e.g., a bad broker version was encountered or a broker
* does not have a version string in its load report.
*/
public Version getLatestVersionNumber(Set<String> brokers, LoadData loadData) throws BrokerFilterBadVersionException {
if ( null == brokers ) {
throw new BrokerFilterBadVersionException("Unable to determine latest version since broker set was null");
}
if ( brokers.size() == 0 ) {
throw new BrokerFilterBadVersionException("Unable to determine latest version since broker set was empty");
}
if ( null == loadData ) {
throw new BrokerFilterBadVersionException("Unable to determine latest version since loadData was null");
}
Version latestVersion = null;
for ( String broker : brokers ) {
BrokerData data = loadData.getBrokerData().get(broker);
if (null == data) {
LOG.warn("No broker data for broker [{}]; disabling PreferLaterVersions feature", broker);
// trigger the ModularLoadManager to reset all the brokers to the original set
throw new BrokerFilterBadVersionException("No broker data for broker \"" + broker + "\"");
}
String brokerVersion = data.getLocalData().getBrokerVersionString();
if (null == brokerVersion || brokerVersion.length() == 0) {
LOG.warn("No version string in load report for broker [{}]; disabling PreferLaterVersions feature", broker);
// trigger the ModularLoadManager to reset all the brokers to the original set
throw new BrokerFilterBadVersionException("No version string in load report for broker \"" + broker + "\"");
}
Version brokerVersionVersion = null;
try {
brokerVersionVersion = Version.valueOf(brokerVersion);
} catch (Exception x) {
LOG.warn("Invalid version string in load report for broker [{}]: [{}]; disabling PreferLaterVersions feature", broker, brokerVersion);
// trigger the ModularLoadManager to reset all the brokers to the original set
throw new BrokerFilterBadVersionException("Invalid version string in load report for broker \"" + broker + "\": \"" + brokerVersion + "\")");
}
if ( null == latestVersion ) {
latestVersion = brokerVersionVersion;
} else if (Version.BUILD_AWARE_ORDER.compare(latestVersion, brokerVersionVersion) < 0) {
latestVersion = brokerVersionVersion;
}
}
if ( null == latestVersion ) {
throw new BrokerFilterBadVersionException("Unable to determine latest broker version");
}
return latestVersion;
}
/**
* From the given set of available broker candidates, filter those using the version numbers.
*
* @param brokers
* The currently available brokers that have not already been filtered.
* @param bundleToAssign
* The data for the bundle to assign.
* @param loadData
* The load data from the leader broker.
* @param conf
* The service configuration.
*/
public void filter(Set<String> brokers, BundleData bundleToAssign, LoadData loadData, ServiceConfiguration conf)
throws BrokerFilterBadVersionException {
if ( !conf.isPreferLaterVersions()) {
return;
}
com.github.zafarkhaja.semver.Version latestVersion = null;
try {
latestVersion = getLatestVersionNumber(brokers, loadData);
LOG.info("Latest broker version found was [{}]", latestVersion);
} catch ( Exception x ) {
LOG.warn("Disabling PreferLaterVersions feature; reason: " + x.getMessage());
throw new BrokerFilterBadVersionException("Cannot determine newest broker version: " + x.getMessage());
}
int numBrokersLatestVersion=0;
int numBrokersOlderVersion=0;
Iterator<String> brokerIterator = brokers.iterator();
while ( brokerIterator.hasNext() ) {
String broker = brokerIterator.next();
BrokerData data = loadData.getBrokerData().get(broker);
String brokerVersion = data.getLocalData().getBrokerVersionString();
com.github.zafarkhaja.semver.Version brokerVersionVersion = Version.valueOf(brokerVersion);
if ( brokerVersionVersion.equals(latestVersion) ) {
LOG.debug("Broker [{}] is running the latest version ([{}])", broker, brokerVersion);
++numBrokersLatestVersion;
} else {
LOG.info("Broker [{}] is running an older version ([{}]); latest version is [{}]", broker, brokerVersion, latestVersion);
++numBrokersOlderVersion;
brokerIterator.remove();
}
}
if ( numBrokersOlderVersion == 0 ) {
LOG.info("All {} brokers are running the latest version [{}]", numBrokersLatestVersion, latestVersion);
}
}
}