Package com.linkedin.databus.bootstrap.producer

Source Code of com.linkedin.databus.bootstrap.producer.DatabusBootstrapProducer$SourceInfo

package com.linkedin.databus.bootstrap.producer;

/*
*
* Copyright 2013 LinkedIn Corp. All rights reserved
*
* 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.
*
*/

import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.log4j.Logger;

import com.linkedin.databus.bootstrap.api.BootstrapProducerStatus;
import com.linkedin.databus.bootstrap.common.BootstrapConn;
import com.linkedin.databus.bootstrap.common.BootstrapDBCleaner;
import com.linkedin.databus.bootstrap.common.BootstrapDBMetaDataDAO;
import com.linkedin.databus.bootstrap.common.BootstrapProducerStatsCollector;
import com.linkedin.databus.client.DatabusHttpClientImpl;
import com.linkedin.databus.client.pub.CheckpointPersistenceProvider;
import com.linkedin.databus.client.pub.DatabusClientException;
import com.linkedin.databus.client.pub.ServerInfo;
import com.linkedin.databus.core.Checkpoint;
import com.linkedin.databus.core.DatabusThreadBase;
import com.linkedin.databus.core.DbusClientMode;
import com.linkedin.databus.core.data_model.DatabusSubscription;
import com.linkedin.databus.core.monitoring.mbean.StatsCollectors;
import com.linkedin.databus.core.util.ConfigLoader;
import com.linkedin.databus.core.util.InvalidConfigException;
import com.linkedin.databus2.core.DatabusException;
import com.linkedin.databus2.core.container.netty.ServerContainer;
import com.linkedin.databus2.core.container.request.BootstrapDBException;
import com.linkedin.databus2.core.container.request.BootstrapDatabaseTooOldException;

