Package org.apache.hadoop.raid

Source Code of org.apache.hadoop.raid.DBUtils

/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you 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 org.apache.hadoop.raid;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.raid.DBConnectionFactory;
import org.apache.hadoop.util.ReflectionUtils;

/**
* Utilities for writing hooks.
*/
public class DBUtils {
  static final private Log LOG = LogFactory.getLog(DBUtils.class);

  // The default value is to retry 20 times with maximum retry interval
  // 60 seconds. The expectation is about 22 minutes. After 7 retries, it
  // reaches 60 seconds.
  static final int DEFAULT_SQL_NUM_RETRIES = 20;
  static final int DEFAULT_RETRY_MAX_INTERVAL_SEC = 60;
  public static final String DB_MAX_RETRY =
      "hdfs.raid.db.max_retries";
  public static final int DEFAULT_DB_MAX_RETRY = 30;
  public static final String RAID_DB_CONNECTION_FACTORY_CLASS_KEY =
      "hdfs.raid.db.connection.factory.class";
  public static final int RETRY_MAX_INTERVAL_SEC = 60;
  public static final List<Object> EMPTY_SQL_PARAMS = new ArrayList<Object>();
  public static Random rand = new Random();
  public static int numDBOpenObjects = 0;
  public static Map<Boolean, String> defaultUrls =
      Collections.synchronizedMap(new HashMap<Boolean, String>());

  public static Long selectCount(DBConnectionFactory connectionFactory,
      String sql, List<Object> sqlParams, int sqlNumRetries,
      String tblName)
      throws IOException {
    List<List<Object>> results =
        DBUtils.runInsertSelect(connectionFactory, sql, sqlParams, true,
            sqlNumRetries, DBUtils.RETRY_MAX_INTERVAL_SEC, false, false);
    Long count = null;
    // should be the first field in the first element
    if (results != null && !results.isEmpty() && results.get(0).get(0) != null) {
      count = (Long)results.get(0).get(0);
    }
    if (count == null) {
      throw new IOException("You cannot select from " + tblName);
    }
    return count;
  }
 
  public static DBConnectionFactory getDBConnectionFactory(Configuration conf)
      throws IOException {
      Class<?> DBFactoryClass = null;
      DBFactoryClass = conf.getClass(
          DBUtils.RAID_DB_CONNECTION_FACTORY_CLASS_KEY,
          SimpleDBConnectionFactory.class);
      if (DBFactoryClass == null) {
        throw new IOException("Connection factory key: " +
            DBUtils.RAID_DB_CONNECTION_FACTORY_CLASS_KEY + " is not set!");
      }
      DBConnectionFactory connectionFactory =
          (DBConnectionFactory) ReflectionUtils.newInstance(DBFactoryClass,
                                                            conf);
      connectionFactory.initialize(conf);
      return connectionFactory;
    }
 
  public static int getSqlNumRetry(Configuration conf) {
    return conf.getInt(DB_MAX_RETRY, DEFAULT_DB_MAX_RETRY);
  }

  public static void runInsert(DBConnectionFactory connectionFactory, String sql,
                               List<Object> sqlParams, int numRetries)
      throws IOException {
    runInsertSelect(connectionFactory, sql, sqlParams, true, numRetries,
                    DEFAULT_RETRY_MAX_INTERVAL_SEC, true, false);
  }
 
  public static List<List<Object>> getResults(ResultSet result)
      throws SQLException {
    List<List<Object>> results = new ArrayList<List<Object>>();
    int numColumns = result.getMetaData().getColumnCount();
    while (result.next()) {
      List<Object> row = new ArrayList<Object>();
      results.add(row);
      for (int index = 1; index <= numColumns; index++) {
        row.add(result.getObject(index));
      }
    }
    return results;
  }
 
  public static void close(ResultSet generatedKeys,
      PreparedStatement[] pstmts, Connection conn) {
    if (generatedKeys != null) {
      try {
        generatedKeys.close();
      } catch (Exception e) {
        LOG.warn("Error to close ResultSet", e);
      } finally {
        try {
          if (!generatedKeys.isClosed()) {
            LOG.warn("ResultSet is not closed");
            DBUtils.numDBOpenObjects++;
          }
        } catch (Exception ignore) {DBUtils.numDBOpenObjects++;}
      }
    }
    if (pstmts != null && pstmts.length > 0) {
      for (PreparedStatement pstmt: pstmts) {
        if (pstmt == null) {
          continue;
        }
        try {
          pstmt.close();
        } catch (Exception e) {
          LOG.warn("Error to close PreparedStatement", e);
        } finally {
          try {
            if (!pstmt.isClosed()) {
              LOG.warn("PreparedStatement is not closed");
              DBUtils.numDBOpenObjects++;
            }
          } catch (Exception ignore) {DBUtils.numDBOpenObjects++;}
        }
      }
    }
    if (conn!=null) {
      try {
        conn.close();
      } catch (Exception e) {
        LOG.warn("Error to close Connection", e);
      } finally {
        try {
          if (!conn.isClosed()) {
            LOG.warn("Connection is not closed");
            DBUtils.numDBOpenObjects++;
          }
        } catch (Exception ignore) {DBUtils.numDBOpenObjects++;}
      }
    }
  }

