Package com.oltpbenchmark.api

Source Code of com.oltpbenchmark.api.Worker

package com.oltpbenchmark.api;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Savepoint;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;

import org.apache.log4j.Logger;

import com.oltpbenchmark.BenchmarkState;
import com.oltpbenchmark.LatencyRecord;
import com.oltpbenchmark.Phase;
import com.oltpbenchmark.WorkloadConfiguration;
import com.oltpbenchmark.api.Procedure.UserAbortException;
import com.oltpbenchmark.catalog.Catalog;
import com.oltpbenchmark.types.DatabaseType;
import com.oltpbenchmark.types.State;
import com.oltpbenchmark.types.TransactionStatus;
import com.oltpbenchmark.util.Histogram;
import com.oltpbenchmark.util.StringUtil;

public abstract class Worker implements Runnable {
    private static final Logger LOG = Logger.getLogger(Worker.class);

  private BenchmarkState testState;
  private LatencyRecord latencies;
 
  private final int id;
  private final BenchmarkModule benchmarkModule;
  protected final Connection conn;
  protected final WorkloadConfiguration wrkld;
  protected final TransactionTypes transactionTypes;
  protected final Map<TransactionType, Procedure> procedures = new HashMap<TransactionType, Procedure>();
  protected final Map<String, Procedure> name_procedures = new HashMap<String, Procedure>();
  protected final Map<Class<? extends Procedure>, Procedure> class_procedures = new HashMap<Class<? extends Procedure>, Procedure>();
 
  private final Histogram<TransactionType> txnSuccess = new Histogram<TransactionType>();
  private final Histogram<TransactionType> txnAbort = new Histogram<TransactionType>();
  private final Histogram<TransactionType> txnRetry = new Histogram<TransactionType>();
  private final Histogram<TransactionType> txnErrors = new Histogram<TransactionType>();
  private final Map<TransactionType, Histogram<String>> txnAbortMessages = new HashMap<TransactionType, Histogram<String>>();
 
  private boolean seenDone = false;
 
  public Worker(BenchmarkModule benchmarkModule, int id) {
    this.id = id;
    this.benchmarkModule = benchmarkModule;
    this.wrkld = this.benchmarkModule.getWorkloadConfiguration();
    this.transactionTypes = this.wrkld.getTransTypes();
    assert(this.transactionTypes != null) :
        "The TransactionTypes from the WorkloadConfiguration is null!";
   
    try {
        this.conn = this.benchmarkModule.makeConnection();
        this.conn.setAutoCommit(false);
        conn.setTransactionIsolation(wrkld.getIsolationMode());
    } catch (SQLException ex) {
        throw new RuntimeException("Failed to connect to database", ex);
    }
   
    // Generate all the Procedures that we're going to need
    this.procedures.putAll(this.benchmarkModule.getProcedures());
    assert(this.procedures.size() == this.transactionTypes.size()) :
        String.format("Failed to get all of the Procedures for %s [expected=%d, actual=%d]",
                      this.benchmarkModule.getBenchmarkName(),
                      this.transactionTypes.size(),
                      this.procedures.size());
        for (Entry<TransactionType, Procedure> e : this.procedures.entrySet()) {
            Procedure proc = e.getValue();
            this.name_procedures.put(e.getKey().getName(), proc);
            this.class_procedures.put(proc.getClass(), proc);
            // e.getValue().generateAllPreparedStatements(this.conn);
        } // FOR
  }
 
  /**
   * Get the BenchmarkModule managing this Worker
   */
  @SuppressWarnings("unchecked")
    public final <T extends BenchmarkModule> T getBenchmarkModule() {
      return ((T)this.benchmarkModule);
  }
  /**
   * Get the unique thread id for this worker
   */
  public final int getId() {
    return this.id;
  }
  /**
   * Get the the total number of workers in this benchmark invocation
   */
  public final int getNumWorkers() {
      return (this.benchmarkModule.getWorkloadConfiguration().getTerminals());
  }
  public final WorkloadConfiguration getWorkloadConfiguration() {
      return (this.benchmarkModule.getWorkloadConfiguration());
  }
  public final Catalog getCatalog() {
      return (this.benchmarkModule.getCatalog());
  }
  public final Random rng() {
      return (this.benchmarkModule.rng());
  }
 