public class DatabusBootstrapProducer extends DatabusHttpClientImpl implements
    BootstrapProducerCallback.ErrorCaseHandler
{
  public static final String MODULE = DatabusBootstrapProducer.class.getName();
  public static final Logger LOG = Logger.getLogger(MODULE);

  private final HashSet<SourceInfo> _registeredPhysicalSources;
  private final List<String> _registeredSources;

  private final Map<String, DatabusThreadBase> _applierThreads;
  private final BootstrapDBPeriodicTriggerThread _dbPeriodicTriggerThread;
  private final BootstrapDBDiskSpaceTriggerThread _dbDiskSpaceTriggerThread;
  private final BootstrapDBCleaner _dbCleaner;

  private final BootstrapDBMetaDataDAO _dbDao;
  private final Map<String, Integer> _srcNameIdMap;

  protected final StatsCollectors<BootstrapProducerStatsCollector> _bootstrapProducerStatsCollectors;
  private final BootstrapProducerStatsCollector _applierStatsCollector;
  private final BootstrapProducerStaticConfig _bootstrapProducerStaticConfig;

  /**
   * @param config
   * @throws SQLException
   * @throws ClassNotFoundException
   * @throws IllegalAccessException
   * @throws InstantiationException
   * @throws DatabusClientException
   * @throws DatabusException
   * @throws BootstrapDatabaseTooOldException
   * @throws Exception
   */

  public DatabusBootstrapProducer(BootstrapProducerConfig config)
      throws IOException, InvalidConfigException, InstantiationException,
      IllegalAccessException, ClassNotFoundException, SQLException,
      DatabusClientException, DatabusException, BootstrapDBException
  {
    this(config.build());
  }

  public DatabusBootstrapProducer(
      BootstrapProducerStaticConfig bootstrapProducerStaticConfig)
      throws IOException, InvalidConfigException, InstantiationException,
      IllegalAccessException, ClassNotFoundException, SQLException,
      DatabusClientException, DatabusException, BootstrapDBException
  {
    super(bootstrapProducerStaticConfig.getClient());
    _registeredPhysicalSources = new HashSet<SourceInfo>();
    decouplePhysicalSources();
    _bootstrapProducerStaticConfig = bootstrapProducerStaticConfig;
    _registeredSources = new ArrayList<String>();
    _applierStatsCollector = new BootstrapProducerStatsCollector(
        getContainerStaticConfig().getId(), "bootstrapApplier", true, true,
        getMbeanServer(), null);
    _bootstrapProducerStatsCollectors = new StatsCollectors<BootstrapProducerStatsCollector>();

    _applierThreads = new HashMap<String, DatabusThreadBase>();

    BootstrapConn conn = new BootstrapConn();
    final boolean autoCommit = false;
    _dbDao = new BootstrapDBMetaDataDAO(conn,
        bootstrapProducerStaticConfig.getBootstrapDBHostname(),
        bootstrapProducerStaticConfig.getBootstrapDBUsername(),
        bootstrapProducerStaticConfig.getBootstrapDBPassword(),
        bootstrapProducerStaticConfig.getBootstrapDBName(), autoCommit);
    _srcNameIdMap = new HashMap<String, Integer>();
    conn.initBootstrapConn(autoCommit,
        bootstrapProducerStaticConfig.getBootstrapDBUsername(),
        bootstrapProducerStaticConfig.getBootstrapDBPassword(),
        bootstrapProducerStaticConfig.getBootstrapDBHostname(),
        bootstrapProducerStaticConfig.getBootstrapDBName());
    if (!_dbDao.doesMinScnTableExist())
    {
      LOG.error("Bootstrap table not found! Please create table: "
          + BootstrapDBMetaDataDAO.CREATE_MINSCN_TABLE);
      throw new BootstrapDBException(
          "Bootstrap DB does not have necessary meta data table! "
              + BootstrapDBMetaDataDAO.MIN_SCN_TABLE_NAME);
    }
    initBootstrapDBMetadata();

    // callback should only be registered after DBMetadata is initialized.
    LOG.info("The Bootstrap Producer is configured for "
        + _registeredPhysicalSources.size() + " sources");
    for (SourceInfo sourceInfo : _registeredPhysicalSources)
    {
      LOG.info("Creating BootstrapProducer callback for PhysicalSource: "
          + sourceInfo.getPhysicalSourceName());
      BootstrapProducerStatsCollector producerStatsCollector = new BootstrapProducerStatsCollector(
          getContainerStaticConfig().getId(),
          sourceInfo.getPhysicalSourceName(), true, true, getMbeanServer(),
          sourceInfo.getLogicalSources());
      _bootstrapProducerStatsCollectors.addStatsCollector(
          sourceInfo.getPhysicalSourceName(), producerStatsCollector);
      registerProducerCallback(sourceInfo.getLogicalSources(),
          producerStatsCollector);
    }
    validateAndRepairBootstrapDBCheckpoint();

    for (String source: _registeredSources)
    {
      LOG.info("Creating ApplierThread for source = " + source);
      final String name = source + "BootstrapApplier";
      BootstrapApplierThread applierThread = new BootstrapApplierThread(name, source,
          _bootstrapProducerStaticConfig, _applierStatsCollector);
      _applierThreads.put(source, applierThread);
    }

    // Create BootstrapDBCleaner
    final String dbCleanerName = "DBCleaner";
    _dbCleaner = new BootstrapDBCleaner(dbCleanerName,
        _bootstrapProducerStaticConfig.getCleaner(),
        _bootstrapProducerStaticConfig, _applierThreads, _registeredSources);

    // Create periodic trigger thread
    _dbPeriodicTriggerThread = new BootstrapDBPeriodicTriggerThread(_dbCleaner,
        _bootstrapProducerStaticConfig.getCleaner().getPeriodSpaceTrigger());

    // Create disk space cleaner thread
    _dbDiskSpaceTriggerThread = new BootstrapDBDiskSpaceTriggerThread(_dbCleaner,
        _bootstrapProducerStaticConfig.getCleaner().getDiskSpaceTrigger());

  }

  /**
   * The method helps identify the Physical sources that are listed in the
   * bootstrap producer config (Which is really the client config) The Physical
   * sources are identified by comparing the list of subscriptions/logical
   * sources. There are two acceptable cases: 1. Two relays provide different
   * logical sources -> Two different physical sources 2. Two relays provide the
   * same logical sources (for load balancing/fault tolerance) -> Only one
   * physical source (The multitenant-client library underneath is aware that
   * two relays provide the same logical sources)
   *
   * @return A set of Physical sources which each contains a list of logical
   *         sources.
   *
   */
  private void decouplePhysicalSources()
  {
    DatabusHttpClientImpl.RuntimeConfig clientRtConfig = getClientConfigManager()
        .getReadOnlyConfig();

    for (ServerInfo relayInfo : clientRtConfig.getRelays())
    {

      if (relayInfo == null || relayInfo.getSources() == null)
        LOG.error("No sources specified in the client config for the bootstrap producer");

      if (relayInfo.getPhysicalSourceName() == null)
      {
        LOG.error("PhysicalSource name not specified");
      }

      SourceInfo sourceInfo = new SourceInfo(relayInfo.getPhysicalSourceName(),
          relayInfo.getSources());
      _registeredPhysicalSources.add(sourceInfo);
    }
  }

  /**
   *
   *
   * Compares the Checkpoint and bootstrap_producer_state's SCN to check against
   * the possibility of gap in event consumption!! If gap is found, makes
   * best-effort to repair it.
   *
   * Three Scenario that should be allowed
   *
   * 1. Bootstrap Producer started after seeding In this case checkpoint SCN
   * should not be greater than producer SCN. 2. Bootstrap Producer started
   * after adding sources without seeding In this case the producer SCN is "-1".
   * The checkpoint "could" be empty 3. Bootstrap Producer bounced In this case
   * checkpoint SCN should not be greater than producer SCN.
   *
   * @throws SQLException
   * @throws BootstrapDBException
   *           if there is a gap and cannot be repaired
   * @throws IOException
   *           `
   */
  private void validateAndRepairBootstrapDBCheckpoint() throws SQLException,
      BootstrapDBException, IOException
  {
    LOG.info("Validating bootstrap DB checkpoints !!");

    for (List<DatabusSubscription> subsList : _relayGroups.keySet())
    {
      List<String> sourceNames = DatabusSubscription.getStrList(subsList);
      long scn = -1;
      try
      {
        scn = _dbDao.getMinWindowSCNFromStateTable(sourceNames,
            "bootstrap_producer_state");
      } catch (BootstrapDBException ex)
      {
        LOG.error(
            "Got exception while trying to fetch SCN from bootstrap_producer_state for sources :"
                + sourceNames, ex);
        throw ex;
      }

      CheckpointPersistenceProvider provider = getCheckpointPersistenceProvider();
      Checkpoint cp = provider.loadCheckpoint(sourceNames);
      LOG.info("Bootstrap Producer SCN :" + scn + ", Checkpoint :" + cp);

      if (null != cp)
      {
        if (cp.getConsumptionMode() != DbusClientMode.ONLINE_CONSUMPTION)
        {
          // Bootstrapping bootstrap Producer not yet supported !!
          String msg = "Bootstrap Producer starting from non-online consumption mode for sources :"
              + sourceNames + ", Ckpt :" + cp;
          LOG.error(msg);
          throw new BootstrapDBException(msg);
        }
        else
        {
          String msg = null;
          if (((cp.getWindowScn() > scn) && (scn > -1))
              || ((cp.getWindowScn() < scn) && (scn > -1)))
          {

            if (((cp.getWindowScn() > scn) && (scn > -1)))
              LOG.warn("Non-Empty checkpint. Bootstrap Producer is at SCN:"
                  + scn
                  + ", while checkpoint is :"
                  + cp
                  + ", Could result in gap in event consumption. Repairing ckpt !!");
            else
              LOG.info("Non-Empty checkpoint. Bootstrap Producer is at SCN:"
                  + scn + ", while checkpoint is :" + cp
                  + ", Copying producer Scn to checkpoint !!");

            cp.setWindowScn(scn);
            cp.setWindowOffset(-1);
            try
            {
              provider.removeCheckpoint(sourceNames);
              provider.storeCheckpoint(sourceNames, cp);

              // Check if persisted properly
              cp = provider.loadCheckpoint(sourceNames);
              if ((null == cp)
                  || (cp.getWindowScn() != scn)
                  || (cp.getWindowOffset() != -1)
                  || (cp.getConsumptionMode() != DbusClientMode.ONLINE_CONSUMPTION))
              {
                msg = "Unable to repair and store the new checkpoint (" + cp
                    + ") to make it same as producer SCN (" + scn + ") !!";
                LOG.fatal(msg);
                throw new BootstrapDBException(msg);
              }
            } catch (IOException ex)
            {
              msg = "Unable to repair and store the new checkpoint (" + cp
                  + ") to make it same as producer SCN (" + scn + ") !!";
              LOG.fatal(msg, ex);
              throw new BootstrapDBException(msg);
            }
          }
        }
      }
      else
      {
        /**
         * Currently since bootstrapping is not available, a null ckpt would
         * result in flexible checkpoint and could result in gap !!
         */
        if (scn > -1)
        {
          String msg = "Empty checkpoint. Bootstrap Producer SCN is at SCN:"
              + scn
              + ", while checkpoint is null !! Could result in gap in event consumption. Repairing ckpt !!";
          LOG.warn(msg);
          cp = new Checkpoint();
          cp.setWindowScn(scn);
          cp.setWindowOffset(-1);
          cp.setConsumptionMode(DbusClientMode.ONLINE_CONSUMPTION);
          try
          {
            provider.removeCheckpoint(sourceNames);
            provider.storeCheckpoint(sourceNames, cp);

            // Check if persisted properly
            cp = provider.loadCheckpoint(sourceNames);
            if ((null == cp)
                || (cp.getWindowScn() != scn)
                || (cp.getWindowOffset() != -1)
                || (cp.getConsumptionMode() != DbusClientMode.ONLINE_CONSUMPTION))
            {
              LOG.fatal("Unable to repair and store the checkpoint (" + cp
                  + ") to make it same as producer SCN (" + scn + ") !!");
              throw new BootstrapDBException(msg);
            }
          } catch (IOException ex)
          {
            msg = "Unable to repair and store the checkpoint (" + cp
                + ") to make it same as producer SCN (" + scn + ") !!";
            LOG.fatal(msg, ex);
            throw new BootstrapDBException(msg);
          }
        }
      }
    }
    LOG.info("Validating bootstrap DB checkpoints done successfully!!");
  }

  private void registerProducerCallback(List<String> logicalSourceList,
      BootstrapProducerStatsCollector statsCollector)
      throws SQLException, DatabusClientException, DatabusException
  {

    // create callback for producer to populate data into log_* tables
    BootstrapProducerCallback bootstrapCallback = new BootstrapProducerCallback(
        _bootstrapProducerStaticConfig, statsCollector, this, logicalSourceList);
    registerDatabusStreamListener(bootstrapCallback, logicalSourceList, null);
  }

  private void initBootstrapDBMetadata() throws SQLException,
      BootstrapDatabaseTooOldException
  {
    DatabusHttpClientImpl.RuntimeConfig clientRtConfig = getClientConfigManager()
        .getReadOnlyConfig();

    // create source list
    for (ServerInfo relayInfo : clientRtConfig.getRelays())
    {
      _registeredSources.addAll(relayInfo.getSources());
      for (String source : _registeredSources)
      {
        BootstrapDBMetaDataDAO.SourceStatusInfo srcIdStatus = _dbDao
            .getSrcIdStatusFromDB(source, false);

        if (0 > srcIdStatus.getSrcId())
        {
          int newState = BootstrapProducerStatus.NEW;

          if (!_bootstrapProducerStaticConfig.isBootstrapDBStateCheck())
          {
            // TO allow test framework to listen to relay directly,DBStateCheck
            // flag is used
            newState = BootstrapProducerStatus.ACTIVE;
          }
          _dbDao.addNewSourceInDB(source, newState);
        }

        srcIdStatus = _dbDao.getSrcIdStatusFromDB(source, false);
        _srcNameIdMap.put(source, srcIdStatus.getSrcId());

        if (_bootstrapProducerStaticConfig.isBootstrapDBStateCheck())
        {
          if (!BootstrapProducerStatus.isReadyForConsumption(srcIdStatus
              .getStatus()))
            throw new BootstrapDatabaseTooOldException(
                "Bootstrap DB is not ready to read from relay !! Status :"
                    + srcIdStatus);
        }
      }
    }
  }

  public List<String> getRegisteredSources()
  {
    return _registeredSources;
  }

  @Override
  public void doStart()
  {
    super.doStart();

    if (_bootstrapProducerStaticConfig.getRunApplierThreadOnStart())
    {
      for (Map.Entry<String, DatabusThreadBase> applierThreadEntry: _applierThreads.entrySet())
      {
        LOG.info("Starting applier thread for source = " + applierThreadEntry.getValue());
        applierThreadEntry.getValue().start();
      }
    }
    else
    {
      LOG.info("Not starting any applier threads because the config getRunApplierThreadOnStart is false");
    }

    if (_bootstrapProducerStaticConfig.getCleaner().getDiskSpaceTrigger().isEnable())
    {
      LOG.info("Starting disk space trigger thread");
      _dbDiskSpaceTriggerThread.start();
    }

    if (_bootstrapProducerStaticConfig.getCleaner().getPeriodSpaceTrigger().isEnable())
    {
      LOG.info("Starting periodic trigger thread");
      _dbPeriodicTriggerThread.start();
    }

    LOG.info(DatabusBootstrapProducer.class.getName() + " is running ...");
  }

  public static void main(String[] args) throws Exception
  {
    // Properties startupProps = BootstrapConfig.loadConfigProperties(args);
    Properties startupProps = ServerContainer.processCommandLineArgs(args);

    BootstrapProducerConfig producerConfig = new BootstrapProducerConfig();
    ConfigLoader<BootstrapProducerStaticConfig> staticProducerConfigLoader = new ConfigLoader<BootstrapProducerStaticConfig>(
        "databus.bootstrap.", producerConfig);
    BootstrapProducerStaticConfig staticProducerConfig = staticProducerConfigLoader
        .loadConfig(startupProps);

    DatabusBootstrapProducer bootstrapProducer = new DatabusBootstrapProducer(
        staticProducerConfig);
    bootstrapProducer.registerShutdownHook();
    bootstrapProducer.startAndBlock();
  }

  @Override
  protected void doShutdown()
  {
    super.doShutdown();
    for (Map.Entry<String, DatabusThreadBase> applierThreadEntry: _applierThreads.entrySet())
    {
      DatabusThreadBase applierThread = applierThreadEntry.getValue();
      if (applierThread.isAlive())
      {
        applierThread.shutdownAsynchronously();
        applierThread.interrupt();
        applierThread.awaitShutdownUniteruptibly();
      }
    }

    if (_dbDiskSpaceTriggerThread.isAlive())
    {
      _dbDiskSpaceTriggerThread.shutdownAsynchronously();
      _dbDiskSpaceTriggerThread.interrupt();
      _dbDiskSpaceTriggerThread.awaitShutdownUniteruptibly();
    }

    if (_dbPeriodicTriggerThread.isAlive())
    {
      _dbPeriodicTriggerThread.shutdownAsynchronously();
      _dbPeriodicTriggerThread.interrupt();
      _dbPeriodicTriggerThread.awaitShutdownUniteruptibly();
    }


  }

  public StatsCollectors<BootstrapProducerStatsCollector> getProducerStatsCollectors()
  {
    return _bootstrapProducerStatsCollectors;
  }

  public BootstrapProducerStatsCollector getApplierStatsCollector()
  {
    return _applierStatsCollector;
  }

  @Override
  public void onErrorRetryLimitExceeded(String message, Throwable exception)
  {
    LOG.fatal("Error Retry Limit reached. Message :(" + message
        + "). Stopping Bootstrap Producer Service. Exception Received :",
        exception);
    doShutdown();
  }

  /*
   * This class holds information about the physical source and the list of
   * logical sources that belong to them
   */
  static private class SourceInfo
  {
    private String PhysicalSourceName;
    private List<String> logicalSources;

    public List<String> getLogicalSources()
    {
      return logicalSources;
    }

    public void setLogicalSources(List<String> logicalSources)
    {
      this.logicalSources = logicalSources;
    }

    public String getPhysicalSourceName()
    {
      return PhysicalSourceName;
    }

    public void setPhysicalSourceName(String physicalSourceName)
    {
      PhysicalSourceName = physicalSourceName;
    }

    private SourceInfo(String physicalSourceName, List<String> logicalSources)
    {
      PhysicalSourceName = physicalSourceName;
      this.logicalSources = logicalSources;
    }

    @Override
    public boolean equals(Object obj)
    {

      if (null == obj)
        return false;
      if (!(obj instanceof SourceInfo))
        return false;
      SourceInfo other = (SourceInfo) obj;
      return this.PhysicalSourceName.equals(other.getPhysicalSourceName());
    }

    @Override
    public int hashCode()
    {
      return PhysicalSourceName != null ? PhysicalSourceName.hashCode() : 0;
    }
  }
}
TOP

Related Classes of com.linkedin.databus.bootstrap.producer.DatabusBootstrapProducer$SourceInfo

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.