  // In the case of a select returns a list of lists, where each inner list represents a row
  // returned by the query.  In the case of an insert, returns null.
  public static List<List<Object>> runInsertSelect(
      DBConnectionFactory connectionFactory, String sql,
    List<Object> sqlParams, boolean isWrite, int numRetries,
    int retryMaxInternalSec, boolean insert, boolean getGeneratedKeys)
        throws IOException {
    int waitMS = 3000; // wait for at least 3s before next retry.
    for (int i = 0; i < numRetries; ++i) {
      Connection conn = null;
      ResultSet generatedKeys = null;
      PreparedStatement pstmt = null;
      String url = null;
      try {
        try {
          url = connectionFactory.getUrl(isWrite);
        } catch (IOException ioe) {
          LOG.warn("Cannot get DB URL, fall back to the default one", ioe);
          url = defaultUrls.get(isWrite);
          if (url == null) {
            throw ioe;
          }
        }
        LOG.info("Attepting connection with URL " + url);
        conn = connectionFactory.getConnection(url);
        defaultUrls.put(isWrite, url);
        pstmt = getPreparedStatement(conn, sql, sqlParams,
            getGeneratedKeys);
        if (insert) {
          int recordsUpdated = pstmt.executeUpdate();
          LOG.info("rows inserted: " + recordsUpdated + " sql: " + sql);
          List<List<Object>> results = null;
          if (getGeneratedKeys) {
            generatedKeys = pstmt.getGeneratedKeys();
            results = getResults(generatedKeys);
          }
          Thread.sleep(connectionFactory.getDBOpsSleepTime() +
              rand.nextInt(1000));
          return results;
        }
        else {
          generatedKeys = pstmt.executeQuery();
          List<List<Object>> results = getResults(generatedKeys);
          pstmt.clearBatch();
          LOG.info("rows selected: " + results.size() + " sql: " + sql);
          Thread.sleep(connectionFactory.getDBOpsSleepTime() +
              rand.nextInt(1000));
          return results;
        }
      } catch (Exception e) {
        // We should catch a better exception than Exception, but since
        // DBConnectionUrlFactory.getUrl() defines throws Exception, it's hard
        // for us to figure out the complete set it can throw. We follow
        // DBConnectionUrlFactory.getUrl()'s definition to catch Exception.
        // It shouldn't be a big problem as after numRetries, we anyway exit.
        LOG.info("Exception " + e + ". Will retry " + (numRetries - i)
            + " times.");
        // Introducing a random factor to the wait time before another retry.
        // The wait time is dependent on # of failures and a random factor.
        // At the first time of getting a SQLException, the wait time
        // is a random number between [0,300] msec. If the first retry
        // still fails, we will wait 300 msec grace period before the 2nd retry.
        // Also at the second retry, the waiting window is expanded to 600 msec
        // alleviating the request rate from the server. Similarly the 3rd retry
        // will wait 600 msec grace period before retry and the waiting window
        // is
        // expanded to 1200 msec.

        waitMS += waitMS;
        if (waitMS > retryMaxInternalSec * 1000) {
          waitMS = retryMaxInternalSec * 1000;
        }
        double waitTime = waitMS + waitMS * rand.nextDouble();
        if (i + 1 == numRetries) {
          LOG.error("Still got Exception after " + numRetries + "  retries.",
              e);
          throw new IOException(e);
        }
        try {
          Thread.sleep((long) waitTime);
        } catch (InterruptedException ie) {
          throw new IOException(ie);
        }
      } finally {
        DBUtils.close(generatedKeys, new PreparedStatement[]{pstmt}, conn);
      }
    }
    return null;
  }
 
  public static PreparedStatement getPreparedStatement(Connection conn,
      String sql, List<Object> sqlParams, boolean getGeneratedKeys)
      throws IOException, SQLException {
    PreparedStatement pstmt = getGeneratedKeys? conn.prepareStatement(sql,
        Statement.RETURN_GENERATED_KEYS) : conn.prepareStatement(sql);
    int pos = 1;
    for (Object param : sqlParams) {
      if (param instanceof Integer) {
        pstmt.setInt(pos++, ((Integer) param).intValue());
      } else {
        pstmt.setString(pos++, (String) param);
      }
    }
    return pstmt;
  }
}
TOP

Related Classes of org.apache.hadoop.raid.DBUtils

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.