  public final Connection getConnection() {
      return (this.conn);
  }
  public final int getRequests() {
        return latencies.size();
    }
    public final Iterable<LatencyRecord.Sample> getLatencyRecords() {
        return latencies;
    }
 
  public final Procedure getProcedure(TransactionType type) {
        return (this.procedures.get(type));
    }
  @Deprecated
    public final Procedure getProcedure(String name) {
        return (this.name_procedures.get(name));
    }
    @SuppressWarnings("unchecked")
    public final <T extends Procedure> T getProcedure(Class<T> procClass) {
        return (T)(this.class_procedures.get(procClass));
    }
   
    public final Histogram<TransactionType> getTransactionSuccessHistogram() {
        return (this.txnSuccess);
    }
    public final Histogram<TransactionType> getTransactionRetryHistogram() {
        return (this.txnRetry);
    }
    public final Histogram<TransactionType> getTransactionAbortHistogram() {
        return (this.txnAbort);
    }
    public final Histogram<TransactionType> getTransactionErrorHistogram() {
        return (this.txnErrors);
    }
    public final Map<TransactionType, Histogram<String>> getTransactionAbortMessageHistogram() {
        return (this.txnAbortMessages);
    }
   
    /**
     * Get unique name for this worker's thread
     */
    public final String getName() {
        return String.format("worker%03d", this.getId());
    }
   
  @Override
  public final void run() {
      Thread t = Thread.currentThread();
      t.setName(this.getName());
     
    // In case of reuse reset the measurements
    latencies = new LatencyRecord(testState.getTestStartNs());
    boolean isRateLimited = testState.isRateLimited();

    // Invoke the initialize callback
    try {
        this.initialize();
    } catch (Throwable ex) {
        throw new RuntimeException("Unexpected error when initializing " + this.getName(), ex);
    }
   
    // wait for start
    testState.blockForStart();
    State state = testState.getState();
   
    TransactionType invalidTT = TransactionType.INVALID;
    assert(invalidTT != null);
   
    while (true) {
      if (state == State.DONE && !seenDone) {
        // This is the first time we have observed that the test is
        // done notify the global test state, then continue applying load
        seenDone = true;
        testState.signalDone();
        break;
      }
      Phase phase = null;
      // apply load
      if (isRateLimited) {
        // re-reads the state because it could have changed if we
        // blocked
        state = testState.fetchWork();
        phase = testState.fetchWorkType();
      }

      boolean measure = state == State.MEASURE;

      // TODO: Measuring latency when not rate limited is ... a little
      // weird because
      // if you add more simultaneous clients, you will increase
      // latency (queue delay)
      // but we do this anyway since it is useful sometimes
      long start = 0;
      if (measure) {
        start = System.nanoTime();
      }

      TransactionType type = invalidTT;
      if (phase != null) type = doWork(measure, phase);
//      assert(type != null) :
//          "Unexpected null TransactionType returned from doWork\n" + this.transactionTypes;
     
      if (measure && type !=null) {
        long end = System.nanoTime();
        latencies.addLatency(type.getId(), start, end);
      }
      state = testState.getState();
    }

    tearDown(false);
    testState = null;
  }

