/**
   * Execute this call to fill in heartbeat data on the slave. This call must be invoked after a
   * heartbeat event is applied.
   */
  public void completeHeartbeat(Database database, long seqno, String eventId) throws SQLException {
    if (logger.isDebugEnabled()) logger.debug("Processing slave heartbeat update");

    Statement st = null;
    ResultSet rs = null;
    Timestamp sts = new Timestamp(0);
    Timestamp now = new Timestamp(System.currentTimeMillis());
    ArrayList<Column> whereClause = new ArrayList<Column>();
    ArrayList<Column> values = new ArrayList<Column>();

    if (logger.isDebugEnabled()) logger.debug("Processing slave heartbeat update: " + now);

    // Get the source timestamp.
    try {
      st = database.createStatement();
      rs = st.executeQuery(sourceTsQuery);
      if (rs.next()) sts = rs.getTimestamp(1);
    } finally {
      if (rs != null) {
        try {
          rs.close();
        } catch (SQLException e) {
        }
      }
      if (st != null) {
        try {
          st.close();
        } catch (SQLException e) {
        }
      }
    }

    // Compute the difference between source and target.
    long lag_millis = now.getTime() - sts.getTime();

    // Update the heartbeat record with target time and difference.
    hbId.setValue(KEY);
    whereClause.add(hbId);

    hbSeqno.setValue(seqno);
    hbEventId.setValue(eventId);
    hbTargetTstamp.setValue(now);
    hbLagMillis.setValue(lag_millis);
    values.add(hbSeqno);
    values.add(hbEventId);
    values.add(hbTargetTstamp);
    values.add(hbLagMillis);

    database.update(hbTable, whereClause, values);
  }
  /**
   * Applies a heartbeat update on the slave. This call is designed for data warehouses that cannot
   * apply a heartbeat using batch loading mechanisms.
   */
  public void applyHeartbeat(Database database, Timestamp sourceTimestamp, String name)
      throws SQLException {
    ArrayList<Column> whereClause = new ArrayList<Column>();
    ArrayList<Column> values = new ArrayList<Column>();

    if (logger.isDebugEnabled())
      logger.debug(
          "Applying heartbeat to slave: name=" + name + " sourceTstamp=" + sourceTimestamp);

    hbId.setValue(KEY);
    whereClause.add(hbId);
    hbSourceTstamp.setValue(sourceTimestamp);
    hbName.setValue(name);
    values.add(hbSourceTstamp);
    values.add(hbName);
    database.update(hbTable, whereClause, values);
  }
  /** Set up the heartbeat table on the master. */
  public void initializeHeartbeatTable(Database database) throws SQLException {
    if (logger.isDebugEnabled()) logger.debug("Initializing heartbeat table");

    // Create the table if it does not exist.
    if (database.findTungstenTable(hbTable.getSchema(), hbTable.getName()) == null) {
      database.createTable(this.hbTable, false, this.hbTable.getSchema(), tableType, serviceName);
    }

    // Add an initial heartbeat value if needed
    ResultSet res = null;
    PreparedStatement hbRowCount = null;
    int rows = 0;

    try {
      hbRowCount =
          database.prepareStatement(
              "SELECT count(*) from " + this.hbTable.getSchema() + "." + this.hbTable.getName());
      res = hbRowCount.executeQuery();
      if (res.next()) {
        rows = res.getInt(1);
      }
    } finally {
      if (res != null) {
        try {
          res.close();
        } catch (SQLException e) {
        }
      }
      if (hbRowCount != null) {
        try {
          hbRowCount.close();
        } catch (Exception e) {
        }
      }
    }

    if (rows == 0) {

      hbId.setValue(KEY);
      hbSourceTstamp.setValue(new Timestamp(System.currentTimeMillis()));
      hbSalt.setValue(saltValue.getAndIncrement());
      database.insert(hbTable);
    }
  }
  /**
   * Execute this call to start a named heartbeat on the master. The heartbeat table update must be
   * logged as we will expect to see it as a DBMSEvent.
   */
  public void startHeartbeat(Database database, String name) throws SQLException {
    ArrayList<Column> whereClause = new ArrayList<Column>();
    ArrayList<Column> values = new ArrayList<Column>();
    Timestamp now = new Timestamp(System.currentTimeMillis());

    if (logger.isDebugEnabled())
      logger.debug("Processing master heartbeat update: name=" + name + " time=" + now);

    hbId.setValue(KEY);
    whereClause.add(hbId);
    hbSourceTstamp.setValue(now);
    hbSalt.setValue(saltValue.getAndIncrement());
    hbName.setValue(name);
    values.add(hbSourceTstamp);
    values.add(hbSalt);
    values.add(hbName);

    database.update(hbTable, whereClause, values);
  }