/**
 * Copyright (C) 2014-2016 LinkedIn Corp. (pinot-core@linkedin.com)
 *
 * 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.
 */
package com.linkedin.pinot.tools.perf;

import com.google.common.base.Preconditions;
import com.linkedin.pinot.core.segment.index.SegmentMetadataImpl;
import com.linkedin.pinot.tools.AbstractBaseCommand;
import com.linkedin.pinot.tools.Command;
import java.io.File;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.kohsuke.args4j.Option;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


@SuppressWarnings("FieldCanBeLocal")
public class PerfBenchmarkRunner extends AbstractBaseCommand implements Command {
  private static final Logger LOGGER = LoggerFactory.getLogger(PerfBenchmarkRunner.class);

  @Option(name = "-mode", required = true, metaVar = "<String>",
      usage = "Mode of the PerfBenchmarkRunner (startAll|startAllButServer|startServerWithPreLoadedSegments).")
  private String _mode;

  @Option(name = "-dataDir", required = false, metaVar = "<String>", usage = "Path to data directory.")
  private String _dataDir;

  @Option(name = "-tempDir", required = false, metaVar = "<String>",
      usage = "Path to temporary directory to start the cluster")
  private String _tempDir = "/tmp/";

  @Option(name = "-loadMode", required = false, metaVar = "<String>", usage = "Load mode of the segments (HEAP|MMAP).")
  private String _loadMode = "HEAP";

  @Option(name = "-segmentFormatVersion", required = false, metaVar = "<String>",
      usage = "Segment format version to be loaded (v1|v3).")
  private String _segmentFormatVersion = "v1";

  @Option(name = "-batchLoad", required = false, metaVar = "<boolean>", usage = "Batch load multiple tables.")
  private boolean _isBatchLoad;

  @Option(name = "-numThreads", required = false, metaVar = "<int>",
      usage = "Number of threads for batch load (default 10).")
  private int _numThreads = 10;

  @Option(name = "-timeoutInSeconds", required = false, metaVar = "<int>",
      usage = "Timeout in seconds for batch load (default 60).")
  private int _timeoutInSeconds = 60;

  @Option(name = "-tableNames", required = false, metaVar = "<String>",
      usage = "Comma separated table names to be loaded (non-batch load).")
  private String _tableNames;

  @Option(name = "-invertedIndexColumns", required = false, metaVar = "<String>",
      usage = "Comma separated inverted index columns to be created (non-batch load).")
  private String _invertedIndexColumns;

  @Option(name = "-help", required = false, help = true, aliases = {"-h", "--h", "--help"},
      usage = "Print this message.")
  private boolean _help = false;

  @Override
  public boolean getHelp() {
    return _help;
  }

  @Override
  public String getName() {
    return getClass().getSimpleName();
  }

  @Override
  public String description() {
    return "Start Pinot cluster with optional preloaded segments";
  }

  @Override
  public boolean execute()
      throws Exception {
    if (_mode.equalsIgnoreCase("startAll") || _mode.equalsIgnoreCase("startAllButServer")) {
      startAllButServer();
    }
    if (_mode.equalsIgnoreCase("startAll") || _mode.equalsIgnoreCase("startServerWithPreLoadedSegments")) {
      startServerWithPreLoadedSegments();
    }
    return true;
  }

  public static void startAllButServer()
      throws Exception {
    PerfBenchmarkDriverConf perfBenchmarkDriverConf = new PerfBenchmarkDriverConf();
    perfBenchmarkDriverConf.setStartServer(false);
    PerfBenchmarkDriver driver = new PerfBenchmarkDriver(perfBenchmarkDriverConf);
    driver.run();
  }

  private void startServerWithPreLoadedSegments()
      throws Exception {
    PerfBenchmarkDriverConf perfBenchmarkDriverConf = new PerfBenchmarkDriverConf();
    perfBenchmarkDriverConf.setStartZookeeper(false);
    perfBenchmarkDriverConf.setStartController(false);
    perfBenchmarkDriverConf.setStartBroker(false);
    perfBenchmarkDriverConf.setServerInstanceDataDir(_dataDir);
    final PerfBenchmarkDriver driver =
        new PerfBenchmarkDriver(perfBenchmarkDriverConf, _tempDir, _loadMode, _segmentFormatVersion, false);
    driver.run();

    if (_isBatchLoad) {
      String[] tableNames = new File(_dataDir).list();
      Preconditions.checkNotNull(tableNames);
      ExecutorService executorService = Executors.newFixedThreadPool(_numThreads);
      for (final String tableName : tableNames) {
        executorService.submit(new Runnable() {
          @Override
          public void run() {
            try {
              loadTable(driver, _dataDir, tableName, null);
            } catch (Exception e) {
              LOGGER.error("Caught exception while loading table: {}", tableName, e);
            }
          }
        });
      }
      executorService.shutdown();
      executorService.awaitTermination(_timeoutInSeconds, TimeUnit.SECONDS);
    } else {
      List<String> invertedIndexColumns = null;
      if (_invertedIndexColumns != null) {
        invertedIndexColumns = Arrays.asList(_invertedIndexColumns.split(","));
      }
      for (String tableName : _tableNames.split(",")) {
        loadTable(driver, _dataDir, tableName, invertedIndexColumns);
      }
    }
  }

  public static void loadTable(PerfBenchmarkDriver driver, String dataDir, String tableName,
      List<String> invertedIndexColumns)
      throws Exception {
    boolean tableConfigured = false;
    File[] segments = new File(dataDir, tableName).listFiles();
    Preconditions.checkNotNull(segments);
    for (File segment : segments) {
      SegmentMetadataImpl segmentMetadata = new SegmentMetadataImpl(segment);
      if (!tableConfigured) {
        driver.configureTable(segmentMetadata.getTableName(), invertedIndexColumns);
        tableConfigured = true;
      }
      driver.addSegment(segmentMetadata);
    }
  }
}

@@ -3907,31 +3907,24 @@
 %7D%0A%0A  public 
-static 
 void startAl
@@ -4129,16 +4129,24 @@
 driver =
+%0A       
  new Per
@@ -4185,16 +4185,67 @@
 iverConf
+, _tempDir, _loadMode, _segmentFormatVersion, false
 );%0A    d

Fix non-server components do not pick up temp directory (#824)