  /**
   * Called in a loop in the thread to exercise the system under test.
   * Each implementing worker should return the TransactionType handle that
   * was executed.
   *
   * @param llr
   */
  protected final TransactionType doWork(boolean measure, Phase phase) {
      TransactionType next = null;
      TransactionStatus status = TransactionStatus.RETRY;
      Savepoint savepoint = null;
      final DatabaseType dbType = wrkld.getDBType();
      final boolean recordAbortMessages = wrkld.getRecordAbortMessages();
     
      try {
          while (status == TransactionStatus.RETRY && this.testState.getState() != State.DONE) {
              if (next == null)
                  next = transactionTypes.getType(phase.chooseTransaction());
              assert(next.isSupplemental() == false) :
                  "Trying to select a supplemental transaction " + next;
             
              try {
                  // For Postgres, we have to create a savepoint in order
                  // to rollback a user aborted transaction
//                  if (dbType == DatabaseType.POSTGRES) {
//                      savepoint = this.conn.setSavepoint();
//                      // if (LOG.isDebugEnabled())
//                      LOG.info("Created SavePoint: " + savepoint);
//                  }
                 
                  status = this.executeWork(next);
                  switch (status) {
                      case SUCCESS:
                          this.txnSuccess.put(next);
                          if (LOG.isDebugEnabled())
                                LOG.debug("Executed a new invocation of " + next);
                          break;
                      case RETRY_DIFFERENT:
                          this.txnRetry.put(next);
                          next = null;
                          status = TransactionStatus.RETRY;
                          continue;
                      case RETRY:
                          continue;
                      default:
                          assert(false) :
                              String.format("Unexpected status '%s' for %s", status, next);
                  } // SWITCH
                 
              // User Abort Handling
              // These are not errors
              } catch (UserAbortException ex) {
                    if (LOG.isDebugEnabled()) LOG.debug(next + " Aborted", ex);
                   
                    /* PAVLO */
                    if (recordAbortMessages) {
                        Histogram<String> error_h = this.txnAbortMessages.get(next);
                        if (error_h == null) {
                            error_h = new Histogram<String>();
                            this.txnAbortMessages.put(next, error_h);
                        }
                        error_h.put(StringUtil.abbrv(ex.getMessage(), 20));
                    }
                   
                    if (savepoint != null) {
                        this.conn.rollback(savepoint);
                    } else {
                        this.conn.rollback();
                    }
                    this.txnAbort.put(next);
                    break;
                   
                // Database System Specific Exception Handling
                } catch (SQLException ex) {
                                      
                    //TODO: Handle acceptable error codes for every DBMS    
//                    if (LOG.isDebugEnabled())
                        LOG.warn(next+ " " +  ex.getMessage()+" "+ex.getErrorCode()+ " - " +ex.getSQLState(), ex);

                    this.txnErrors.put(next);
                   
                    if (savepoint != null) {
                        this.conn.rollback(savepoint);
                    } else {
                        this.conn.rollback();
                    }
                    if (ex.getErrorCode() == 1213 && ex.getSQLState().equals("40001")) {
                        // MySQLTransactionRollbackException
                        continue;
                    }
                    if (ex.getErrorCode() == 1205 && ex.getSQLState().equals("4100")) {
                        // MySQL Lock timeout
                        continue;
                    }
                    if (ex.getErrorCode() == 1205 && ex.getSQLState().equals("40001")) {
                        // SQLServerException Deadlock
                        continue;
                    }
                    if (ex.getErrorCode() == -911 && ex.getSQLState().equals("40001")) {
                        // DB2Exception Deadlock
                        continue;
                    }
                    if (ex.getErrorCode() == 0 && ex.getSQLState() != null && ex.getSQLState().equals("40001")) {
                        // Postgres serialization
                        continue;
                    }
                    if (ex.getErrorCode() == 8177 && ex.getSQLState().equals("72000")) {
                        // ORA-08177: Oracle Serialization
                        continue;
                    }
                   
                    // UNKNOWN: In this case .. Retry as well!
                    else {
                        continue;
                        //FIXME Disable this for now
                        // throw ex;
                    }
                }
          } // WHILE
      } catch (SQLException ex) {
            throw new RuntimeException(String.format("Unexpected error in %s when executing %s [%s]",
                                                     this.getName(), next, dbType), ex);
        }
       
        return (next);
  }

  /**
   * Optional callback that can be used to initialize the Worker
   * right before the benchmark execution begins
   */
  protected void initialize() {
     // The default is to do nothing
  }
 
    /**
     * Invoke a single transaction for the given TransactionType
     * @param txnType
     * @return TODO
     * @throws UserAbortException TODO
     * @throws SQLException TODO
     */
  protected abstract TransactionStatus executeWork(TransactionType txnType) throws UserAbortException, SQLException;
 
  /**
   * Called at the end of the test to do any clean up that may be
   * required.
   * @param error TODO
   */
  public void tearDown(boolean error) {
    try {
      conn.close();
    } catch (SQLException e) {
      LOG.warn("No connection to close");
    }
  }

  public void setBenchmarkState(BenchmarkState testState) {
    assert this.testState == null;
    this.testState = testState;
  }
}
TOP

Related Classes of com.oltpbenchmark.api.Worker

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.