/**
   * For "iteration" number of cycles, generate user-visit events within the bounds of customer
   * range and user range.
   */
  public void run() {
    final String meth = "run()";
    ILdtOperations ldtOps = dbOps.getLdtOps();

    long startTimeMs = System.currentTimeMillis();
    long endTimeMS = startTimeMs + 1000 * emulationDays * 86400;
    long checkTimeMS = 0;
    long secondStartMS = 0;
    long deltaTimeMS = 0;
    int interTransactionWaitMS = 10;
    long secondCount = 1;
    long opNum = 0;

    try {
      // This thread will try to maintain a given TPS load, where it will
      // measure its performance per second and try to speed up or slow
      // down accordingly.
      // In each second, it will attempt to complete "threadTPS" actions
      // by calling the "working function" doOperation().
      console.info(
          "<%s:%s> ThreadNum(%d) Start: ThreadTPS(%d)", CLASSNAME, meth, threadNumber, threadTPS);
      do {
        // We're going to organize ourselves in terms of TPS and
        // second intervals.  For a given number of TPS, we're going
        // to try to fit in that amount of operations, and then sleep
        // for the remaining amount if we're ahead of schedule in each
        // second.  If we fall behind, then we're going to shorten the
        // amount of time between each operation.
        secondStartMS = System.currentTimeMillis();

        for (int i = 0; i < threadTPS; i++) {
          opNum = (secondCount * threadTPS) + i;
          doOperation((int) opNum, ldtOps);
          Thread.sleep(interTransactionWaitMS);
        } // for each Transaction (per second)
        secondCount++;
        checkTimeMS = System.currentTimeMillis();
        deltaTimeMS = checkTimeMS - secondStartMS;
        console.debug(
            "StartSecond(%d) CheckTime(%d) Delta(%d)", secondStartMS, checkTimeMS, deltaTimeMS);
        if (deltaTimeMS > 1000) {
          if (interTransactionWaitMS > 10) {
            interTransactionWaitMS--;
          } // otherwise, do nothing.
        } else {
          interTransactionWaitMS++;
          Thread.sleep(Math.abs(1000 - deltaTimeMS));
        }
      } while (checkTimeMS < endTimeMS); // end for each generateCount

    } catch (Exception e) {
      e.printStackTrace();
      console.error("<%s:%s>Problem with Thread(%d) ", CLASSNAME, meth, opNum);
    }
  } // end run()
  /**
   * Constructor for URL Tracker EXAMPLE class.
   *
   * @param console
   * @param host
   * @param port
   * @param namespace
   * @param baseNamespace
   * @param cacheNamespace
   * @param fileName
   * @param ldtType
   * @param clean
   * @param remove
   * @param customers
   * @param records
   * @param generateCount
   * @param threadCount
   * @param cleanIntervalSec
   * @param cleanDurationSec
   * @param cleanMethod
   * @param timeToLive
   * @param noLoad
   * @param loadOnly
   * @param noCleanThreads
   * @param doScan
   * @param emulationDays
   * @throws AerospikeException
   */
  public UrlTracker(
      Console console,
      String host,
      int port,
      String namespace,
      String baseNamespace,
      String cacheNamespace,
      String fileName,
      String ldtType,
      boolean clean,
      boolean remove,
      long customers,
      long records,
      long generateCount,
      int threadCount,
      int cleanIntervalSec,
      long cleanDurationSec,
      int cleanMethod,
      long timeToLive,
      boolean noLoad,
      boolean loadOnly,
      boolean noCleanThreads,
      boolean doScan,
      int emulationDays)
      throws AerospikeException {
    this.host = host;
    this.port = port;
    this.namespace = namespace;
    this.baseNamespace = baseNamespace;
    this.cacheNamespace = cacheNamespace;
    this.parms = new DbParameters(host, port, namespace, baseNamespace, cacheNamespace);

    this.dbOps = new DbOps(console, parms, ldtType);

    this.client = dbOps.getClient();
    this.inputFileName = fileName;
    this.ldtType = ldtType;
    this.console = console;
    this.cleanBefore = clean;
    this.cleanAfter = remove;
    this.customerRecords = customers;
    this.threadCount = threadCount;
    this.userRecords = records;
    this.timeToLive = timeToLive;

    // If non-zero, then generate data rather than read from JSON file.
    this.generateCount = generateCount;

    this.cleanIntervalSec = cleanIntervalSec;
    this.cleanDurationSec = cleanDurationSec;
    this.cleanMethod = cleanMethod;

    this.noLoad = noLoad;
    this.loadOnly = loadOnly;

    this.doScan = doScan;

    this.noCleanThreads = noCleanThreads;
    this.emulationDays = emulationDays;

    this.testTiming = new TestTiming();
  } // end UrlTracker constructor