/** * 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); } } }