Package com.linkedin.databus.bootstrap.utils

Source Code of com.linkedin.databus.bootstrap.utils.BootstrapDBSeeder

package com.linkedin.databus.bootstrap.utils;
/*
*
* 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.ByteArrayInputStream;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

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.BootstrapDBMetaDataDAO;
import com.linkedin.databus.bootstrap.common.BootstrapReadOnlyConfig;
import com.linkedin.databus.core.DbusEventBufferAppendable;
import com.linkedin.databus.core.DbusEventBufferStreamAppendable;
import com.linkedin.databus.core.DbusEventFactory;
import com.linkedin.databus.core.DbusEventInfo;
import com.linkedin.databus.core.DbusEventKey;
import com.linkedin.databus.core.DbusEventKey.KeyType;
import com.linkedin.databus.core.DbusOpcode;
import com.linkedin.databus.core.InternalDatabusEventsListener;
import com.linkedin.databus.core.InvalidEventException;
import com.linkedin.databus.core.KeyTypeNotImplementedException;
import com.linkedin.databus.core.monitoring.mbean.DbusEventsStatisticsCollector;
import com.linkedin.databus2.producers.db.OracleTriggerMonitoredSourceInfo;
import com.linkedin.databus2.util.DBHelper;

public class BootstrapDBSeeder
implements DbusEventBufferAppendable, DbusEventBufferStreamAppendable
{
  private static final int FIFTY_MB_IN_BYTES =     50000000;
  public static final String      MODULE               = BootstrapDBSeeder.class.getName();
  public static final Logger      LOG                  = Logger.getLogger(MODULE);

  private BootstrapDBMetaDataDAO           _bootstrapDao       = null;
  private BootstrapReadOnlyConfig _config              = null;
  private ByteBuffer              _buf                  = null;
  private ByteArrayInputStream    _bufStream           = null;
  private ByteArrayInputStream    _bufStream2           = null;
  private final Map<Short, PreparedStatement> _statementMap;
  private short                   _currSrcId         = -1;
  private long                    _totLatency          = 0;
  private long                    _scn                 = 0;
  private List<OracleTriggerMonitoredSourceInfo> _sources           = null;
  private Map<String, Long>         _lastRows          = null;
  private Map<String, String>       _lastKeys          = null;
  private String                    _lastSeenKey       = null;
  private long                      _startSCN          = -1;


  public BootstrapDBSeeder(BootstrapReadOnlyConfig config,
                           List<OracleTriggerMonitoredSourceInfo> sources)
  throws Exception
  {
    _config = config;
    _statementMap = new HashMap<Short, PreparedStatement>();
    byte[] b = new byte[FIFTY_MB_IN_BYTES];
    _buf = ByteBuffer.wrap(b);
    _bufStream = new ByteArrayInputStream(b);
    _bufStream2 = new ByteArrayInputStream(b);
    getConnection();
    _sources = sources;
    initSources();
  }


  public void initSources()
  throws SQLException
  {
    LOG.info("MySQL JDBC Version :" + getConnection().getMetaData().getDriverVersion());
    _lastRows = new HashMap<String, Long>();
    _lastKeys = new HashMap<String, String>();
    for ( OracleTriggerMonitoredSourceInfo sourceInfo : _sources)
    {
      createBootStrapSourceDB(sourceInfo);
      _lastRows.put(sourceInfo.getEventView(), getRowIdFromCheckpoint(sourceInfo));
      String k = geSrcKeyFromCheckpoint(sourceInfo);
      _lastKeys.put(sourceInfo.getEventView(), k);
      if ( -1 == _currSrcId)
        _currSrcId = sourceInfo.getSourceId();

      if ( null == _lastSeenKey)
        _lastSeenKey = k;
    }
  }

  public void startSeeding()
  throws SQLException
  {
    setBootstrapSourceStatus(BootstrapProducerStatus.SEEDING);
  }

  public Map<String, Long> getLastRows()
  {
    return _lastRows;
  }

  public Map<String, String> getLastKeys()
  {
    return _lastKeys;
  }

  private PreparedStatement prepareInsertStatement(short srcId) throws SQLException
  {
    Connection conn = null;
    PreparedStatement stmt = null;

    StringBuilder sql = new StringBuilder();

    try
    {
      conn = getConnection();
      sql.append("insert into ");
      sql.append(getTableName(srcId));
      sql.append("(scn, srckey, val) ");
      sql.append(" values(?,?,?) ");
      sql.append("on duplicate key update scn = ?, ");
      sql.append("val = ?");
      // sql.append("(scn, windowscn, srckey) ");
      // sql.append(" values(?, ?,?)");

      stmt = conn.prepareStatement(sql.toString());
    } catch (SQLException e) {
      LOG.fatal("Unable to create insert statement for Statement : (" + sql + ")", e);
      throw e;
    }

    return stmt;
  }


  /*
   * Returns a connection object.
   * Note: The connection object is still owned by BootstrapConn. SO dont close it
   */
  public Connection getConnection()
  {
    Connection conn = null;

    if (_bootstrapDao == null)
    {
      LOG.info("<<<< Creating Bootstrap Connection!! >>>>");
      BootstrapConn dbConn = new BootstrapConn();
      try
      {
        dbConn.initBootstrapConn(false,
                                 java.sql.Connection.TRANSACTION_READ_UNCOMMITTED,
                                 _config.getBootstrapDBUsername(),
                                 _config.getBootstrapDBPassword(),
                                 _config.getBootstrapDBHostname(),
                                 _config.getBootstrapDBName());
        _bootstrapDao = new BootstrapDBMetaDataDAO(dbConn,
                                                   _config.getBootstrapDBHostname(),
                                                   _config.getBootstrapDBUsername(),
                                                   _config.getBootstrapDBPassword(),
                                                   _config.getBootstrapDBName(),
                                                   false);
      } catch (Exception e) {
        LOG.fatal("Unable to open BootstrapDB Connection !!", e);
        throw new RuntimeException("Got exception when getting bootstrap DB Connection.",e);
      }
    }

    try
    {
      conn = _bootstrapDao.getBootstrapConn().getDBConn();
    } catch ( SQLException e) {
      LOG.fatal("Not able to open BootstrapDB Connection !!", e);
      throw new RuntimeException("Got exception when getting bootstrap DB Connection.",e);
    }
    return conn;
  }

  /*
   * Initialize Bootstrap Sources entry
   */
  public void setBootstrapSourceStatus(int status)
      throws SQLException
      {
    Connection conn = null;
    PreparedStatement stmt = null;
    try
    {
      String sql = "insert into bootstrap_sources (id, src, status) values (?, ?, ?) on duplicate key update id = ?, src= ?, status=?";
      conn = getConnection();
      stmt = conn.prepareStatement(sql);

      for ( OracleTriggerMonitoredSourceInfo sourceInfo : _sources)
      {
        stmt.setInt(1, sourceInfo.getSourceId());
        stmt.setString(2, sourceInfo.getSourceName());
        stmt.setInt(3,status);
        stmt.setInt(4, sourceInfo.getSourceId());
        stmt.setString(5, sourceInfo.getSourceName());
        stmt.setInt(6,status );
        stmt.executeUpdate();
      }
      conn.commit();
    } catch (SQLException sqlEx) {
      LOG.error("Got Exception while initializing Bootstrap Sources !!", sqlEx);
      if ( conn != null)
        conn.rollback();
      throw sqlEx;
    } finally {
      DBHelper.close(stmt);
    }

      }

  /*
   * Creates Bootstrap source DB only if it is not already available
   */
  public void createBootStrapSourceDB(OracleTriggerMonitoredSourceInfo source ) throws SQLException
  {
    _bootstrapDao.createNewSrcTable(source.getSourceId());
  }


  public String getTableName(int srcId) throws SQLException
  {
    return _bootstrapDao.getBootstrapConn().getSrcTableName(srcId);
  }

  @Override
  public void start(long startSCN)
  {
    _scn = startSCN;
  }

  @Override
  public void startEvents() {
  }

  @Override
  public boolean appendEvent(DbusEventKey key,
                             short pPartitionId,
                             short lPartitionId,
                             long timeStamp,
                             short srcId,
                             byte[] schemaId,
                             byte[] value,
                             boolean enableTracing,
                             DbusEventsStatisticsCollector statsCollector)
  {
    return appendEvent(key, _scn, pPartitionId, lPartitionId, timeStamp, srcId,
                       schemaId, value, enableTracing, statsCollector);
  }

  @Override
  @Deprecated
  public boolean appendEvent(DbusEventKey key,
                             short pPartitionId,
                             short lPartitionId,
                             long timeStamp,
                             short srcId,
                             byte[] schemaId,
                             byte[] value,
                             boolean enableTracing) {
    throw new RuntimeException("Not implemented!!!");
  }

  public boolean appendEvent(DbusEventKey key,
                             DbusEventKey seederChunkKey,
                             long sequenceId,
                             short pPartitionId,
                             short lPartitionId,
                             long timeStamp,
                             short srcId,
                             byte[] schemaId,
                             byte[] value,
                             boolean enableTracing,
                             DbusEventsStatisticsCollector statsCollector)
  {
    DbusEventInfo eventInfo = new DbusEventInfo(DbusOpcode.UPSERT, sequenceId, pPartitionId, lPartitionId,
                                                timeStamp, srcId, schemaId, value, enableTracing,
                                                false);
    return appendEvent(key, seederChunkKey, eventInfo, statsCollector);
  }

  @Override
  public boolean appendEvent(DbusEventKey key,
                             short pPartitionId,
                             short lPartitionId,
                             long timeStamp,
                             short srcId,
                             byte[] schemaId,
                             byte[] value,
                             boolean enableTracing,
                             boolean isReplicated,
                             DbusEventsStatisticsCollector statsCollector)
  {
    throw new RuntimeException("This API not expected to be called on BootstrapDBSeeder !!");
  }

  public boolean appendEvent(DbusEventKey key,
                             DbusEventKey seederChunkKey,
                             DbusEventInfo eventInfo,
                             DbusEventsStatisticsCollector statsCollector)
  {
    boolean isDebugEnabled = LOG.isDebugEnabled();

    boolean ret = false;
    PreparedStatement stmt = null;

    /*
     * Track the startSCN
     */
    if ( _startSCN == -1 )
      _startSCN = eventInfo.getSequenceId();

    short srcId = eventInfo.getSrcId();
    long sequenceId = eventInfo.getSequenceId();

    try
    {
      //long start = System.nanoTime();
      if ( seederChunkKey.getKeyType() == KeyType.LONG )
      {
        _lastSeenKey = "" + seederChunkKey.getLongKey();
      } else {
        _lastSeenKey = seederChunkKey.getStringKey();
      }

      if (isDebugEnabled)
        LOG.debug("Seeder Chunk Key is: " + seederChunkKey + ",EventInfo :" + eventInfo + ", Key is :" + key);

      eventInfo.setOpCode(null);
      eventInfo.setAutocommit(true);

      if (eventInfo.getValueLength() >= FIFTY_MB_IN_BYTES)
      {
        LOG.fatal("Event Size larger than 50 MB. For Key :" + key + ", avro record size is : "+ eventInfo.getValueLength());
      }

      DbusEventFactory.serializeEvent(key, _buf, eventInfo);
      long end = System.nanoTime();

      stmt = _statementMap.get(srcId);

      if ( null == stmt)
      {
        stmt = prepareInsertStatement(srcId);
        _statementMap.put(srcId, stmt);
      }

      if ( isDebugEnabled)
      {
        LOG.debug("Number of Bytes in serialized format:" + _buf.position());
        LOG.debug("Key is :" + ( (key.getKeyType() == KeyType.LONG) ? key.getLongKey() : key.getStringKey()));
      }

      stmt.setLong(1,sequenceId);
      String keyStr = null;

      if (key.getKeyType() == DbusEventKey.KeyType.LONG)
      {
        keyStr = key.getLongKey().toString();
      } else {
        keyStr = key.getStringKey();
      }
      stmt.setString(2, keyStr);

      // Reuse the iStream to set the blob
      _bufStream.reset();
      stmt.setBlob(3, _bufStream, _buf.position());
      //stmt.setBytes(3, _buf.array());

      stmt.setLong(4,sequenceId);

      // Reuse the iStream to se the blob
      _bufStream2.reset();
      stmt.setBlob(5, _bufStream2, _buf.position());
      //stmt.setBytes(5, _buf.array());

      stmt.executeUpdate();

      long end2   = System.nanoTime();

      _totLatency += (end2 - end);

      _currSrcId = srcId;
      ret = true;
    } catch (SQLException sqlEx) {
      LOG.error("Error occured while inserting record for key:" +
          key.getStringKey() + "(" + key.getLongKey() + ") with sequenceId:" + sequenceId, sqlEx);
      throw new RuntimeException(sqlEx);
    } catch (KeyTypeNotImplementedException ex) {
      LOG.error("KeyNotImplemented error while inserting record for key:" +
          key.getStringKey() + "(" + key.getLongKey() + ") with sequenceId:" + sequenceId, ex);
      throw new RuntimeException(ex);
    }
    finally {
      _buf.clear();
    }

    return ret;
  }


  @Override
  public void rollbackEvents() {
    Connection conn = getConnection();
    try
    {
      LOG.error("Rolling back uncommitted bootstrap events");
      conn.rollback();
    catch (SQLException e) {
      LOG.error("Unable to rollback while seeding bootstrap.", e);
    }
  }

  @Override
  public void endEvents(boolean updateWindowScn, long windowScn,
                        boolean updateIndex, boolean callListener, DbusEventsStatisticsCollector statsCollector) {

  }

  /*
   * Callback for processing end of source
   */
  public void endSource(long scn)
  {
    String seederSql = "update bootstrap_seeder_state set endscn = ? where srcid = ? ";
    Connection conn = null;
    PreparedStatement stmt = null;
    try
    {
      conn = getConnection();
      stmt = conn.prepareStatement(seederSql);
      stmt.setLong(1,scn);
      stmt.setInt(2,_currSrcId);
      stmt.executeUpdate();
      conn.commit();
    } catch (SQLException sqlEx) {
      throw new RuntimeException(sqlEx);
    } finally {
      DBHelper.close(stmt);
    }
  }

  /*
   * Callback for processing end of Seeding
   */
  public void endSeeding()
  {
    //Copy seeder state to producer and applier state
    Connection conn = null;
    Statement stmt = null;
    ResultSet rs = null;
    long minScn  = -1;
    try
    {
      String sql = getMinSCNSeederQuery();
      conn = getConnection();
      stmt = conn.createStatement();
      LOG.info("Executing Min startSCN Query in Bootstrap DB:" + sql);
      rs = stmt.executeQuery(sql);
      rs.next();
      minScn = rs.getLong(1);
      LOG.info("Minimum startSCN of all the sources which were seeded was :" + minScn);
      updateBootstrapStateTable(minScn,"bootstrap_applier_state");
      updateBootstrapStateTable(minScn,"bootstrap_producer_state");
      createLogTableRows();

      // Go to the next bootstrap state for this Source
      setBootstrapSourceStatus(BootstrapProducerStatus.SEEDING_CATCHUP);
      conn.commit();
    } catch (SQLException sqlEx) {
      LOG.error("Got Exception while updating bootstrap state tables",sqlEx);
      try
      {
        conn.rollback();
      }catch(Exception ex) {
        LOG.error("Error while rolling back ...",ex);
      }
      throw new RuntimeException(sqlEx);
    } catch (Exception e) {
      throw new RuntimeException(e);
    } finally {
      DBHelper.close(rs,stmt,null);
    }
  }

  private void updateBootstrapStateTable(long scn, String table)
      throws SQLException
      {
    String upsertSQL = getBootstrapStateInsertStmt(table);
    Connection conn = null;
    PreparedStatement stmt = null;
    try
    {
      conn = getConnection();
      stmt = conn.prepareStatement(upsertSQL);

      for (OracleTriggerMonitoredSourceInfo srcInfo : _sources)
      {
        stmt.setInt(1, srcInfo.getSourceId());
        stmt.setInt(2,0);
        stmt.setLong(3,scn);
        stmt.setLong(4, 0);
        stmt.setInt(5,0);
        stmt.setLong(6, scn);
        stmt.setLong(7,0);
        stmt.executeUpdate();
      }
    } finally {
      DBHelper.close(stmt);
    }
      }

  private void createLogTableRows()
      throws SQLException
      {
    String sql = "insert into bootstrap_loginfo (srcid, logid, minwindowscn, maxwindowscn, maxrid) values ( ?, 0, -1, -1, 0)";
    Connection conn = null;
    PreparedStatement stmt = null;
    try
    {
      conn = getConnection();
      stmt = conn.prepareStatement(sql);

      for (OracleTriggerMonitoredSourceInfo srcInfo : _sources)
      {
        try
        {
          stmt.setInt(1,srcInfo.getSourceId());
          stmt.executeUpdate();
          _bootstrapDao.createNewLogTable(srcInfo.getSourceId());
        } catch ( SQLException sqlEx) {
          LOG.error("Got Error inserting entry into bootstrap_loginfo but proceeding !!", sqlEx);
        }
      }
    } catch ( SQLException sqlEx) {
      LOG.error("Got Error inserting entry into bootstrap_loginfo !!", sqlEx);
      throw sqlEx;
    } finally {
      DBHelper.close(stmt);
    }
      }

  private String getBootstrapStateInsertStmt(String table)
  {
    StringBuilder sql = new StringBuilder();
    sql.append("insert into ").append(table);
    sql.append(" (srcid, logid, windowscn, rid) values ( ?, ?, ?, ?) ");
    sql.append(" on duplicate key update logid = ?, windowscn = ?, rid = ?");
    return sql.toString();
  }

  private String getMinSCNSeederQuery()
  {
    StringBuilder buf = new StringBuilder();
    buf.append("select min(startscn) from bootstrap_seeder_state where srcid IN (");

    for (int i = 0 ; i < _sources.size() - 1; i++ )
    {
      buf.append(_sources.get(i).getSourceId()).append(", ");
    }

    buf.append(_sources.get(_sources.size() -1).getSourceId());
    buf.append(")");
    return buf.toString();
  }

  @Override
  public void endEvents(long rowId, DbusEventsStatisticsCollector statsCollector)
  {

    Connection conn = null;
    PreparedStatement stmt = null;

    try
    {
      StringBuilder sql = new StringBuilder();
      //sql.append("insert into bootstrap_applier_state ");
      //sql.append("values (?,?,?,?) ");
      //sql.append("on duplicate key update rid = ?");
      sql.append("insert into bootstrap_seeder_state ");
      sql.append("values ( ?, ?, -1, ?, ?)");
      sql.append("on duplicate key update rid = ?, srckey = ?"); //startscn set only at the first insert

      conn = getConnection();
      stmt = conn.prepareStatement(sql.toString());
      stmt.setInt(1,_currSrcId);
      stmt.setLong(2, _startSCN);
      stmt.setLong(3,rowId);
      stmt.setString(4,_lastSeenKey);
      stmt.setLong(5,rowId);
      stmt.setString(6, _lastSeenKey);

      LOG.info("\t Total Latency before commit :" + (_totLatency/1000000000));
      stmt.executeUpdate();
      long start = System.nanoTime();
      conn.commit();
      long end = System.nanoTime();
      long lat = (end - start)/1000000;
      LOG.info("\t Comit time (msec) :" + lat);
    } catch (Exception e) {
      LOG.fatal("Got Exception in endEvents !!", e);
      throw new RuntimeException("Got Exception in endEvents !!", e);
    } finally {
      // Dont close conn as it is being reused.
      DBHelper.close(stmt);
    }
  }


  @Override
  public boolean empty() {
    return false;
  }

  @Override
  public int readEvents(ReadableByteChannel readChannel,
                        Iterable<InternalDatabusEventsListener> eventListeners,
                        DbusEventsStatisticsCollector statsCollector)
                            throws InvalidEventException {
    return 0;
  }


  public long getRowIdFromCheckpoint(OracleTriggerMonitoredSourceInfo sourceInfo)
  {
    Connection conn = null;
    PreparedStatement stmt = null;
    ResultSet row = null;
    long rowId = -1;
    try
    {
      StringBuilder sql = new StringBuilder();
      sql.append("select rid from bootstrap_seeder_state ");
      sql.append("where srcid = ?");

      conn = getConnection();
      stmt = conn.prepareStatement(sql.toString());

      stmt.setInt(1, sourceInfo.getSourceId());
      row = stmt.executeQuery();

      if (row.next())
      {
        rowId = row.getLong(1);
      }

    } catch (Exception e) {
      LOG.error("Got exception while trying to fetch the rowid from bootstrap_seeder_state !!", e);
    } finally {
      DBHelper.close(row, stmt, null);
    }

    return rowId;
  }

  /*
   * TODO: Refactor to do one query to get rid and srckey from bootstrap_seeder_state
   */
  public String geSrcKeyFromCheckpoint(OracleTriggerMonitoredSourceInfo sourceInfo)
  {
    Connection conn = null;
    PreparedStatement stmt = null;
    ResultSet row = null;
    String srcKey = null;
    try
    {
      StringBuilder sql = new StringBuilder();
      sql.append("select srckey from bootstrap_seeder_state ");
      sql.append("where srcid = ?");

      conn = getConnection();
      stmt = conn.prepareStatement(sql.toString());

      stmt.setInt(1, sourceInfo.getSourceId());
      row = stmt.executeQuery();

      if (row.next())
      {
        srcKey = row.getString(1);
      }

    } catch (Exception e) {
      LOG.error("Got exception while trying to fetch the srckey from bootstrap_seeder_state !!", e);
    } finally {
      DBHelper.close(row, stmt, null);
    }

    return srcKey;
  }

  @Override
  public long getMinScn()
  {
    return 0;
  }

  @Override
  public long lastWrittenScn()
  {
    return 0;
  }


  @Override
  public void setStartSCN(long sinceSCN)  { }


  @Override
  public long getPrevScn()
  {
    return 0;
  }


  @Override
  public boolean appendEvent(DbusEventKey key, long sequenceId,
                             short pPartitionId, short lPartitionId, long timeStamp, short srcId,
                             byte[] schemaId, byte[] value, boolean enableTracing,
                             DbusEventsStatisticsCollector statsCollector) {
    return false;
  }


  @Override
  public boolean appendEvent(DbusEventKey key, DbusEventInfo eventInfo,
                             DbusEventsStatisticsCollector statsCollector) {
    return false;
  }
}
TOP

Related Classes of com.linkedin.databus.bootstrap.utils.BootstrapDBSeeder

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.