From 55a3f2b5725727027a1978b07f605594a3ee23be Mon Sep 17 00:00:00 2001
From: skakker
Date: Wed, 10 Jan 2018 11:31:11 +0530
Subject: [PATCH 01/15] Spark Executor Spill Heuristic - (Depends on Custom SHS
- Requires totalMemoryBytesSpilled metric) (#310)
---
app-conf/HeuristicConf.xml | 8 +-
.../fetchers/statusapiv1/statusapiv1.scala | 6 +-
.../ExecutorStorageSpillHeuristic.scala | 133 +++++++++++++++++
.../legacydata/LegacyDataConverters.scala | 1 +
.../spark/legacydata/SparkExecutorData.java | 3 +-
.../drelephant/util/MemoryFormatUtils.java | 3 +
...lpExecutorStorageSpillHeuristic.scala.html | 23 +++
.../spark/SparkMetricsAggregatorTest.scala | 1 +
.../heuristics/ExecutorGcHeuristicTest.scala | 3 +-
.../ExecutorStorageSpillHeuristicTest.scala | 136 ++++++++++++++++++
.../heuristics/ExecutorsHeuristicTest.scala | 1 +
11 files changed, 313 insertions(+), 5 deletions(-)
create mode 100644 app/com/linkedin/drelephant/spark/heuristics/ExecutorStorageSpillHeuristic.scala
create mode 100644 app/views/help/spark/helpExecutorStorageSpillHeuristic.scala.html
create mode 100644 test/com/linkedin/drelephant/spark/heuristics/ExecutorStorageSpillHeuristicTest.scala
diff --git a/app-conf/HeuristicConf.xml b/app-conf/HeuristicConf.xml
index d4fc7b9c1..3a8d833d4 100644
--- a/app-conf/HeuristicConf.xml
+++ b/app-conf/HeuristicConf.xml
@@ -316,5 +316,11 @@
com.linkedin.drelephant.spark.heuristics.ExecutorGcHeuristic
views.html.help.spark.helpExecutorGcHeuristic
-
+
+ spark
+ Executor spill
+ com.linkedin.drelephant.spark.heuristics.ExecutorStorageSpillHeuristic
+ views.html.help.spark.helpExecutorStorageSpillHeuristic
+
+
diff --git a/app/com/linkedin/drelephant/spark/fetchers/statusapiv1/statusapiv1.scala b/app/com/linkedin/drelephant/spark/fetchers/statusapiv1/statusapiv1.scala
index 41d88e1b8..ea3d29dbc 100644
--- a/app/com/linkedin/drelephant/spark/fetchers/statusapiv1/statusapiv1.scala
+++ b/app/com/linkedin/drelephant/spark/fetchers/statusapiv1/statusapiv1.scala
@@ -87,6 +87,7 @@ trait ExecutorSummary{
def totalShuffleWrite: Long
def maxMemory: Long
def totalGCTime: Long
+ def totalMemoryBytesSpilled: Long
def executorLogs: Map[String, String]}
trait JobData{
@@ -160,7 +161,7 @@ trait StageData{
def schedulingPool: String
def accumulatorUpdates: Seq[AccumulableInfo]
- def tasks: Option[Map[Long, TaskData]]
+ def tasks: Option[Map[Long, TaskDataImpl]]
def executorSummary: Option[Map[String, ExecutorStageSummary]]}
trait TaskData{
@@ -293,6 +294,7 @@ class ExecutorSummaryImpl(
var totalShuffleWrite: Long,
var maxMemory: Long,
var totalGCTime: Long,
+ var totalMemoryBytesSpilled: Long,
var executorLogs: Map[String, String]) extends ExecutorSummary
class JobDataImpl(
@@ -366,7 +368,7 @@ class StageDataImpl(
var schedulingPool: String,
var accumulatorUpdates: Seq[AccumulableInfoImpl],
- var tasks: Option[Map[Long, TaskData]],
+ var tasks: Option[Map[Long, TaskDataImpl]],
var executorSummary: Option[Map[String, ExecutorStageSummaryImpl]]) extends StageData
class TaskDataImpl(
diff --git a/app/com/linkedin/drelephant/spark/heuristics/ExecutorStorageSpillHeuristic.scala b/app/com/linkedin/drelephant/spark/heuristics/ExecutorStorageSpillHeuristic.scala
new file mode 100644
index 000000000..a625b441e
--- /dev/null
+++ b/app/com/linkedin/drelephant/spark/heuristics/ExecutorStorageSpillHeuristic.scala
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2016 LinkedIn Corp.
+ *
+ * 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.drelephant.spark.heuristics
+
+import com.linkedin.drelephant.analysis.Severity
+import com.linkedin.drelephant.spark.fetchers.statusapiv1.{ExecutorStageSummary, ExecutorSummary, StageData}
+import com.linkedin.drelephant.analysis._
+import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData
+import com.linkedin.drelephant.spark.data.SparkApplicationData
+import com.linkedin.drelephant.util.MemoryFormatUtils
+
+import scala.collection.JavaConverters
+
+
+/**
+ * A heuristic based on memory spilled.
+ *
+ */
+class ExecutorStorageSpillHeuristic(private val heuristicConfigurationData: HeuristicConfigurationData)
+ extends Heuristic[SparkApplicationData] {
+
+ import ExecutorStorageSpillHeuristic._
+ import JavaConverters._
+
+ val spillFractionOfExecutorsThreshold: Double =
+ if(heuristicConfigurationData.getParamMap.get(SPILL_FRACTION_OF_EXECUTORS_THRESHOLD_KEY) == null) DEFAULT_SPILL_FRACTION_OF_EXECUTORS_THRESHOLD
+ else heuristicConfigurationData.getParamMap.get(SPILL_FRACTION_OF_EXECUTORS_THRESHOLD_KEY).toDouble
+
+ val spillMaxMemoryThreshold: Double =
+ if(heuristicConfigurationData.getParamMap.get(SPILL_MAX_MEMORY_THRESHOLD_KEY) == null) DEFAULT_SPILL_MAX_MEMORY_THRESHOLD
+ else heuristicConfigurationData.getParamMap.get(SPILL_MAX_MEMORY_THRESHOLD_KEY).toDouble
+
+ val sparkExecutorCoresThreshold : Int =
+ if(heuristicConfigurationData.getParamMap.get(SPARK_EXECUTOR_CORES_THRESHOLD_KEY) == null) DEFAULT_SPARK_EXECUTOR_CORES_THRESHOLD
+ else heuristicConfigurationData.getParamMap.get(SPARK_EXECUTOR_CORES_THRESHOLD_KEY).toInt
+
+ val sparkExecutorMemoryThreshold : String =
+ if(heuristicConfigurationData.getParamMap.get(SPARK_EXECUTOR_MEMORY_THRESHOLD_KEY) == null) DEFAULT_SPARK_EXECUTOR_MEMORY_THRESHOLD
+ else heuristicConfigurationData.getParamMap.get(SPARK_EXECUTOR_MEMORY_THRESHOLD_KEY)
+
+ override def getHeuristicConfData(): HeuristicConfigurationData = heuristicConfigurationData
+
+ override def apply(data: SparkApplicationData): HeuristicResult = {
+ val evaluator = new Evaluator(this, data)
+ var resultDetails = Seq(
+ new HeuristicResultDetails("Total memory spilled", MemoryFormatUtils.bytesToString(evaluator.totalMemorySpilled)),
+ new HeuristicResultDetails("Max memory spilled", MemoryFormatUtils.bytesToString(evaluator.maxMemorySpilled)),
+ new HeuristicResultDetails("Mean memory spilled", MemoryFormatUtils.bytesToString(evaluator.meanMemorySpilled)),
+ new HeuristicResultDetails("Fraction of executors having non zero bytes spilled", evaluator.fractionOfExecutorsHavingBytesSpilled.toString)
+ )
+
+ if(evaluator.severity != Severity.NONE){
+ resultDetails :+ new HeuristicResultDetails("Note", "Your execution memory is being spilled. Kindly look into it.")
+ if(evaluator.sparkExecutorCores >= sparkExecutorCoresThreshold && evaluator.sparkExecutorMemory >= MemoryFormatUtils.stringToBytes(sparkExecutorMemoryThreshold)) {
+ resultDetails :+ new HeuristicResultDetails("Recommendation", "You can try decreasing the number of cores to reduce the number of concurrently running tasks.")
+ } else if (evaluator.sparkExecutorMemory <= MemoryFormatUtils.stringToBytes(sparkExecutorMemoryThreshold)) {
+ resultDetails :+ new HeuristicResultDetails("Recommendation", "You can try increasing the executor memory to reduce spill.")
+ }
+ }
+
+ val result = new HeuristicResult(
+ heuristicConfigurationData.getClassName,
+ heuristicConfigurationData.getHeuristicName,
+ evaluator.severity,
+ 0,
+ resultDetails.asJava
+ )
+ result
+ }
+}
+
+object ExecutorStorageSpillHeuristic {
+ val SPARK_EXECUTOR_MEMORY = "spark.executor.memory"
+ val SPARK_EXECUTOR_CORES = "spark.executor.cores"
+ val SPILL_FRACTION_OF_EXECUTORS_THRESHOLD_KEY = "spill_fraction_of_executors_threshold"
+ val SPILL_MAX_MEMORY_THRESHOLD_KEY = "spill_max_memory_threshold"
+ val SPARK_EXECUTOR_CORES_THRESHOLD_KEY = "spark_executor_cores_threshold"
+ val SPARK_EXECUTOR_MEMORY_THRESHOLD_KEY = "spark_executor_memory_threshold"
+ val DEFAULT_SPILL_FRACTION_OF_EXECUTORS_THRESHOLD : Double = 0.2
+ val DEFAULT_SPILL_MAX_MEMORY_THRESHOLD : Double = 0.05
+ val DEFAULT_SPARK_EXECUTOR_CORES_THRESHOLD : Int = 4
+ val DEFAULT_SPARK_EXECUTOR_MEMORY_THRESHOLD : String ="10GB"
+
+ class Evaluator(executorStorageSpillHeuristic: ExecutorStorageSpillHeuristic, data: SparkApplicationData) {
+ lazy val executorSummaries: Seq[ExecutorSummary] = data.executorSummaries
+ lazy val appConfigurationProperties: Map[String, String] =
+ data.appConfigurationProperties
+ val maxMemorySpilled: Long = executorSummaries.map(_.totalMemoryBytesSpilled).max
+ val meanMemorySpilled = executorSummaries.map(_.totalMemoryBytesSpilled).sum / executorSummaries.size
+ val totalMemorySpilled = executorSummaries.map(_.totalMemoryBytesSpilled).sum
+ val fractionOfExecutorsHavingBytesSpilled: Double = executorSummaries.count(_.totalMemoryBytesSpilled > 0).toDouble / executorSummaries.size.toDouble
+ val severity: Severity = {
+ if (fractionOfExecutorsHavingBytesSpilled != 0) {
+ if (fractionOfExecutorsHavingBytesSpilled < executorStorageSpillHeuristic.spillFractionOfExecutorsThreshold
+ && maxMemorySpilled < executorStorageSpillHeuristic.spillMaxMemoryThreshold * sparkExecutorMemory) {
+ Severity.LOW
+ }
+ else if (fractionOfExecutorsHavingBytesSpilled < executorStorageSpillHeuristic.spillFractionOfExecutorsThreshold
+ && meanMemorySpilled < executorStorageSpillHeuristic.spillMaxMemoryThreshold * sparkExecutorMemory) {
+ Severity.MODERATE
+ }
+
+ else if (fractionOfExecutorsHavingBytesSpilled >= executorStorageSpillHeuristic.spillFractionOfExecutorsThreshold
+ && meanMemorySpilled < executorStorageSpillHeuristic.spillMaxMemoryThreshold * sparkExecutorMemory) {
+ Severity.SEVERE
+ }
+ else if (fractionOfExecutorsHavingBytesSpilled >= executorStorageSpillHeuristic.spillFractionOfExecutorsThreshold
+ && meanMemorySpilled >= executorStorageSpillHeuristic.spillMaxMemoryThreshold * sparkExecutorMemory) {
+ Severity.CRITICAL
+ } else Severity.NONE
+ }
+ else Severity.NONE
+ }
+
+ lazy val sparkExecutorMemory: Long = (appConfigurationProperties.get(SPARK_EXECUTOR_MEMORY).map(MemoryFormatUtils.stringToBytes)).getOrElse(0)
+ lazy val sparkExecutorCores: Int = (appConfigurationProperties.get(SPARK_EXECUTOR_CORES).map(_.toInt)).getOrElse(0)
+ }
+}
+
diff --git a/app/com/linkedin/drelephant/spark/legacydata/LegacyDataConverters.scala b/app/com/linkedin/drelephant/spark/legacydata/LegacyDataConverters.scala
index 62a58695b..f9cc59394 100644
--- a/app/com/linkedin/drelephant/spark/legacydata/LegacyDataConverters.scala
+++ b/app/com/linkedin/drelephant/spark/legacydata/LegacyDataConverters.scala
@@ -174,6 +174,7 @@ object LegacyDataConverters {
executorInfo.shuffleWrite,
executorInfo.maxMem,
executorInfo.totalGCTime,
+ executorInfo.totalMemoryBytesSpilled,
executorLogs = Map.empty
)
}
diff --git a/app/com/linkedin/drelephant/spark/legacydata/SparkExecutorData.java b/app/com/linkedin/drelephant/spark/legacydata/SparkExecutorData.java
index 4e2ad4de3..e107a5c4d 100644
--- a/app/com/linkedin/drelephant/spark/legacydata/SparkExecutorData.java
+++ b/app/com/linkedin/drelephant/spark/legacydata/SparkExecutorData.java
@@ -44,6 +44,7 @@ public static class ExecutorInfo {
public long outputBytes = 0L;
public long shuffleRead = 0L;
public long totalGCTime = 0L;
+ public long totalMemoryBytesSpilled = 0L;
public long shuffleWrite = 0L;
public String toString() {
@@ -51,7 +52,7 @@ public String toString() {
+ ", maxMem: " + maxMem + ", diskUsed: " + diskUsed + ", totalTasks" + totalTasks + ", tasksActive: "
+ activeTasks + ", tasksComplete: " + completedTasks + ", tasksFailed: " + failedTasks + ", duration: "
+ duration + ", inputBytes: " + inputBytes + ", outputBytes:" + outputBytes + ", shuffleRead: " + shuffleRead
- + ", shuffleWrite: " + shuffleWrite + ", totalGCTime: " + totalGCTime + "}";
+ + ", shuffleWrite: " + shuffleWrite + ", totalGCTime: " + totalGCTime + ", totalMemoryBytesSpilled: " + totalMemoryBytesSpilled + "}";
}
}
diff --git a/app/com/linkedin/drelephant/util/MemoryFormatUtils.java b/app/com/linkedin/drelephant/util/MemoryFormatUtils.java
index b32f61fb9..8ed49fbc0 100644
--- a/app/com/linkedin/drelephant/util/MemoryFormatUtils.java
+++ b/app/com/linkedin/drelephant/util/MemoryFormatUtils.java
@@ -94,6 +94,9 @@ public static long stringToBytes(String formattedString) {
return 0L;
}
+ //handling if the string has , for eg. 1,000MB
+ formattedString = formattedString.replace(",", "");
+
Matcher matcher = REGEX_MATCHER.matcher(formattedString);
if (!matcher.matches()) {
throw new IllegalArgumentException(
diff --git a/app/views/help/spark/helpExecutorStorageSpillHeuristic.scala.html b/app/views/help/spark/helpExecutorStorageSpillHeuristic.scala.html
new file mode 100644
index 000000000..a23efb735
--- /dev/null
+++ b/app/views/help/spark/helpExecutorStorageSpillHeuristic.scala.html
@@ -0,0 +1,23 @@
+@*
+* Copyright 2016 LinkedIn Corp.
+*
+* 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.
+*@
+Spark performs best when data is kept in memory. Spilled execution memory is tracked by memoryBytesSpilled, which is available executor level. If execution memory is being spilled, then the warnings are as follows:
+Low: memoryBytesSpilled is non-zero for 1 or more executors, greater than zero for < 20% of executors, and max size is < .05 * spark.executor.memory.
+Moderate: memoryBytesSpilled is non-zero for 1 or more executors, greater than zero for < 20% of executors, and avg size is < .05 * spark.executor.memory.
+Severe: memoryBytes Spilled is greater than zero for > 20% of executors and avg size is < .05 * spark.executor.memory.
+Critical: memoryBytes Spilled is greater than zero for > 20% of executors and/or avg size is >= .05 * spark.executor.memory.
+Suggestions
+If number of cores (spark.executor.cores) is more than 4 and executor memory is > 10GB : Try decreasing the number of cores which would decrese the number of tasks running in parallel, hence decreasing the number of bytes spilled.
+You can also try increasing the spark.executor.memory which will reduce memory spilled.
diff --git a/test/com/linkedin/drelephant/spark/SparkMetricsAggregatorTest.scala b/test/com/linkedin/drelephant/spark/SparkMetricsAggregatorTest.scala
index 77e3e1d29..66605dd3e 100644
--- a/test/com/linkedin/drelephant/spark/SparkMetricsAggregatorTest.scala
+++ b/test/com/linkedin/drelephant/spark/SparkMetricsAggregatorTest.scala
@@ -195,6 +195,7 @@ object SparkMetricsAggregatorTest {
totalShuffleWrite = 0,
maxMemory = 0,
totalGCTime = 0,
+ totalMemoryBytesSpilled = 0,
executorLogs = Map.empty
)
}
diff --git a/test/com/linkedin/drelephant/spark/heuristics/ExecutorGcHeuristicTest.scala b/test/com/linkedin/drelephant/spark/heuristics/ExecutorGcHeuristicTest.scala
index 869b9cb67..d9a8fc69f 100644
--- a/test/com/linkedin/drelephant/spark/heuristics/ExecutorGcHeuristicTest.scala
+++ b/test/com/linkedin/drelephant/spark/heuristics/ExecutorGcHeuristicTest.scala
@@ -117,8 +117,9 @@ object ExecutorGcHeuristicTest {
totalInputBytes=0,
totalShuffleRead=0,
totalShuffleWrite= 0,
- maxMemory= 0,
+ maxMemory = 0,
totalGCTime,
+ totalMemoryBytesSpilled = 0,
executorLogs = Map.empty
)
diff --git a/test/com/linkedin/drelephant/spark/heuristics/ExecutorStorageSpillHeuristicTest.scala b/test/com/linkedin/drelephant/spark/heuristics/ExecutorStorageSpillHeuristicTest.scala
new file mode 100644
index 000000000..2a061e8d3
--- /dev/null
+++ b/test/com/linkedin/drelephant/spark/heuristics/ExecutorStorageSpillHeuristicTest.scala
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2016 LinkedIn Corp.
+ *
+ * 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.drelephant.spark.heuristics
+
+import scala.collection.JavaConverters
+import com.linkedin.drelephant.analysis.{ApplicationType, Severity, SeverityThresholds}
+import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData
+import com.linkedin.drelephant.spark.data.{SparkApplicationData, SparkLogDerivedData, SparkRestDerivedData}
+import com.linkedin.drelephant.spark.fetchers.statusapiv1.{ApplicationInfoImpl, ExecutorSummaryImpl, StageDataImpl}
+import org.apache.spark.scheduler.SparkListenerEnvironmentUpdate
+import org.scalatest.{FunSpec, Matchers}
+
+
+class ExecutorStorageSpillHeuristicTest extends FunSpec with Matchers {
+ import ExecutorStorageSpillHeuristicTest._
+
+ describe("ExecutorStorageSpillHeuristic") {
+ val heuristicConfigurationData = newFakeHeuristicConfigurationData(
+ Map.empty
+ )
+ val executorStorageSpillHeuristic = new ExecutorStorageSpillHeuristic(heuristicConfigurationData)
+
+ val appConfigurationProperties = Map("spark.executor.memory" -> "4g", "spark.executor.cores"->"4", "spark.executor.instances"->"4")
+
+ val executorSummaries = Seq(
+ newFakeExecutorSummary(
+ id = "1",
+ totalMemoryBytesSpilled = 200000L
+ ),
+ newFakeExecutorSummary(
+ id = "2",
+ totalMemoryBytesSpilled = 100000L
+ ),
+ newFakeExecutorSummary(
+ id = "3",
+ totalMemoryBytesSpilled = 300000L
+ ),
+ newFakeExecutorSummary(
+ id = "4",
+ totalMemoryBytesSpilled = 200000L
+ )
+ )
+
+ describe(".apply") {
+ val data1 = newFakeSparkApplicationData(executorSummaries, appConfigurationProperties)
+ val heuristicResult = executorStorageSpillHeuristic.apply(data1)
+ val heuristicResultDetails = heuristicResult.getHeuristicResultDetails
+
+ it("returns the severity") {
+ heuristicResult.getSeverity should be(Severity.SEVERE)
+ }
+
+ it("returns the total memory spilled") {
+ val details = heuristicResultDetails.get(0)
+ details.getName should include("Total memory spilled")
+ details.getValue should be("781.25 KB")
+ }
+
+ it("returns the max memory spilled") {
+ val details = heuristicResultDetails.get(1)
+ details.getName should include("Max memory spilled")
+ details.getValue should be("292.97 KB")
+ }
+
+ it("returns the mean memory spilled") {
+ val details = heuristicResultDetails.get(2)
+ details.getName should include("Mean memory spilled")
+ details.getValue should be("195.31 KB")
+ }
+ }
+ }
+}
+
+object ExecutorStorageSpillHeuristicTest {
+ import JavaConverters._
+
+ def newFakeHeuristicConfigurationData(params: Map[String, String] = Map.empty): HeuristicConfigurationData =
+ new HeuristicConfigurationData("heuristic", "class", "view", new ApplicationType("type"), params.asJava)
+
+ def newFakeExecutorSummary(
+ id: String,
+ totalMemoryBytesSpilled: Long
+ ): ExecutorSummaryImpl = new ExecutorSummaryImpl(
+ id,
+ hostPort = "",
+ rddBlocks = 0,
+ memoryUsed=0,
+ diskUsed = 0,
+ activeTasks = 0,
+ failedTasks = 0,
+ completedTasks = 0,
+ totalTasks = 0,
+ totalDuration=0,
+ totalInputBytes=0,
+ totalShuffleRead=0,
+ totalShuffleWrite= 0,
+ maxMemory= 0,
+ totalGCTime = 0,
+ totalMemoryBytesSpilled,
+ executorLogs = Map.empty
+ )
+
+ def newFakeSparkApplicationData(
+ executorSummaries: Seq[ExecutorSummaryImpl],
+ appConfigurationProperties: Map[String, String]
+ ): SparkApplicationData = {
+ val appId = "application_1"
+
+ val restDerivedData = SparkRestDerivedData(
+ new ApplicationInfoImpl(appId, name = "app", Seq.empty),
+ jobDatas = Seq.empty,
+ stageDatas = Seq.empty,
+ executorSummaries = executorSummaries
+ )
+
+ val logDerivedData = SparkLogDerivedData(
+ SparkListenerEnvironmentUpdate(Map("Spark Properties" -> appConfigurationProperties.toSeq))
+ )
+
+ SparkApplicationData(appId, restDerivedData, Some(logDerivedData))
+ }
+}
diff --git a/test/com/linkedin/drelephant/spark/heuristics/ExecutorsHeuristicTest.scala b/test/com/linkedin/drelephant/spark/heuristics/ExecutorsHeuristicTest.scala
index 7dbeea921..b4095ccf9 100644
--- a/test/com/linkedin/drelephant/spark/heuristics/ExecutorsHeuristicTest.scala
+++ b/test/com/linkedin/drelephant/spark/heuristics/ExecutorsHeuristicTest.scala
@@ -250,6 +250,7 @@ object ExecutorsHeuristicTest {
totalShuffleWrite,
maxMemory,
totalGCTime = 0,
+ totalMemoryBytesSpilled = 0,
executorLogs = Map.empty
)
From eec5ff7636ea3daa287732ece4c7c4f887afb745 Mon Sep 17 00:00:00 2001
From: skakker
Date: Wed, 10 Jan 2018 11:37:14 +0530
Subject: [PATCH 02/15] Spark Stages with Failed tasks Heuristic - (Depends on
Custom SHS - Requires stages/failedTasks Rest API) (#288)
---
app-conf/FetcherConf.xml | 5 +-
app-conf/HeuristicConf.xml | 6 +
.../spark/data/SparkApplicationData.scala | 6 +-
.../spark/data/SparkRestDerivedData.scala | 1 +
.../spark/fetchers/SparkFetcher.scala | 1 +
.../spark/fetchers/SparkRestClient.scala | 14 +-
.../StagesWithFailedTasksHeuristic.scala | 146 +++++++++++++++
.../legacydata/LegacyDataConverters.scala | 32 +++-
.../helpStagesWithFailedTasks.scala.html | 22 +++
.../spark/SparkMetricsAggregatorTest.scala | 6 +-
.../spark/data/SparkApplicationDataTest.scala | 3 +-
.../spark/fetchers/SparkFetcherTest.scala | 3 +-
.../spark/fetchers/SparkRestClientTest.scala | 23 ++-
.../ConfigurationHeuristicTest.scala | 3 +-
.../heuristics/ExecutorGcHeuristicTest.scala | 3 +-
.../heuristics/ExecutorsHeuristicTest.scala | 3 +-
.../spark/heuristics/JobsHeuristicTest.scala | 3 +-
.../heuristics/StagesHeuristicTest.scala | 3 +-
.../StagesWithFailedTasksHeuristicTest.scala | 166 ++++++++++++++++++
.../drelephant/util/InfoExtractorTest.java | 6 +-
20 files changed, 437 insertions(+), 18 deletions(-)
create mode 100644 app/com/linkedin/drelephant/spark/heuristics/StagesWithFailedTasksHeuristic.scala
create mode 100644 app/views/help/spark/helpStagesWithFailedTasks.scala.html
create mode 100644 test/com/linkedin/drelephant/spark/heuristics/StagesWithFailedTasksHeuristicTest.scala
diff --git a/app-conf/FetcherConf.xml b/app-conf/FetcherConf.xml
index 5448c9f40..dc78a64eb 100644
--- a/app-conf/FetcherConf.xml
+++ b/app-conf/FetcherConf.xml
@@ -88,7 +88,10 @@
-->
spark
- com.linkedin.drelephant.spark.fetchers.FSFetcher
+ com.linkedin.drelephant.spark.fetchers.SparkFetcher
+
+ true
+
-
+
mapreduce
com.linkedin.drelephant.mapreduce.fetchers.MapReduceFSFetcherHadoop2
@@ -68,7 +68,7 @@
PST
-
+
+
spark
Spark Job Metrics
com.linkedin.drelephant.spark.heuristics.JobsHeuristic
views.html.help.spark.helpJobsHeuristic
+
spark
Spark Stage Metrics
com.linkedin.drelephant.spark.heuristics.StagesHeuristic
views.html.help.spark.helpStagesHeuristic
+
spark
- Peak Unified Memory
+ Executor Peak Unified Memory
com.linkedin.drelephant.spark.heuristics.UnifiedMemoryHeuristic
views.html.help.spark.helpUnifiedMemoryHeuristic
+
+
spark
- JVM Used Memory
+ Executor JVM Used Memory
com.linkedin.drelephant.spark.heuristics.JvmUsedMemoryHeuristic
views.html.help.spark.helpJvmUsedMemoryHeuristic
+
+
spark
Stages with failed tasks
com.linkedin.drelephant.spark.heuristics.StagesWithFailedTasksHeuristic
views.html.help.spark.helpStagesWithFailedTasks
+
spark
Executor GC
com.linkedin.drelephant.spark.heuristics.ExecutorGcHeuristic
views.html.help.spark.helpExecutorGcHeuristic
+
+
spark
Executor spill
com.linkedin.drelephant.spark.heuristics.ExecutorStorageSpillHeuristic
views.html.help.spark.helpExecutorStorageSpillHeuristic
+
+
+
+
+ spark
+ Driver Metrics
+ com.linkedin.drelephant.spark.heuristics.DriverHeuristic
+ views.html.help.spark.helpDriverHeuristic
+
diff --git a/app/com/linkedin/drelephant/spark/fetchers/SparkRestClient.scala b/app/com/linkedin/drelephant/spark/fetchers/SparkRestClient.scala
index 42c5ae668..21c4fb1a3 100644
--- a/app/com/linkedin/drelephant/spark/fetchers/SparkRestClient.scala
+++ b/app/com/linkedin/drelephant/spark/fetchers/SparkRestClient.scala
@@ -223,7 +223,7 @@ class SparkRestClient(sparkConf: SparkConf) {
}
private def getExecutorSummaries(attemptTarget: WebTarget): Seq[ExecutorSummaryImpl] = {
- val target = attemptTarget.path("executors")
+ val target = attemptTarget.path("allexecutors")
try {
get(target, SparkRestObjectMapper.readValue[Seq[ExecutorSummaryImpl]])
} catch {
diff --git a/app/com/linkedin/drelephant/spark/heuristics/ConfigurationHeuristic.scala b/app/com/linkedin/drelephant/spark/heuristics/ConfigurationHeuristic.scala
index 0c0193ef5..5af1ed94f 100644
--- a/app/com/linkedin/drelephant/spark/heuristics/ConfigurationHeuristic.scala
+++ b/app/com/linkedin/drelephant/spark/heuristics/ConfigurationHeuristic.scala
@@ -29,7 +29,7 @@ import com.linkedin.drelephant.math.Statistics
* A heuristic based on an app's known configuration.
*
* The results from this heuristic primarily inform users about key app configuration settings, including
- * driver memory, driver cores, executor cores, executor instances, executor memory, and the serializer.
+ * executor cores, executor instances, executor memory, and the serializer.
*
* It also checks whether the values specified are within threshold.
*/
@@ -55,10 +55,6 @@ class ConfigurationHeuristic(private val heuristicConfigurationData: HeuristicCo
property.getOrElse("Not presented. Using default.")
val resultDetails = Seq(
- new HeuristicResultDetails(
- SPARK_DRIVER_MEMORY_KEY,
- formatProperty(evaluator.driverMemoryBytes.map(MemoryFormatUtils.bytesToString))
- ),
new HeuristicResultDetails(
SPARK_EXECUTOR_MEMORY_KEY,
formatProperty(evaluator.executorMemoryBytes.map(MemoryFormatUtils.bytesToString))
@@ -80,16 +76,16 @@ class ConfigurationHeuristic(private val heuristicConfigurationData: HeuristicCo
formatProperty(evaluator.isDynamicAllocationEnabled.map(_.toString))
),
new HeuristicResultDetails(
- SPARK_DRIVER_CORES_KEY,
- formatProperty(evaluator.driverCores.map(_.toString))
+ SPARK_YARN_EXECUTOR_MEMORY_OVERHEAD,
+ evaluator.sparkYarnExecutorMemoryOverhead
),
new HeuristicResultDetails(
- SPARK_YARN_DRIVER_MEMORY_OVERHEAD,
- evaluator.sparkYarnDriverMemoryOverhead
+ SPARK_DYNAMIC_ALLOCATION_MIN_EXECUTORS,
+ evaluator.dynamicMinExecutors.getOrElse(0).toString
),
new HeuristicResultDetails(
- SPARK_YARN_EXECUTOR_MEMORY_OVERHEAD,
- evaluator.sparkYarnExecutorMemoryOverhead
+ SPARK_DYNAMIC_ALLOCATION_MAX_EXECUTORS,
+ evaluator.dynamicMaxExecutors.getOrElse(0).toString
)
)
// Constructing a mutable ArrayList for resultDetails, otherwise addResultDetail method HeuristicResult cannot be used.
@@ -109,21 +105,24 @@ class ConfigurationHeuristic(private val heuristicConfigurationData: HeuristicCo
result.addResultDetail(SPARK_SHUFFLE_SERVICE_ENABLED, formatProperty(evaluator.isShuffleServiceEnabled.map(_.toString)),
"Spark shuffle service is not enabled.")
}
- if (evaluator.severityMinExecutors == Severity.CRITICAL) {
- result.addResultDetail("Minimum Executors", "The minimum executors for Dynamic Allocation should be <=1. Please change it in the " + SPARK_DYNAMIC_ALLOCATION_MIN_EXECUTORS + " field.")
+ if (evaluator.severityMinExecutors != Severity.NONE) {
+ result.addResultDetail("Minimum Executors", "The minimum executors for Dynamic Allocation should be "+ THRESHOLD_MIN_EXECUTORS + ". Please change it in the " + SPARK_DYNAMIC_ALLOCATION_MIN_EXECUTORS + " field.")
}
- if (evaluator.severityMaxExecutors == Severity.CRITICAL) {
- result.addResultDetail("Maximum Executors", "The maximum executors for Dynamic Allocation should be <=900. Please change it in the " + SPARK_DYNAMIC_ALLOCATION_MAX_EXECUTORS + " field.")
+ if (evaluator.severityMaxExecutors != Severity.NONE) {
+ result.addResultDetail("Maximum Executors", "The maximum executors for Dynamic Allocation should be <=" + THRESHOLD_MAX_EXECUTORS + ". Please change it in the " + SPARK_DYNAMIC_ALLOCATION_MAX_EXECUTORS + " field.")
}
- if (evaluator.jarsSeverity == Severity.CRITICAL) {
+ if (evaluator.jarsSeverity != Severity.NONE) {
result.addResultDetail("Jars notation", "It is recommended to not use * notation while specifying jars in the field " + SPARK_YARN_JARS)
}
- if(evaluator.severityDriverMemoryOverhead.getValue >= Severity.SEVERE.getValue) {
- result.addResultDetail("Driver Overhead Memory", "Please do not specify excessive amount of overhead memory for Driver. Change it in the field " + SPARK_YARN_DRIVER_MEMORY_OVERHEAD)
- }
- if(evaluator.severityExecutorMemoryOverhead.getValue >= Severity.SEVERE.getValue) {
+ if(evaluator.severityExecutorMemoryOverhead != Severity.NONE) {
result.addResultDetail("Executor Overhead Memory", "Please do not specify excessive amount of overhead memory for Executors. Change it in the field " + SPARK_YARN_EXECUTOR_MEMORY_OVERHEAD)
}
+ if(evaluator.severityExecutorCores != Severity.NONE) {
+ result.addResultDetail("Executor cores", "The number of executor cores should be <=" + evaluator.DEFAULT_SPARK_CORES_THRESHOLDS.low + ". Please change it in the field " + SPARK_EXECUTOR_CORES_KEY)
+ }
+ if(evaluator.severityExecutorMemory != Severity.NONE) {
+ result.addResultDetail("Executor memory", "Please do not specify excessive amount of executor memory. Change it in the field " + SPARK_EXECUTOR_MEMORY_KEY)
+ }
result
}
}
@@ -134,7 +133,6 @@ object ConfigurationHeuristic {
val SERIALIZER_IF_NON_NULL_RECOMMENDATION_KEY = "serializer_if_non_null_recommendation"
- val SPARK_DRIVER_MEMORY_KEY = "spark.driver.memory"
val SPARK_EXECUTOR_MEMORY_KEY = "spark.executor.memory"
val SPARK_EXECUTOR_INSTANCES_KEY = "spark.executor.instances"
val SPARK_EXECUTOR_CORES_KEY = "spark.executor.cores"
@@ -142,12 +140,10 @@ object ConfigurationHeuristic {
val SPARK_APPLICATION_DURATION = "spark.application.duration"
val SPARK_SHUFFLE_SERVICE_ENABLED = "spark.shuffle.service.enabled"
val SPARK_DYNAMIC_ALLOCATION_ENABLED = "spark.dynamicAllocation.enabled"
- val SPARK_DRIVER_CORES_KEY = "spark.driver.cores"
val SPARK_DYNAMIC_ALLOCATION_MIN_EXECUTORS = "spark.dynamicAllocation.minExecutors"
val SPARK_DYNAMIC_ALLOCATION_MAX_EXECUTORS = "spark.dynamicAllocation.maxExecutors"
val SPARK_YARN_JARS = "spark.yarn.secondary.jars"
val SPARK_YARN_EXECUTOR_MEMORY_OVERHEAD = "spark.yarn.executor.memoryOverhead"
- val SPARK_YARN_DRIVER_MEMORY_OVERHEAD = "spark.yarn.driver.memoryOverhead"
val THRESHOLD_MIN_EXECUTORS: Int = 1
val THRESHOLD_MAX_EXECUTORS: Int = 900
val SPARK_OVERHEAD_MEMORY_THRESHOLD_KEY = "spark.overheadMemory.thresholds.key"
@@ -159,9 +155,6 @@ object ConfigurationHeuristic {
lazy val appConfigurationProperties: Map[String, String] =
data.appConfigurationProperties
- lazy val driverMemoryBytes: Option[Long] =
- Try(getProperty(SPARK_DRIVER_MEMORY_KEY).map(MemoryFormatUtils.stringToBytes)).getOrElse(None)
-
lazy val executorMemoryBytes: Option[Long] =
Try(getProperty(SPARK_EXECUTOR_MEMORY_KEY).map(MemoryFormatUtils.stringToBytes)).getOrElse(None)
@@ -171,9 +164,6 @@ object ConfigurationHeuristic {
lazy val executorCores: Option[Int] =
Try(getProperty(SPARK_EXECUTOR_CORES_KEY).map(_.toInt)).getOrElse(None)
- lazy val driverCores: Option[Int] =
- Try(getProperty(SPARK_DRIVER_CORES_KEY).map(_.toInt)).getOrElse(None)
-
lazy val dynamicMinExecutors: Option[Int] =
Try(getProperty(SPARK_DYNAMIC_ALLOCATION_MIN_EXECUTORS).map(_.toInt)).getOrElse(None)
@@ -196,8 +186,6 @@ object ConfigurationHeuristic {
lazy val sparkYarnExecutorMemoryOverhead: String = if (getProperty(SPARK_YARN_EXECUTOR_MEMORY_OVERHEAD).getOrElse("0").matches("(.*)[0-9]"))
MemoryFormatUtils.bytesToString(MemoryFormatUtils.stringToBytes(getProperty(SPARK_YARN_EXECUTOR_MEMORY_OVERHEAD).getOrElse("0") + "MB")) else (getProperty(SPARK_YARN_EXECUTOR_MEMORY_OVERHEAD).getOrElse("0"))
- lazy val sparkYarnDriverMemoryOverhead: String = if (getProperty(SPARK_YARN_DRIVER_MEMORY_OVERHEAD).getOrElse("0").matches("(.*)[0-9]"))
- MemoryFormatUtils.bytesToString(MemoryFormatUtils.stringToBytes(getProperty(SPARK_YARN_DRIVER_MEMORY_OVERHEAD).getOrElse("0") + "MB")) else getProperty(SPARK_YARN_DRIVER_MEMORY_OVERHEAD).getOrElse("0")
lazy val serializer: Option[String] = getProperty(SPARK_SERIALIZER_KEY)
@@ -211,7 +199,7 @@ object ConfigurationHeuristic {
case Some(_) => DEFAULT_SERIALIZER_IF_NON_NULL_SEVERITY_IF_RECOMMENDATION_UNMET
}
- //The following thresholds are for checking if the memory and cores values (executor and driver) are above normal. These thresholds are experimental, and may change in the future.
+ //The following thresholds are for checking if the memory and cores values of executors are above normal. These thresholds are experimental, and may change in the future.
val DEFAULT_SPARK_MEMORY_THRESHOLDS =
SeverityThresholds(low = MemoryFormatUtils.stringToBytes("10G"), MemoryFormatUtils.stringToBytes("15G"),
severe = MemoryFormatUtils.stringToBytes("20G"), critical = MemoryFormatUtils.stringToBytes("25G"), ascending = true)
@@ -219,8 +207,6 @@ object ConfigurationHeuristic {
SeverityThresholds(low = 4, moderate = 6, severe = 8, critical = 10, ascending = true)
val severityExecutorMemory = DEFAULT_SPARK_MEMORY_THRESHOLDS.severityOf(executorMemoryBytes.getOrElse(0).asInstanceOf[Number].longValue)
- val severityDriverMemory = DEFAULT_SPARK_MEMORY_THRESHOLDS.severityOf(driverMemoryBytes.getOrElse(0).asInstanceOf[Number].longValue)
- val severityDriverCores = DEFAULT_SPARK_CORES_THRESHOLDS.severityOf(driverCores.getOrElse(0).asInstanceOf[Number].intValue)
val severityExecutorCores = DEFAULT_SPARK_CORES_THRESHOLDS.severityOf(executorCores.getOrElse(0).asInstanceOf[Number].intValue)
val severityMinExecutors = if (dynamicMinExecutors.getOrElse(0).asInstanceOf[Number].intValue > THRESHOLD_MIN_EXECUTORS) {
Severity.CRITICAL
@@ -233,12 +219,10 @@ object ConfigurationHeuristic {
Severity.NONE
}
val severityExecutorMemoryOverhead = configurationHeuristic.sparkOverheadMemoryThreshold.severityOf(MemoryFormatUtils.stringToBytes(sparkYarnExecutorMemoryOverhead))
- val severityDriverMemoryOverhead = configurationHeuristic.sparkOverheadMemoryThreshold.severityOf(MemoryFormatUtils.stringToBytes(sparkYarnDriverMemoryOverhead))
-
//Severity for the configuration thresholds
- val severityConfThresholds: Severity = Severity.max(severityDriverCores, severityDriverMemory, severityExecutorCores, severityExecutorMemory,
- severityMinExecutors, severityMaxExecutors, jarsSeverity, severityExecutorMemoryOverhead, severityDriverMemoryOverhead)
+ val severityConfThresholds: Severity = Severity.max(severityExecutorCores, severityExecutorMemory,
+ severityMinExecutors, severityMaxExecutors, jarsSeverity, severityExecutorMemoryOverhead)
/**
* The following logic computes severity based on shuffle service and dynamic allocation flags.
diff --git a/app/com/linkedin/drelephant/spark/heuristics/DriverHeuristic.scala b/app/com/linkedin/drelephant/spark/heuristics/DriverHeuristic.scala
new file mode 100644
index 000000000..b52356b07
--- /dev/null
+++ b/app/com/linkedin/drelephant/spark/heuristics/DriverHeuristic.scala
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2016 LinkedIn Corp.
+ *
+ * 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.drelephant.spark.heuristics
+
+import java.util.ArrayList
+
+import scala.collection.JavaConverters
+import scala.util.Try
+import com.linkedin.drelephant.analysis._
+import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData
+import com.linkedin.drelephant.spark.data.SparkApplicationData
+import com.linkedin.drelephant.util.MemoryFormatUtils
+import com.linkedin.drelephant.spark.fetchers.statusapiv1.ExecutorSummary
+
+/**
+ * A heuristic based the driver's configurations and memory used.
+ * It checks whether the configuration values specified are within the threshold range.
+ * It also analyses the peak JVM memory used and time spent in GC by the job.
+ */
+class DriverHeuristic(private val heuristicConfigurationData: HeuristicConfigurationData)
+ extends Heuristic[SparkApplicationData] {
+
+ import DriverHeuristic._
+ import JavaConverters._
+
+ val gcSeverityThresholds: SeverityThresholds =
+ SeverityThresholds.parse(heuristicConfigurationData.getParamMap.get(GC_SEVERITY_THRESHOLDS_KEY), ascending = true)
+ .getOrElse(DEFAULT_GC_SEVERITY_THRESHOLDS)
+
+ val sparkOverheadMemoryThreshold: SeverityThresholds = SeverityThresholds.parse(heuristicConfigurationData.getParamMap.get(SPARK_OVERHEAD_MEMORY_THRESHOLD_KEY), ascending = true)
+ .getOrElse(DEFAULT_SPARK_OVERHEAD_MEMORY_THRESHOLDS)
+
+ val sparkExecutorMemoryThreshold: String = heuristicConfigurationData.getParamMap.getOrDefault(SPARK_EXECUTOR_MEMORY_THRESHOLD_KEY, DEFAULT_SPARK_EXECUTOR_MEMORY_THRESHOLD)
+
+ override def getHeuristicConfData(): HeuristicConfigurationData = heuristicConfigurationData
+
+ lazy val driverPeakJvmMemoryThresholdString: String = heuristicConfigurationData.getParamMap.get(MAX_DRIVER_PEAK_JVM_USED_MEMORY_THRESHOLD_KEY)
+
+ override def apply(data: SparkApplicationData): HeuristicResult = {
+ val evaluator = new Evaluator(this, data)
+
+ def formatProperty(property: Option[String]): String =
+ property.getOrElse("Not presented. Using default.")
+
+ var resultDetails = Seq(
+ new HeuristicResultDetails(
+ SPARK_DRIVER_MEMORY_KEY,
+ formatProperty(evaluator.driverMemoryBytes.map(MemoryFormatUtils.bytesToString))
+ ),
+ new HeuristicResultDetails(
+ "Ratio of time spent in GC to total time", evaluator.ratio.toString
+ ),
+ new HeuristicResultDetails(
+ SPARK_DRIVER_CORES_KEY,
+ formatProperty(evaluator.driverCores.map(_.toString))
+ ),
+ new HeuristicResultDetails(
+ SPARK_YARN_DRIVER_MEMORY_OVERHEAD,
+ evaluator.sparkYarnDriverMemoryOverhead
+ ),
+ new HeuristicResultDetails("Max driver peak JVM used memory", MemoryFormatUtils.bytesToString(evaluator.maxDriverPeakJvmUsedMemory))
+ )
+ if(evaluator.severityJvmUsedMemory != Severity.NONE) {
+ resultDetails = resultDetails :+ new HeuristicResultDetails("Driver Peak JVM used Memory", "The allocated memory for the driver (in " + SPARK_DRIVER_MEMORY_KEY + ") is much more than the peak JVM used memory by the driver.")
+ }
+ if (evaluator.severityGc != Severity.NONE) {
+ resultDetails = resultDetails :+ new HeuristicResultDetails("Gc ratio high", "The driver is spending too much time on GC. We recommend increasing the driver memory.")
+ }
+ if(evaluator.severityDriverCores != Severity.NONE) {
+ resultDetails = resultDetails :+ new HeuristicResultDetails("Driver Cores", "Please do not specify excessive number of driver cores. Change it in the field : " + SPARK_DRIVER_CORES_KEY)
+ }
+ if(evaluator.severityDriverMemoryOverhead != Severity.NONE) {
+ resultDetails = resultDetails :+ new HeuristicResultDetails("Driver Overhead Memory", "Please do not specify excessive amount of overhead memory for Driver. Change it in the field " + SPARK_YARN_DRIVER_MEMORY_OVERHEAD)
+ }
+ if(evaluator.severityDriverMemory != Severity.NONE) {
+ resultDetails = resultDetails :+ new HeuristicResultDetails("Spark Driver Memory", "Please do not specify excessive amount of memory for Driver. Change it in the field " + SPARK_DRIVER_MEMORY_KEY)
+ }
+
+ // Constructing a mutable ArrayList for resultDetails, otherwise addResultDetail method HeuristicResult cannot be used.
+ val mutableResultDetailsArrayList = new ArrayList(resultDetails.asJava)
+ val result = new HeuristicResult(
+ heuristicConfigurationData.getClassName,
+ heuristicConfigurationData.getHeuristicName,
+ evaluator.severity,
+ 0,
+ mutableResultDetailsArrayList
+ )
+ result
+ }
+}
+
+object DriverHeuristic {
+
+ val SPARK_DRIVER_MEMORY_KEY = "spark.driver.memory"
+ val SPARK_DRIVER_CORES_KEY = "spark.driver.cores"
+ val SPARK_YARN_DRIVER_MEMORY_OVERHEAD = "spark.yarn.driver.memoryOverhead"
+ val SPARK_OVERHEAD_MEMORY_THRESHOLD_KEY = "spark.overheadMemory.thresholds.key"
+ val SPARK_EXECUTOR_MEMORY_THRESHOLD_KEY = "spark_executor_memory_threshold_key"
+ val EXECUTION_MEMORY = "executionMemory"
+ val STORAGE_MEMORY = "storageMemory"
+ val JVM_USED_MEMORY = "jvmUsedMemory"
+
+ // 300 * FileUtils.ONE_MB (300 * 1024 * 1024)
+ val reservedMemory : Long = 314572800
+ val MAX_DRIVER_PEAK_JVM_USED_MEMORY_THRESHOLD_KEY = "peak_jvm_memory_threshold"
+ val GC_SEVERITY_THRESHOLDS_KEY: String = "gc_severity_threshold"
+ val DEFAULT_GC_SEVERITY_THRESHOLDS =
+ SeverityThresholds(low = 0.08D, moderate = 0.09D, severe = 0.1D, critical = 0.15D, ascending = true)
+
+ val DEFAULT_SPARK_OVERHEAD_MEMORY_THRESHOLDS =
+ SeverityThresholds(low = MemoryFormatUtils.stringToBytes("2G"), MemoryFormatUtils.stringToBytes("4G"),
+ severe = MemoryFormatUtils.stringToBytes("6G"), critical = MemoryFormatUtils.stringToBytes("8G"), ascending = true)
+
+ val DEFAULT_SPARK_EXECUTOR_MEMORY_THRESHOLD = "2G"
+
+ class Evaluator(driverHeuristic: DriverHeuristic, data: SparkApplicationData) {
+ lazy val appConfigurationProperties: Map[String, String] =
+ data.appConfigurationProperties
+
+ lazy val executorSummaries : Seq[ExecutorSummary] = data.executorSummaries
+ lazy val driver : ExecutorSummary = executorSummaries.find(_.id == "driver").getOrElse(null)
+
+ if(driver == null) {
+ throw new Exception("No driver found!")
+ }
+
+ //peakJvmMemory calculations
+ val maxDriverPeakJvmUsedMemory : Long = driver.peakJvmUsedMemory.getOrElse(JVM_USED_MEMORY, 0L).asInstanceOf[Number].longValue
+
+ lazy val DEFAULT_MAX_DRIVER_PEAK_JVM_USED_MEMORY_THRESHOLDS =
+ SeverityThresholds(low = 1.25 * (maxDriverPeakJvmUsedMemory + reservedMemory), moderate = 1.5 * (maxDriverPeakJvmUsedMemory + reservedMemory),
+ severe = 2 * (maxDriverPeakJvmUsedMemory + reservedMemory), critical = 3 * (maxDriverPeakJvmUsedMemory + reservedMemory), ascending = true)
+
+ val MAX_DRIVER_PEAK_JVM_USED_MEMORY_THRESHOLDS : SeverityThresholds = if(driverHeuristic.driverPeakJvmMemoryThresholdString == null) {
+ DEFAULT_MAX_DRIVER_PEAK_JVM_USED_MEMORY_THRESHOLDS
+ } else {
+ SeverityThresholds.parse(driverHeuristic.driverPeakJvmMemoryThresholdString.split(",").map(_.toDouble * (maxDriverPeakJvmUsedMemory + reservedMemory)).toString, ascending = false).getOrElse(DEFAULT_MAX_DRIVER_PEAK_JVM_USED_MEMORY_THRESHOLDS)
+ }
+
+ lazy val severityJvmUsedMemory : Severity = if (driverMemoryBytes.getOrElse(0L).asInstanceOf[Number].longValue <= MemoryFormatUtils.stringToBytes(driverHeuristic.sparkExecutorMemoryThreshold)) {
+ Severity.NONE
+ } else {
+ MAX_DRIVER_PEAK_JVM_USED_MEMORY_THRESHOLDS.severityOf(driverMemoryBytes.getOrElse(0L).asInstanceOf[Number].longValue)
+ }
+
+ //Gc Calculations
+ val ratio : Double = driver.totalGCTime.toDouble / driver.totalDuration.toDouble
+ val severityGc = driverHeuristic.gcSeverityThresholds.severityOf(ratio)
+
+ lazy val driverMemoryBytes: Option[Long] =
+ Try(getProperty(SPARK_DRIVER_MEMORY_KEY).map(MemoryFormatUtils.stringToBytes)).getOrElse(None)
+
+ lazy val driverCores: Option[Int] =
+ Try(getProperty(SPARK_DRIVER_CORES_KEY).map(_.toInt)).getOrElse(None)
+
+ lazy val sparkYarnDriverMemoryOverhead: String = if (getProperty(SPARK_YARN_DRIVER_MEMORY_OVERHEAD).getOrElse("0").matches("(.*)[0-9]"))
+ MemoryFormatUtils.bytesToString(MemoryFormatUtils.stringToBytes(getProperty(SPARK_YARN_DRIVER_MEMORY_OVERHEAD).getOrElse("0") + "MB")) else getProperty(SPARK_YARN_DRIVER_MEMORY_OVERHEAD).getOrElse("0")
+
+ //The following thresholds are for checking if the memory and cores values (driver) are above normal. These thresholds are experimental, and may change in the future.
+ val DEFAULT_SPARK_MEMORY_THRESHOLDS =
+ SeverityThresholds(low = MemoryFormatUtils.stringToBytes("10G"), MemoryFormatUtils.stringToBytes("15G"),
+ severe = MemoryFormatUtils.stringToBytes("20G"), critical = MemoryFormatUtils.stringToBytes("25G"), ascending = true)
+ val DEFAULT_SPARK_CORES_THRESHOLDS =
+ SeverityThresholds(low = 4, moderate = 6, severe = 8, critical = 10, ascending = true)
+
+ val severityDriverMemory = DEFAULT_SPARK_MEMORY_THRESHOLDS.severityOf(driverMemoryBytes.getOrElse(0).asInstanceOf[Number].longValue)
+ val severityDriverCores = DEFAULT_SPARK_CORES_THRESHOLDS.severityOf(driverCores.getOrElse(0).asInstanceOf[Number].intValue)
+ val severityDriverMemoryOverhead = driverHeuristic.sparkOverheadMemoryThreshold.severityOf(MemoryFormatUtils.stringToBytes(sparkYarnDriverMemoryOverhead))
+
+ //Severity for the configuration thresholds
+ val severityConfThresholds: Severity = Severity.max(severityDriverCores, severityDriverMemory, severityDriverMemoryOverhead)
+ lazy val severity: Severity = Severity.max(severityConfThresholds, severityGc, severityJvmUsedMemory)
+ private def getProperty(key: String): Option[String] = appConfigurationProperties.get(key)
+ }
+
+}
diff --git a/app/com/linkedin/drelephant/spark/heuristics/ExecutorGcHeuristic.scala b/app/com/linkedin/drelephant/spark/heuristics/ExecutorGcHeuristic.scala
index d4ef405dc..7b8929ed7 100644
--- a/app/com/linkedin/drelephant/spark/heuristics/ExecutorGcHeuristic.scala
+++ b/app/com/linkedin/drelephant/spark/heuristics/ExecutorGcHeuristic.scala
@@ -21,6 +21,8 @@ import com.linkedin.drelephant.spark.fetchers.statusapiv1._
import com.linkedin.drelephant.analysis._
import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData
import com.linkedin.drelephant.spark.data.SparkApplicationData
+import com.linkedin.drelephant.math.Statistics
+
import scala.collection.JavaConverters
@@ -47,8 +49,8 @@ class ExecutorGcHeuristic(private val heuristicConfigurationData: HeuristicConfi
val evaluator = new Evaluator(this, data)
var resultDetails = Seq(
new HeuristicResultDetails("GC time to Executor Run time ratio", evaluator.ratio.toString),
- new HeuristicResultDetails("Total GC time", evaluator.jvmTime.toString),
- new HeuristicResultDetails("Total Executor Runtime", evaluator.executorRunTimeTotal.toString)
+ new HeuristicResultDetails("Total GC time", evaluator.msecToString(evaluator.jvmTime)),
+ new HeuristicResultDetails("Total Executor Runtime", evaluator.msecToString(evaluator.executorRunTimeTotal))
)
//adding recommendations to the result, severityTimeA corresponds to the ascending severity calculation
@@ -98,7 +100,7 @@ object ExecutorGcHeuristic {
if (executorSummaries.isEmpty) {
throw new Exception("No executor information available.")
}
-
+
lazy val appConfigurationProperties: Map[String, String] =
data.appConfigurationProperties
var (jvmTime, executorRunTimeTotal) = getTimeValues(executorSummaries)
@@ -122,6 +124,25 @@ object ExecutorGcHeuristic {
})
(jvmGcTimeTotal, executorRunTimeTotal)
}
+
+ //convert millisec to units
+ def msecToString (milliSec: Long): String = {
+ var value : Long = milliSec
+ if(value < Statistics.SECOND_IN_MS){
+ return value.toString + " msec"
+ } else if(value < Statistics.MINUTE_IN_MS) {
+ return (value/Statistics.SECOND_IN_MS).toString + " Seconds"
+ } else if(value < Statistics.HOUR_IN_MS) {
+ return (value/Statistics.MINUTE_IN_MS).toString + " Minutes"
+ }else {
+ var minutes = (value % Statistics.HOUR_IN_MS)/Statistics.MINUTE_IN_MS
+ if(minutes == 0) {
+ return (value/Statistics.HOUR_IN_MS).toString + " Hours"
+ } else {
+ return (value/Statistics.HOUR_IN_MS).toString + " Hours " + minutes.toString + " Minutes"
+ }
+ }
+ }
}
}
diff --git a/app/com/linkedin/drelephant/spark/heuristics/ExecutorStorageSpillHeuristic.scala b/app/com/linkedin/drelephant/spark/heuristics/ExecutorStorageSpillHeuristic.scala
index 7001c8e27..90571ed75 100644
--- a/app/com/linkedin/drelephant/spark/heuristics/ExecutorStorageSpillHeuristic.scala
+++ b/app/com/linkedin/drelephant/spark/heuristics/ExecutorStorageSpillHeuristic.scala
@@ -97,13 +97,20 @@ object ExecutorStorageSpillHeuristic {
class Evaluator(executorStorageSpillHeuristic: ExecutorStorageSpillHeuristic, data: SparkApplicationData) {
lazy val executorAndDriverSummaries: Seq[ExecutorSummary] = data.executorSummaries
+ if (executorAndDriverSummaries == null) {
+ throw new Exception("Executors Summary is null.")
+ }
lazy val executorSummaries: Seq[ExecutorSummary] = executorAndDriverSummaries.filterNot(_.id.equals("driver"))
+ if (executorSummaries.isEmpty) {
+ throw new Exception("No executor information available.")
+ }
lazy val appConfigurationProperties: Map[String, String] =
data.appConfigurationProperties
val maxTasks: Int = executorSummaries.head.maxTasks
val maxMemorySpilled: Long = executorSummaries.map(_.totalMemoryBytesSpilled).max
val meanMemorySpilled = executorSummaries.map(_.totalMemoryBytesSpilled).sum / executorSummaries.size
- val totalMemorySpilledPerTask = totalMemorySpilled/(executorSummaries.map(_.totalTasks).sum)
+ lazy val totalTasks = Integer.max(executorSummaries.map(_.totalTasks).sum, 1)
+ val totalMemorySpilledPerTask = totalMemorySpilled/totalTasks
lazy val totalMemorySpilled = executorSummaries.map(_.totalMemoryBytesSpilled).sum
val fractionOfExecutorsHavingBytesSpilled: Double = executorSummaries.count(_.totalMemoryBytesSpilled > 0).toDouble / executorSummaries.size.toDouble
val severity: Severity = {
diff --git a/app/com/linkedin/drelephant/spark/heuristics/ExecutorsHeuristic.scala b/app/com/linkedin/drelephant/spark/heuristics/ExecutorsHeuristic.scala
index dae604124..d125bc492 100644
--- a/app/com/linkedin/drelephant/spark/heuristics/ExecutorsHeuristic.scala
+++ b/app/com/linkedin/drelephant/spark/heuristics/ExecutorsHeuristic.scala
@@ -16,16 +16,16 @@
package com.linkedin.drelephant.spark.heuristics
-import scala.collection.JavaConverters
-import scala.collection.mutable.ArrayBuffer
-
-import com.linkedin.drelephant.analysis.{Heuristic, HeuristicResult, HeuristicResultDetails, Severity, SeverityThresholds}
+import com.linkedin.drelephant.analysis._
import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData
import com.linkedin.drelephant.math.Statistics
import com.linkedin.drelephant.spark.data.SparkApplicationData
import com.linkedin.drelephant.spark.fetchers.statusapiv1.ExecutorSummary
import com.linkedin.drelephant.util.MemoryFormatUtils
+import scala.collection.JavaConverters
+import scala.collection.mutable.ArrayBuffer
+
/**
* A heuristic based on metrics for a Spark app's executors.
@@ -37,6 +37,7 @@ import com.linkedin.drelephant.util.MemoryFormatUtils
class ExecutorsHeuristic(private val heuristicConfigurationData: HeuristicConfigurationData)
extends Heuristic[SparkApplicationData] {
import ExecutorsHeuristic._
+
import JavaConverters._
val maxToMedianRatioSeverityThresholds: SeverityThresholds =
diff --git a/app/com/linkedin/drelephant/spark/heuristics/JvmUsedMemoryHeuristic.scala b/app/com/linkedin/drelephant/spark/heuristics/JvmUsedMemoryHeuristic.scala
index f57aba948..5875151ed 100644
--- a/app/com/linkedin/drelephant/spark/heuristics/JvmUsedMemoryHeuristic.scala
+++ b/app/com/linkedin/drelephant/spark/heuristics/JvmUsedMemoryHeuristic.scala
@@ -26,7 +26,7 @@ import scala.collection.JavaConverters
/**
- * A heuristic based on peak JVM used memory for the spark executors and driver
+ * A heuristic based on peak JVM used memory for the spark executors
*
*/
class JvmUsedMemoryHeuristic(private val heuristicConfigurationData: HeuristicConfigurationData)
@@ -38,25 +38,19 @@ class JvmUsedMemoryHeuristic(private val heuristicConfigurationData: HeuristicCo
override def getHeuristicConfData(): HeuristicConfigurationData = heuristicConfigurationData
lazy val executorPeakJvmMemoryThresholdString: String = heuristicConfigurationData.getParamMap.get(MAX_EXECUTOR_PEAK_JVM_USED_MEMORY_THRESHOLD_KEY)
- lazy val driverPeakJvmMemoryThresholdString: String = heuristicConfigurationData.getParamMap.get(MAX_DRIVER_PEAK_JVM_USED_MEMORY_THRESHOLD_KEY)
+ lazy val sparkExecutorMemoryThreshold: String = heuristicConfigurationData.getParamMap.getOrDefault(SPARK_EXECUTOR_MEMORY_THRESHOLD_KEY, DEFAULT_SPARK_EXECUTOR_MEMORY_THRESHOLD)
override def apply(data: SparkApplicationData): HeuristicResult = {
val evaluator = new Evaluator(this, data)
var resultDetails = Seq(
new HeuristicResultDetails("Max executor peak JVM used memory", MemoryFormatUtils.bytesToString(evaluator.maxExecutorPeakJvmUsedMemory)),
- new HeuristicResultDetails("Max driver peak JVM used memory", MemoryFormatUtils.bytesToString(evaluator.maxDriverPeakJvmUsedMemory)),
- new HeuristicResultDetails("spark.executor.memory", MemoryFormatUtils.bytesToString(evaluator.sparkExecutorMemory)),
- new HeuristicResultDetails("spark.driver.memory", MemoryFormatUtils.bytesToString(evaluator.sparkDriverMemory))
+ new HeuristicResultDetails("spark.executor.memory", MemoryFormatUtils.bytesToString(evaluator.sparkExecutorMemory))
)
- if(evaluator.severityExecutor.getValue > Severity.LOW.getValue) {
- resultDetails :+ new HeuristicResultDetails("Executor Memory", "The allocated memory for the executor (in " + SPARK_EXECUTOR_MEMORY +") is much more than the peak JVM used memory by executors.")
- resultDetails :+ new HeuristicResultDetails("Reasonable size for executor memory", ((1+BUFFER_PERCENT.toDouble/100.0)*evaluator.maxExecutorPeakJvmUsedMemory).toString)
- }
-
- if(evaluator.severityDriver.getValue > Severity.LOW.getValue) {
- resultDetails :+ new HeuristicResultDetails("Driver Memory", "The allocated memory for the driver (in " + SPARK_DRIVER_MEMORY + ") is much more than the peak JVM used memory by the driver.")
+ if (evaluator.severity != Severity.NONE) {
+ resultDetails = resultDetails :+ new HeuristicResultDetails("Executor Memory", "The allocated memory for the executor (in " + SPARK_EXECUTOR_MEMORY + ") is much more than the peak JVM used memory by executors.")
+ resultDetails = resultDetails :+ new HeuristicResultDetails("Suggested spark.executor.memory", MemoryFormatUtils.roundOffMemoryStringToNextInteger((MemoryFormatUtils.bytesToString(((1 + BUFFER_FRACTION) * evaluator.maxExecutorPeakJvmUsedMemory).toLong))))
}
val result = new HeuristicResult(
@@ -73,52 +67,39 @@ class JvmUsedMemoryHeuristic(private val heuristicConfigurationData: HeuristicCo
object JvmUsedMemoryHeuristic {
val JVM_USED_MEMORY = "jvmUsedMemory"
val SPARK_EXECUTOR_MEMORY = "spark.executor.memory"
- val SPARK_DRIVER_MEMORY = "spark.driver.memory"
+ val SPARK_EXECUTOR_MEMORY_THRESHOLD_KEY = "spark_executor_memory_threshold"
+
// 300 * FileUtils.ONE_MB (300 * 1024 * 1024)
- val reservedMemory : Long = 314572800
- val BUFFER_PERCENT : Int = 20
+ val reservedMemory: Long = 314572800
+ val BUFFER_FRACTION: Double = 0.2
val MAX_EXECUTOR_PEAK_JVM_USED_MEMORY_THRESHOLD_KEY = "executor_peak_jvm_memory_threshold"
- val MAX_DRIVER_PEAK_JVM_USED_MEMORY_THRESHOLD_KEY = "driver_peak_jvm_memory_threshold"
+ lazy val DEFAULT_SPARK_EXECUTOR_MEMORY_THRESHOLD = "2G"
class Evaluator(jvmUsedMemoryHeuristic: JvmUsedMemoryHeuristic, data: SparkApplicationData) {
lazy val appConfigurationProperties: Map[String, String] =
data.appConfigurationProperties
lazy val executorSummaries: Seq[ExecutorSummary] = data.executorSummaries
- lazy val driverSummary : Option[ExecutorSummary] = executorSummaries.find(_.id.equals("driver"))
- val maxDriverPeakJvmUsedMemory : Long = driverSummary.get.peakJvmUsedMemory.getOrElse(JVM_USED_MEMORY, 0L).asInstanceOf[Number].longValue
- val executorList : Seq[ExecutorSummary] = executorSummaries.filterNot(_.id.equals("driver"))
- val sparkExecutorMemory : Long = (appConfigurationProperties.get(SPARK_EXECUTOR_MEMORY).map(MemoryFormatUtils.stringToBytes)).getOrElse(0L)
- val sparkDriverMemory : Long = appConfigurationProperties.get(SPARK_DRIVER_MEMORY).map(MemoryFormatUtils.stringToBytes).getOrElse(0L)
+ val executorList: Seq[ExecutorSummary] = executorSummaries.filterNot(_.id.equals("driver"))
+ val sparkExecutorMemory: Long = (appConfigurationProperties.get(SPARK_EXECUTOR_MEMORY).map(MemoryFormatUtils.stringToBytes)).getOrElse(0L)
lazy val maxExecutorPeakJvmUsedMemory: Long = if (executorList.isEmpty) 0L else executorList.map {
_.peakJvmUsedMemory.getOrElse(JVM_USED_MEMORY, 0).asInstanceOf[Number].longValue
}.max
lazy val DEFAULT_MAX_EXECUTOR_PEAK_JVM_USED_MEMORY_THRESHOLDS =
- SeverityThresholds(low = 1.5 * (maxExecutorPeakJvmUsedMemory + reservedMemory), moderate = 2 * (maxExecutorPeakJvmUsedMemory + reservedMemory), severe = 4 * (maxExecutorPeakJvmUsedMemory + reservedMemory), critical = 8 * (maxExecutorPeakJvmUsedMemory + reservedMemory), ascending = true)
-
- lazy val DEFAULT_MAX_DRIVER_PEAK_JVM_USED_MEMORY_THRESHOLDS =
- SeverityThresholds(low = 1.5 * (maxDriverPeakJvmUsedMemory + reservedMemory), moderate = 2 * (maxDriverPeakJvmUsedMemory + reservedMemory), severe = 4 * (maxDriverPeakJvmUsedMemory + reservedMemory), critical = 8 * (maxDriverPeakJvmUsedMemory + reservedMemory), ascending = true)
+ SeverityThresholds(low = 1.25 * (maxExecutorPeakJvmUsedMemory + reservedMemory), moderate = 1.5 * (maxExecutorPeakJvmUsedMemory + reservedMemory), severe = 2 * (maxExecutorPeakJvmUsedMemory + reservedMemory), critical = 3 * (maxExecutorPeakJvmUsedMemory + reservedMemory), ascending = true)
- val MAX_EXECUTOR_PEAK_JVM_USED_MEMORY_THRESHOLDS : SeverityThresholds = if(jvmUsedMemoryHeuristic.executorPeakJvmMemoryThresholdString == null) {
+ val MAX_EXECUTOR_PEAK_JVM_USED_MEMORY_THRESHOLDS: SeverityThresholds = if (jvmUsedMemoryHeuristic.executorPeakJvmMemoryThresholdString == null) {
DEFAULT_MAX_EXECUTOR_PEAK_JVM_USED_MEMORY_THRESHOLDS
} else {
SeverityThresholds.parse(jvmUsedMemoryHeuristic.executorPeakJvmMemoryThresholdString.split(",").map(_.toDouble * (maxExecutorPeakJvmUsedMemory + reservedMemory)).toString, ascending = false).getOrElse(DEFAULT_MAX_EXECUTOR_PEAK_JVM_USED_MEMORY_THRESHOLDS)
}
- val MAX_DRIVER_PEAK_JVM_USED_MEMORY_THRESHOLDS : SeverityThresholds = if(jvmUsedMemoryHeuristic.driverPeakJvmMemoryThresholdString == null) {
- DEFAULT_MAX_DRIVER_PEAK_JVM_USED_MEMORY_THRESHOLDS
+ lazy val severity = if (sparkExecutorMemory <= MemoryFormatUtils.stringToBytes(jvmUsedMemoryHeuristic.sparkExecutorMemoryThreshold)) {
+ Severity.NONE
} else {
- SeverityThresholds.parse(jvmUsedMemoryHeuristic.driverPeakJvmMemoryThresholdString.split(",").map(_.toDouble * (maxDriverPeakJvmUsedMemory + reservedMemory)).toString, ascending = false).getOrElse(DEFAULT_MAX_DRIVER_PEAK_JVM_USED_MEMORY_THRESHOLDS)
+ MAX_EXECUTOR_PEAK_JVM_USED_MEMORY_THRESHOLDS.severityOf(sparkExecutorMemory)
}
-
- val severityExecutor = MAX_EXECUTOR_PEAK_JVM_USED_MEMORY_THRESHOLDS.severityOf(sparkExecutorMemory)
- val severityDriver = MAX_DRIVER_PEAK_JVM_USED_MEMORY_THRESHOLDS.severityOf(sparkDriverMemory)
-
- /**
- * disabling the skew check for executors
- * val severitySkew = DEFAULT_JVM_MEMORY_SKEW_THRESHOLDS.severityOf(maxExecutorPeakJvmUsedMemory)
- */
- val severity : Severity = Severity.max(severityDriver, severityExecutor)
}
+
}
diff --git a/app/com/linkedin/drelephant/spark/heuristics/StagesHeuristic.scala b/app/com/linkedin/drelephant/spark/heuristics/StagesHeuristic.scala
index b2c36f90b..baa0426bd 100644
--- a/app/com/linkedin/drelephant/spark/heuristics/StagesHeuristic.scala
+++ b/app/com/linkedin/drelephant/spark/heuristics/StagesHeuristic.scala
@@ -192,7 +192,8 @@ object StagesHeuristic {
}
private def averageExecutorRuntimeAndSeverityOf(stageData: StageData): (Long, Severity) = {
- val averageExecutorRuntime = stageData.executorRunTime / executorInstances
+ val allTasks : Int = Integer.max((stageData.numActiveTasks + stageData.numCompleteTasks + stageData.numFailedTasks), 1)
+ val averageExecutorRuntime = stageData.executorRunTime / allTasks
(averageExecutorRuntime, stageRuntimeMillisSeverityThresholds.severityOf(averageExecutorRuntime))
}
}
diff --git a/app/com/linkedin/drelephant/spark/heuristics/StagesWithFailedTasksHeuristic.scala b/app/com/linkedin/drelephant/spark/heuristics/StagesWithFailedTasksHeuristic.scala
index 162d1021a..c062db2c1 100644
--- a/app/com/linkedin/drelephant/spark/heuristics/StagesWithFailedTasksHeuristic.scala
+++ b/app/com/linkedin/drelephant/spark/heuristics/StagesWithFailedTasksHeuristic.scala
@@ -43,7 +43,7 @@ class StagesWithFailedTasksHeuristic(private val heuristicConfigurationData: Heu
new HeuristicResultDetails("Stages with Overhead memory errors", evaluator.stagesWithOverheadError.toString)
)
if (evaluator.severityOverheadStages.getValue >= Severity.MODERATE.getValue)
- resultDetails = resultDetails :+ new HeuristicResultDetails("Overhead memory errors", "Some tasks have failed due to overhead memory error. Please try increasing spark.yarn.executor.memoryOverhead by 500MB in spark.yarn.executor.memoryOverhead")
+ resultDetails = resultDetails :+ new HeuristicResultDetails("Overhead memory errors", "Some tasks have failed due to overhead memory error. Please try increasing spark.yarn.executor.memoryOverhead by " + increaseMemoryBy +" in spark.yarn.executor.memoryOverhead")
//TODO: refine recommendations
if (evaluator.severityOOMStages.getValue >= Severity.MODERATE.getValue)
resultDetails = resultDetails :+ new HeuristicResultDetails("OOM errors", "Some tasks have failed due to OOM error. Try increasing spark.executor.memory or decreasing spark.memory.fraction (take a look at unified memory heuristic) or decreasing number of cores.")
@@ -63,6 +63,7 @@ object StagesWithFailedTasksHeuristic {
val OOM_ERROR = "java.lang.OutOfMemoryError"
val OVERHEAD_MEMORY_ERROR = "killed by YARN for exceeding memory limits"
val ratioThreshold: Double = 2
+ val increaseMemoryBy: String = "1G"
class Evaluator(memoryFractionHeuristic: StagesWithFailedTasksHeuristic, data: SparkApplicationData) {
lazy val stagesWithFailedTasks: Seq[StageData] = data.stagesWithFailedTasks
diff --git a/app/com/linkedin/drelephant/spark/heuristics/UnifiedMemoryHeuristic.scala b/app/com/linkedin/drelephant/spark/heuristics/UnifiedMemoryHeuristic.scala
index 3b6f54cfb..53d261d9b 100644
--- a/app/com/linkedin/drelephant/spark/heuristics/UnifiedMemoryHeuristic.scala
+++ b/app/com/linkedin/drelephant/spark/heuristics/UnifiedMemoryHeuristic.scala
@@ -39,6 +39,7 @@ class UnifiedMemoryHeuristic(private val heuristicConfigurationData: HeuristicCo
override def getHeuristicConfData(): HeuristicConfigurationData = heuristicConfigurationData
lazy val peakUnifiedMemoryThresholdString: String = heuristicConfigurationData.getParamMap.get(PEAK_UNIFIED_MEMORY_THRESHOLD_KEY)
+ val sparkExecutorMemoryThreshold: String = heuristicConfigurationData.getParamMap.getOrDefault(SPARK_EXECUTOR_MEMORY_THRESHOLD_KEY, DEFAULT_SPARK_EXECUTOR_MEMORY_THRESHOLD)
override def apply(data: SparkApplicationData): HeuristicResult = {
val evaluator = new Evaluator(this, data)
@@ -69,6 +70,8 @@ object UnifiedMemoryHeuristic {
val SPARK_EXECUTOR_MEMORY_KEY = "spark.executor.memory"
val SPARK_MEMORY_FRACTION_KEY = "spark.memory.fraction"
val PEAK_UNIFIED_MEMORY_THRESHOLD_KEY = "peak_unified_memory_threshold"
+ val SPARK_EXECUTOR_MEMORY_THRESHOLD_KEY = "spark_executor_memory_threshold"
+ val DEFAULT_SPARK_EXECUTOR_MEMORY_THRESHOLD = "2G"
class Evaluator(unifiedMemoryHeuristic: UnifiedMemoryHeuristic, data: SparkApplicationData) {
lazy val appConfigurationProperties: Map[String, String] =
@@ -95,39 +98,28 @@ object UnifiedMemoryHeuristic {
SeverityThresholds.parse(unifiedMemoryHeuristic.peakUnifiedMemoryThresholdString.split(",").map(_.toDouble * maxMemory).toString, ascending = false).getOrElse(DEFAULT_PEAK_UNIFIED_MEMORY_THRESHOLD)
}
- def getPeakUnifiedMemoryExecutorSeverity(executorSummary: ExecutorSummary): Severity = {
- return PEAK_UNIFIED_MEMORY_THRESHOLDS.severityOf(executorSummary.peakUnifiedMemory.getOrElse(EXECUTION_MEMORY, 0).asInstanceOf[Number].longValue
- + executorSummary.peakUnifiedMemory.getOrElse(STORAGE_MEMORY, 0).asInstanceOf[Number].longValue)
- }
-
val sparkExecutorMemory: Long = (appConfigurationProperties.get(SPARK_EXECUTOR_MEMORY_KEY).map(MemoryFormatUtils.stringToBytes)).getOrElse(0L)
- val sparkMemoryFraction: Double = appConfigurationProperties.getOrElse(SPARK_MEMORY_FRACTION_KEY, 0.6D).asInstanceOf[Number].doubleValue
+ lazy val sparkMemoryFraction: Double = appConfigurationProperties.getOrElse(SPARK_MEMORY_FRACTION_KEY, "0.6").toDouble
lazy val meanUnifiedMemory: Long = (executorList.map {
executorSummary => {
- executorSummary.peakUnifiedMemory.getOrElse(EXECUTION_MEMORY, 0).asInstanceOf[Number].longValue
- +executorSummary.peakUnifiedMemory.getOrElse(STORAGE_MEMORY, 0).asInstanceOf[Number].longValue
+ (executorSummary.peakUnifiedMemory.getOrElse(EXECUTION_MEMORY, 0).asInstanceOf[Number].longValue
+ + executorSummary.peakUnifiedMemory.getOrElse(STORAGE_MEMORY, 0).asInstanceOf[Number].longValue)
}
}.sum) / executorList.size
lazy val maxUnifiedMemory: Long = executorList.map {
executorSummary => {
- executorSummary.peakUnifiedMemory.getOrElse(EXECUTION_MEMORY, 0).asInstanceOf[Number].longValue
- +executorSummary.peakUnifiedMemory.getOrElse(STORAGE_MEMORY, 0).asInstanceOf[Number].longValue
+ (executorSummary.peakUnifiedMemory.getOrElse(EXECUTION_MEMORY, 0).asInstanceOf[Number].longValue
+ + executorSummary.peakUnifiedMemory.getOrElse(STORAGE_MEMORY, 0).asInstanceOf[Number].longValue)
}
}.max
- lazy val severity: Severity = {
- var severityPeakUnifiedMemoryVariable: Severity = Severity.NONE
- for (executorSummary <- executorList) {
- var peakUnifiedMemoryExecutorSeverity: Severity = getPeakUnifiedMemoryExecutorSeverity(executorSummary)
- if (peakUnifiedMemoryExecutorSeverity.getValue > severityPeakUnifiedMemoryVariable.getValue) {
- severityPeakUnifiedMemoryVariable = peakUnifiedMemoryExecutorSeverity
- }
- }
- severityPeakUnifiedMemoryVariable
+ lazy val severity: Severity = if (sparkExecutorMemory <= MemoryFormatUtils.stringToBytes(unifiedMemoryHeuristic.sparkExecutorMemoryThreshold)) {
+ Severity.NONE
+ } else {
+ PEAK_UNIFIED_MEMORY_THRESHOLDS.severityOf(maxUnifiedMemory)
}
}
-
}
diff --git a/app/com/linkedin/drelephant/util/MemoryFormatUtils.java b/app/com/linkedin/drelephant/util/MemoryFormatUtils.java
index 8ed49fbc0..0d7a8f584 100644
--- a/app/com/linkedin/drelephant/util/MemoryFormatUtils.java
+++ b/app/com/linkedin/drelephant/util/MemoryFormatUtils.java
@@ -90,7 +90,7 @@ public static String bytesToString(long value) {
* @return The bytes value
*/
public static long stringToBytes(String formattedString) {
- if (formattedString == null) {
+ if (formattedString == null || formattedString.isEmpty()) {
return 0L;
}
@@ -124,4 +124,41 @@ public static long stringToBytes(String formattedString) {
+ "] does not match any unit. The supported units are (case-insensitive, and also the 'B' is ignorable): ["
+ StringUtils.join(UNITS) + "].");
}
+
+ /**
+ * Given a memory value in string format, it rounds off the double value to next integer.
+ * @param formattedString
+ * @return : formatted String with int value to next integer.
+ */
+ public static String roundOffMemoryStringToNextInteger(String formattedString) {
+ if (formattedString == null || formattedString.isEmpty()) {
+ return "";
+ }
+
+ //handling if the string has , for eg. 1,000MB
+ formattedString = formattedString.replace(",", "");
+
+ Matcher matcher = REGEX_MATCHER.matcher(formattedString);
+ if (!matcher.matches()) {
+ throw new IllegalArgumentException(
+ "The formatted string [" + formattedString + "] does not match with the regex /" + REGEX_MATCHER.toString()
+ + "/");
+ }
+ if (matcher.groupCount() != 1 && matcher.groupCount() != 2) {
+ throw new IllegalArgumentException();
+ }
+
+ double numPart = Double.parseDouble(matcher.group(1));
+ if (numPart < 0) {
+ throw new IllegalArgumentException("The number part of the memory cannot be less than zero: [" + numPart + "].");
+ }
+
+ int numPartInt = (int) Math.ceil(numPart);
+
+ String unitPart = matcher.groupCount() == 2 ? matcher.group(2).toUpperCase() : "";
+ if (!unitPart.endsWith("B")) {
+ unitPart += "B";
+ }
+ return (numPartInt + " " + unitPart);
+ }
}
diff --git a/app/views/help/spark/helpConfigurationHeuristic.scala.html b/app/views/help/spark/helpConfigurationHeuristic.scala.html
index 1d9ec968f..6bf673521 100644
--- a/app/views/help/spark/helpConfigurationHeuristic.scala.html
+++ b/app/views/help/spark/helpConfigurationHeuristic.scala.html
@@ -14,8 +14,7 @@
* the License.
*@
The results from this heuristic primarily inform you about key app
-configuration settings, including driver memory, driver cores, executor cores,
-executor instances, executor memory, and the serializer.
-It also checks the values of dynamically allocated min and max executors, the specified yarn jars, executor and driver memory overhead and whether other configuration values are within threshold.
+configuration settings, including executor cores, executor instances, executor memory, and the serializer.
+It also checks the values of dynamically allocated min and max executors, the specified yarn jars, executor memory overhead and whether other configuration values are within threshold.
Suggestions
Suggestions based on the configurations you have set are given in the heuristic result itself.
\ No newline at end of file
diff --git a/app/views/help/spark/helpDriverHeuristic.scala.html b/app/views/help/spark/helpDriverHeuristic.scala.html
new file mode 100644
index 000000000..83f65a674
--- /dev/null
+++ b/app/views/help/spark/helpDriverHeuristic.scala.html
@@ -0,0 +1,23 @@
+@*
+* Copyright 2016 LinkedIn Corp.
+*
+* 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.
+*@
+This is a heuristic for checking whether the driver is well tuned and the configurations are set to a good value.
+It checks the following properties
+Driver Max Peak JVM Used Memory
+This analyses whether the driver memory is set to a good value. To avoid wasted memory, it checks if the peak JVM used memory by the driver is reasonably close to the user allocated driver memory which is specified in spark.driver.memory. If the peak JVM memory is much smaller, then the driver memory should be reduced.
+Time spent by driver in GC
+This checks if your job spends too much time in GC. We recommend increasing spark.driver.memory if it does.
+Checking configuration thresholds
+The values of spark.driver.memory, spark.driver.cores and spark.yarn.driver.memoryOverhead are checked to verify if they are within threshold values.
\ No newline at end of file
diff --git a/app/views/help/spark/helpJvmUsedMemoryHeuristic.scala.html b/app/views/help/spark/helpJvmUsedMemoryHeuristic.scala.html
index f58462821..8db78a999 100644
--- a/app/views/help/spark/helpJvmUsedMemoryHeuristic.scala.html
+++ b/app/views/help/spark/helpJvmUsedMemoryHeuristic.scala.html
@@ -15,6 +15,5 @@
*@
This is a heuristic for peak JVM used memory.
Executor Max Peak JVM Used Memory
-This is to analyse whether the executor memory is set to a good value. To avoid wasted memory, it checks if the peak JVM used memory by the executor is reasonably close to the blocked executor memory which is specified in spark.executor.memory. If the peak JVM memory is much smaller, then the executor memory should be reduced.
-Driver Max Peak JVM Used Memory
-This is to analyse whether the driver memory is set to a good value. To avoid wasted memory, it checks if the peak JVM used memory by the driver is reasonably close to the blocked driver memory which is specified in spark.driver.memory. If the peak JVM memory is much smaller, then the driver memory should be reduced.
+This is to analyse whether the executor memory is set to a good value. To avoid wasted memory, it checks if the peak JVM used memory by the executor is reasonably close to the user allocated executor memory which is specified in spark.executor.memory. If the peak JVM memory is much smaller, then the executor memory should be reduced.
+ Note: Please note that for calculation purposes Dr. Elephant considers 1024 Bytes in 1 KB whereas the spark history server considers 1000 Bytes. So please don't get confused if you find discrepancy in values from these two places.
\ No newline at end of file
diff --git a/app/views/help/spark/helpUnifiedMemoryHeuristic.scala.html b/app/views/help/spark/helpUnifiedMemoryHeuristic.scala.html
index 9ea156004..352e9e3ff 100644
--- a/app/views/help/spark/helpUnifiedMemoryHeuristic.scala.html
+++ b/app/views/help/spark/helpUnifiedMemoryHeuristic.scala.html
@@ -22,3 +22,4 @@ Action Items
2. If your job's Executor Memory is high, then we recommend reducing the spark.executor.memory itself which will lower the Allocated Unified Memory space.
Note:
spark.memory.fraction: This is the fraction of JVM Used Memory (Executor memory - Reserved memory) dedicated to the unified memory region (execution + storage). It basically partitions user memory from execution and storage memory.
+ Note: Please note that for calculation purposes Dr. Elephant considers 1024 Bytes in 1 KB whereas the spark history server considers 1000 Bytes. So please don't get confused if you find discrepancy in values from these two places.
\ No newline at end of file
diff --git a/test/com/linkedin/drelephant/spark/fetchers/SparkRestClientTest.scala b/test/com/linkedin/drelephant/spark/fetchers/SparkRestClientTest.scala
index 947fc8e7b..c20223fb8 100644
--- a/test/com/linkedin/drelephant/spark/fetchers/SparkRestClientTest.scala
+++ b/test/com/linkedin/drelephant/spark/fetchers/SparkRestClientTest.scala
@@ -303,7 +303,7 @@ object SparkRestClientTest {
@Path("applications/{appId}/{attemptId}/stages")
def getStages(): StagesResource = new StagesResource()
- @Path("applications/{appId}/{attemptId}/executors")
+ @Path("applications/{appId}/{attemptId}/allexecutors")
def getExecutors(): ExecutorsResource = new ExecutorsResource()
@Path("applications/{appId}/{attemptId}/logs")
@@ -385,7 +385,7 @@ object SparkRestClientTest {
@Path("applications/{appId}/stages")
def getStages(): StagesResource = new StagesResource()
- @Path("applications/{appId}/executors")
+ @Path("applications/{appId}/allexecutors")
def getExecutors(): ExecutorsResource = new ExecutorsResource()
@Path("applications/{appId}/logs")
diff --git a/test/com/linkedin/drelephant/spark/heuristics/ConfigurationHeuristicTest.scala b/test/com/linkedin/drelephant/spark/heuristics/ConfigurationHeuristicTest.scala
index f04777d5b..f22b5379e 100644
--- a/test/com/linkedin/drelephant/spark/heuristics/ConfigurationHeuristicTest.scala
+++ b/test/com/linkedin/drelephant/spark/heuristics/ConfigurationHeuristicTest.scala
@@ -27,7 +27,9 @@ import org.apache.spark.scheduler.SparkListenerEnvironmentUpdate
import org.scalatest.{FunSpec, Matchers}
import java.util.Date
-
+/**
+ * Test class for Configuration Heuristic. It checks whether all the values used in the heuristic are calculated correctly.
+ */
class ConfigurationHeuristicTest extends FunSpec with Matchers {
import ConfigurationHeuristicTest._
@@ -60,67 +62,50 @@ class ConfigurationHeuristicTest extends FunSpec with Matchers {
val heuristicResultDetails = heuristicResult.getHeuristicResultDetails
it("returns the size of result details") {
- heuristicResultDetails.size() should be(9)
+ heuristicResultDetails.size() should be(8)
}
it("returns the severity") {
heuristicResult.getSeverity should be(Severity.NONE)
}
- it("returns the driver memory") {
- val details = heuristicResultDetails.get(0)
- details.getName should include("spark.driver.memory")
- details.getValue should be("2 GB")
- }
-
it("returns the executor memory") {
- val details = heuristicResultDetails.get(1)
+ val details = heuristicResultDetails.get(0)
details.getName should include("spark.executor.memory")
details.getValue should be("1 GB")
}
it("returns the executor instances") {
- val details = heuristicResultDetails.get(2)
+ val details = heuristicResultDetails.get(1)
details.getName should include("spark.executor.instances")
details.getValue should be("900")
}
it("returns the executor cores") {
- val details = heuristicResultDetails.get(3)
+ val details = heuristicResultDetails.get(2)
details.getName should include("spark.executor.cores")
details.getValue should include("default")
}
it("returns the application duration") {
- val details = heuristicResultDetails.get(4)
+ val details = heuristicResultDetails.get(3)
details.getName should include("spark.application.duration")
details.getValue should include("10")
}
it("returns the dynamic allocation flag") {
- val details = heuristicResultDetails.get(5)
+ val details = heuristicResultDetails.get(4)
details.getName should include("spark.dynamicAllocation.enabled")
details.getValue should be("true")
}
-
- it("returns the driver cores") {
- val details = heuristicResultDetails.get(6)
- details.getName should include("spark.driver.cores")
- details.getValue should include("default")
- }
-
- it("returns the driver overhead memory") {
- val details = heuristicResultDetails.get(7)
- details.getName should include("spark.yarn.driver.memoryOverhead")
- details.getValue should include("500 MB")
- }
}
describe("apply with Severity") {
val configurationProperties = Map(
"spark.serializer" -> "dummySerializer",
"spark.shuffle.service.enabled" -> "false",
- "spark.dynamicAllocation.enabled" -> "true"
+ "spark.dynamicAllocation.enabled" -> "true",
+ "spark.executor.cores" -> "5"
)
val data = newFakeSparkApplicationData(configurationProperties)
@@ -136,24 +121,30 @@ class ConfigurationHeuristicTest extends FunSpec with Matchers {
}
it("returns the dynamic allocation flag") {
- val details = heuristicResultDetails.get(5)
+ val details = heuristicResultDetails.get(4)
details.getName should include("spark.dynamicAllocation.enabled")
details.getValue should be("true")
}
it("returns the serializer") {
- val details = heuristicResultDetails.get(9)
+ val details = heuristicResultDetails.get(8)
details.getName should include("spark.serializer")
details.getValue should be("dummySerializer")
details.getDetails should be("KyroSerializer is Not Enabled.")
}
it("returns the shuffle service flag") {
- val details = heuristicResultDetails.get(10)
+ val details = heuristicResultDetails.get(9)
details.getName should include("spark.shuffle.service.enabled")
details.getValue should be("false")
details.getDetails should be("Spark shuffle service is not enabled.")
}
+
+ it("returns executor cores") {
+ val details = heuristicResultDetails.get(10)
+ details.getName should include("Executor cores")
+ details.getValue should be("The number of executor cores should be <=4. Please change it in the field spark.executor.cores")
+ }
}
describe(".Evaluator") {
@@ -163,16 +154,6 @@ class ConfigurationHeuristicTest extends FunSpec with Matchers {
new Evaluator(configurationHeuristic, newFakeSparkApplicationData(configurationProperties))
}
- it("has the driver memory bytes when they're present") {
- val evaluator = newEvaluatorWithConfigurationProperties(Map("spark.driver.memory" -> "2G"))
- evaluator.driverMemoryBytes should be(Some(2L * 1024 * 1024 * 1024))
- }
-
- it("has no driver memory bytes when they're absent") {
- val evaluator = newEvaluatorWithConfigurationProperties(Map.empty)
- evaluator.driverMemoryBytes should be(None)
- }
-
it("has the executor memory bytes when they're present") {
val evaluator = newEvaluatorWithConfigurationProperties(Map("spark.executor.memory" -> "1g"))
evaluator.executorMemoryBytes should be(Some(1L * 1024 * 1024 * 1024))
@@ -198,21 +179,11 @@ class ConfigurationHeuristicTest extends FunSpec with Matchers {
evaluator.executorCores should be(Some(2))
}
- it("has the driver cores when they're present") {
- val evaluator = newEvaluatorWithConfigurationProperties(Map("spark.driver.cores" -> "3"))
- evaluator.driverCores should be(Some(3))
- }
-
it("has no executor cores when they're absent") {
val evaluator = newEvaluatorWithConfigurationProperties(Map.empty)
evaluator.executorCores should be(None)
}
- it("has no driver cores when they're absent") {
- val evaluator = newEvaluatorWithConfigurationProperties(Map.empty)
- evaluator.driverCores should be(None)
- }
-
it("has the serializer when it's present") {
val evaluator = newEvaluatorWithConfigurationProperties(Map("spark.serializer" -> "org.apache.spark.serializer.KryoSerializer"))
evaluator.serializer should be(Some("org.apache.spark.serializer.KryoSerializer"))
diff --git a/test/com/linkedin/drelephant/spark/heuristics/DriverHeuristicTest.scala b/test/com/linkedin/drelephant/spark/heuristics/DriverHeuristicTest.scala
new file mode 100644
index 000000000..fe0da4ec6
--- /dev/null
+++ b/test/com/linkedin/drelephant/spark/heuristics/DriverHeuristicTest.scala
@@ -0,0 +1,104 @@
+package com.linkedin.drelephant.spark.heuristics
+
+import com.linkedin.drelephant.analysis.{ApplicationType, Severity}
+import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData
+import com.linkedin.drelephant.spark.data.{SparkApplicationData, SparkRestDerivedData}
+import com.linkedin.drelephant.spark.fetchers.statusapiv1.{ApplicationInfoImpl, ExecutorSummaryImpl}
+import com.linkedin.drelephant.spark.heuristics.DriverHeuristic.Evaluator
+import org.scalatest.{FunSpec, Matchers}
+
+import scala.collection.JavaConverters
+import scala.concurrent.duration.Duration
+
+/**
+ * Test class for Driver Metrics Heuristic. It checks whether all the values used in the heuristic are calculated correctly.
+ */
+class DriverHeuristicTest extends FunSpec with Matchers {
+
+ import DriverHeuristicTest._
+
+ val heuristicConfigurationData = newFakeHeuristicConfigurationData()
+
+ val driverHeuristic = new DriverHeuristic(heuristicConfigurationData)
+
+ val executorData = Seq(
+ newDummyExecutorData("1", 400000, Map("executionMemory" -> 300000, "storageMemory" -> 94567), null, 0, 0),
+ newDummyExecutorData("2", 400000, Map("executionMemory" -> 200000, "storageMemory" -> 34568), null, 0, 0),
+ newDummyExecutorData("3", 400000, Map("executionMemory" -> 300000, "storageMemory" -> 34569), null, 0, 0),
+ newDummyExecutorData("4", 400000, Map("executionMemory" -> 20000, "storageMemory" -> 3456), null, 0, 0),
+ newDummyExecutorData("5", 400000, Map("executionMemory" -> 200000, "storageMemory" -> 34564), null, 0, 0),
+ newDummyExecutorData("driver", 400000, Map("executionMemory" -> 300000, "storageMemory" -> 94561), Map("jvmUsedMemory" -> 394567123),
+ totalGCTime = Duration("2min").toMillis, totalDuration = Duration("15min").toMillis)
+ )
+ describe(".apply") {
+ val data = newFakeSparkApplicationData(executorData)
+ val heuristicResult = driverHeuristic.apply(data)
+ val heuristicResultDetails = heuristicResult.getHeuristicResultDetails
+
+ it("has severity") {
+ heuristicResult.getSeverity should be(Severity.SEVERE)
+ }
+
+ describe("Evaluator") {
+ val evaluator = new Evaluator(driverHeuristic, data)
+ it("has max driver peak JVM memory") {
+ evaluator.maxDriverPeakJvmUsedMemory should be(394567123)
+ }
+ it("ratio of time spend in Gc to total duration") {
+ evaluator.ratio should be(0.13333333333333333)
+ }
+ }
+ }
+}
+
+object DriverHeuristicTest {
+
+ import JavaConverters._
+
+ def newFakeHeuristicConfigurationData(params: Map[String, String] = Map.empty): HeuristicConfigurationData =
+ new HeuristicConfigurationData("heuristic", "class", "view", new ApplicationType("type"), params.asJava)
+
+ def newDummyExecutorData(
+ id: String,
+ maxMemory: Long,
+ peakUnifiedMemory: Map[String, Long],
+ peakJvmUsedMemory: Map[String, Long],
+ totalGCTime: Long,
+ totalDuration: Long
+ ): ExecutorSummaryImpl = new ExecutorSummaryImpl(
+ id,
+ hostPort = "",
+ rddBlocks = 0,
+ memoryUsed = 0,
+ diskUsed = 0,
+ activeTasks = 0,
+ failedTasks = 0,
+ completedTasks = 0,
+ totalTasks = 0,
+ maxTasks = 0,
+ totalDuration,
+ totalInputBytes = 0,
+ totalShuffleRead = 0,
+ totalShuffleWrite = 0,
+ maxMemory,
+ totalGCTime,
+ totalMemoryBytesSpilled = 0,
+ executorLogs = Map.empty,
+ peakJvmUsedMemory,
+ peakUnifiedMemory
+ )
+
+ def newFakeSparkApplicationData(executorSummaries: Seq[ExecutorSummaryImpl]): SparkApplicationData = {
+ val appId = "application_1"
+ val restDerivedData = SparkRestDerivedData(
+ new ApplicationInfoImpl(appId, name = "app", Seq.empty),
+ jobDatas = Seq.empty,
+ stageDatas = Seq.empty,
+ executorSummaries = executorSummaries,
+ stagesWithFailedTasks = Seq.empty
+ )
+
+ SparkApplicationData(appId, restDerivedData, logDerivedData = None)
+ }
+}
+
diff --git a/test/com/linkedin/drelephant/spark/heuristics/ExecutorGcHeuristicTest.scala b/test/com/linkedin/drelephant/spark/heuristics/ExecutorGcHeuristicTest.scala
index 8bec2b3fa..2204922c1 100644
--- a/test/com/linkedin/drelephant/spark/heuristics/ExecutorGcHeuristicTest.scala
+++ b/test/com/linkedin/drelephant/spark/heuristics/ExecutorGcHeuristicTest.scala
@@ -26,18 +26,14 @@ import org.scalatest.{FunSpec, Matchers}
import scala.concurrent.duration.Duration
-
+/**
+ * Test class for Executor GC Heuristic. It checks whether all the values used in the heuristic are calculated correctly.
+ */
class ExecutorGcHeuristicTest extends FunSpec with Matchers {
import ExecutorGcHeuristicTest._
describe("ExecutorGcHeuristic") {
- val heuristicConfigurationData = newFakeHeuristicConfigurationData(
- Map(
- "max_to_median_ratio_severity_thresholds" -> "1.414,2,4,16",
- "ignore_max_bytes_less_than_threshold" -> "4000000",
- "ignore_max_millis_less_than_threshold" -> "4000001"
- )
- )
+ val heuristicConfigurationData = newFakeHeuristicConfigurationData()
val executorGcHeuristic = new ExecutorGcHeuristic(heuristicConfigurationData)
val executorSummaries = Seq(
@@ -63,10 +59,21 @@ class ExecutorGcHeuristicTest extends FunSpec with Matchers {
)
)
+ val executorSummaries1 = Seq(
+ newFakeExecutorSummary(
+ id = "1",
+ totalGCTime = 500,
+ totalDuration = 700
+ )
+ )
+
describe(".apply") {
- val data1 = newFakeSparkApplicationData(executorSummaries)
- val heuristicResult = executorGcHeuristic.apply(data1)
+ val data = newFakeSparkApplicationData(executorSummaries)
+ val data1 = newFakeSparkApplicationData(executorSummaries1)
+ val heuristicResult = executorGcHeuristic.apply(data)
+ val heuristicResult1 = executorGcHeuristic.apply(data1)
val heuristicResultDetails = heuristicResult.getHeuristicResultDetails
+ val heuristicResultDetails1 = heuristicResult1.getHeuristicResultDetails
it("returns the severity") {
heuristicResult.getSeverity should be(Severity.CRITICAL)
@@ -81,13 +88,25 @@ class ExecutorGcHeuristicTest extends FunSpec with Matchers {
it("returns the total GC time") {
val details = heuristicResultDetails.get(1)
details.getName should include("Total GC time")
- details.getValue should be("1200000")
+ details.getValue should be("20 Minutes")
}
it("returns the executor's run time") {
val details = heuristicResultDetails.get(2)
details.getName should include("Total Executor Runtime")
- details.getValue should be("4740000")
+ details.getValue should be("1 Hours 19 Minutes")
+ }
+
+ it("returns total Gc Time in millisec") {
+ val details = heuristicResultDetails1.get(1)
+ details.getName should include("Total GC time")
+ details.getValue should be("500 msec")
+ }
+
+ it("returns executor run Time in millisec") {
+ val details = heuristicResultDetails1.get(2)
+ details.getName should include("Total Executor Runtime")
+ details.getValue should be("700 msec")
}
}
}
diff --git a/test/com/linkedin/drelephant/spark/heuristics/ExecutorStorageSpillHeuristicTest.scala b/test/com/linkedin/drelephant/spark/heuristics/ExecutorStorageSpillHeuristicTest.scala
index 6b083c5d5..e02e4f2fb 100644
--- a/test/com/linkedin/drelephant/spark/heuristics/ExecutorStorageSpillHeuristicTest.scala
+++ b/test/com/linkedin/drelephant/spark/heuristics/ExecutorStorageSpillHeuristicTest.scala
@@ -21,10 +21,13 @@ import com.linkedin.drelephant.analysis.{ApplicationType, Severity, SeverityThre
import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData
import com.linkedin.drelephant.spark.data.{SparkApplicationData, SparkLogDerivedData, SparkRestDerivedData}
import com.linkedin.drelephant.spark.fetchers.statusapiv1.{ApplicationInfoImpl, ExecutorSummaryImpl, StageDataImpl}
+import com.linkedin.drelephant.spark.heuristics.ExecutorStorageSpillHeuristic.Evaluator
import org.apache.spark.scheduler.SparkListenerEnvironmentUpdate
import org.scalatest.{FunSpec, Matchers}
-
+/**
+ * Test class for Executor Storage Spill Heuristic. It checks whether all the values used in the heuristic are calculated correctly.
+ */
class ExecutorStorageSpillHeuristicTest extends FunSpec with Matchers {
import ExecutorStorageSpillHeuristicTest._
@@ -59,6 +62,7 @@ class ExecutorStorageSpillHeuristicTest extends FunSpec with Matchers {
val data1 = newFakeSparkApplicationData(executorSummaries, appConfigurationProperties)
val heuristicResult = executorStorageSpillHeuristic.apply(data1)
val heuristicResultDetails = heuristicResult.getHeuristicResultDetails
+ val evaluator = new Evaluator(executorStorageSpillHeuristic, data1)
it("returns the severity") {
heuristicResult.getSeverity should be(Severity.SEVERE)
@@ -81,6 +85,10 @@ class ExecutorStorageSpillHeuristicTest extends FunSpec with Matchers {
details.getName should include("Mean memory spilled")
details.getValue should be("195.31 KB")
}
+
+ it("has the memory spilled per task") {
+ evaluator.totalMemorySpilledPerTask should be(800000)
+ }
}
}
}
@@ -103,7 +111,7 @@ object ExecutorStorageSpillHeuristicTest {
activeTasks = 0,
failedTasks = 0,
completedTasks = 0,
- totalTasks = 10,
+ totalTasks = 0,
maxTasks = 10,
totalDuration=0,
totalInputBytes=0,
diff --git a/test/com/linkedin/drelephant/spark/heuristics/ExecutorsHeuristicTest.scala b/test/com/linkedin/drelephant/spark/heuristics/ExecutorsHeuristicTest.scala
index 2ef2580e9..c1996fe41 100644
--- a/test/com/linkedin/drelephant/spark/heuristics/ExecutorsHeuristicTest.scala
+++ b/test/com/linkedin/drelephant/spark/heuristics/ExecutorsHeuristicTest.scala
@@ -25,7 +25,9 @@ import com.linkedin.drelephant.spark.fetchers.statusapiv1.{ApplicationInfoImpl,
import org.apache.spark.scheduler.SparkListenerEnvironmentUpdate
import org.scalatest.{FunSpec, Matchers}
-
+/**
+ * Test class for Executors Heuristic. It checks whether all the values used in the heuristic are calculated correctly.
+ */
class ExecutorsHeuristicTest extends FunSpec with Matchers {
import ExecutorsHeuristicTest._
diff --git a/test/com/linkedin/drelephant/spark/heuristics/JobsHeuristicTest.scala b/test/com/linkedin/drelephant/spark/heuristics/JobsHeuristicTest.scala
index c112a312e..435eddcdb 100644
--- a/test/com/linkedin/drelephant/spark/heuristics/JobsHeuristicTest.scala
+++ b/test/com/linkedin/drelephant/spark/heuristics/JobsHeuristicTest.scala
@@ -26,7 +26,9 @@ import org.apache.spark.JobExecutionStatus
import org.apache.spark.scheduler.SparkListenerEnvironmentUpdate
import org.scalatest.{FunSpec, Matchers}
-
+/**
+ * Test class for Jobs Heuristic. It checks whether all the values used in the heuristic are calculated correctly.
+ */
class JobsHeuristicTest extends FunSpec with Matchers {
import JobsHeuristicTest._
diff --git a/test/com/linkedin/drelephant/spark/heuristics/JvmUsedMemoryHeuristicTest.scala b/test/com/linkedin/drelephant/spark/heuristics/JvmUsedMemoryHeuristicTest.scala
index d9b2e2106..e0dc6641f 100644
--- a/test/com/linkedin/drelephant/spark/heuristics/JvmUsedMemoryHeuristicTest.scala
+++ b/test/com/linkedin/drelephant/spark/heuristics/JvmUsedMemoryHeuristicTest.scala
@@ -9,6 +9,9 @@ import org.scalatest.{FunSpec, Matchers}
import scala.collection.JavaConverters
+/**
+ * Test class for JVM Used memory. It checks whether all the values used in the heuristic are calculated correctly.
+ */
class JvmUsedMemoryHeuristicTest extends FunSpec with Matchers {
import JvmUsedMemoryHeuristicTest._
@@ -17,44 +20,49 @@ class JvmUsedMemoryHeuristicTest extends FunSpec with Matchers {
val peakJvmUsedMemoryHeuristic = new JvmUsedMemoryHeuristic(heuristicConfigurationData)
- val appConfigurationProperties = Map("spark.driver.memory"->"40000000000", "spark.executor.memory"->"500000000")
+ val appConfigurationProperties = Map("spark.driver.memory"->"40000000000", "spark.executor.memory"->"50000000000")
val executorData = Seq(
newDummyExecutorData("1", Map("jvmUsedMemory" -> 394567123)),
- newDummyExecutorData("2", Map("jvmUsedMemory" -> 23456834)),
- newDummyExecutorData("3", Map("jvmUsedMemory" -> 334569)),
- newDummyExecutorData("4", Map("jvmUsedMemory" -> 134563)),
- newDummyExecutorData("5", Map("jvmUsedMemory" -> 234564)),
+ newDummyExecutorData("2", Map("jvmUsedMemory" -> 2834)),
+ newDummyExecutorData("3", Map("jvmUsedMemory" -> 3945667)),
+ newDummyExecutorData("4", Map("jvmUsedMemory" -> 16367890)),
+ newDummyExecutorData("5", Map("jvmUsedMemory" -> 2345647)),
newDummyExecutorData("driver", Map("jvmUsedMemory" -> 394561))
)
describe(".apply") {
val data = newFakeSparkApplicationData(appConfigurationProperties, executorData)
val heuristicResult = peakJvmUsedMemoryHeuristic.apply(data)
+ val heuristicResultDetails = heuristicResult.getHeuristicResultDetails
it("has severity") {
heuristicResult.getSeverity should be(Severity.CRITICAL)
}
+ it("has all the details") {
+ heuristicResultDetails.size() should be(4)
+ }
+
describe(".Evaluator") {
import JvmUsedMemoryHeuristic.Evaluator
val data = newFakeSparkApplicationData(appConfigurationProperties, executorData)
+ val heuristicResult = peakJvmUsedMemoryHeuristic.apply(data)
+ val heuristicResultDetails = heuristicResult.getHeuristicResultDetails
val evaluator = new Evaluator(peakJvmUsedMemoryHeuristic, data)
it("has severity executor") {
- evaluator.severityExecutor should be(Severity.NONE)
- }
-
- it("has severity driver") {
- evaluator.severityDriver should be(Severity.CRITICAL)
+ evaluator.severity should be(Severity.CRITICAL)
}
it("has max peak jvm memory") {
evaluator.maxExecutorPeakJvmUsedMemory should be (394567123)
}
- it("has max driver peak jvm memory") {
- evaluator.maxDriverPeakJvmUsedMemory should be (394561)
+ it("has reasonable size") {
+ val details = heuristicResultDetails.get(3)
+ details.getName should be ("Suggested spark.executor.memory")
+ details.getValue should be ("452 MB")
}
}
}
diff --git a/test/com/linkedin/drelephant/spark/heuristics/StagesHeuristicTest.scala b/test/com/linkedin/drelephant/spark/heuristics/StagesHeuristicTest.scala
index 723f69250..e6aae4fe1 100644
--- a/test/com/linkedin/drelephant/spark/heuristics/StagesHeuristicTest.scala
+++ b/test/com/linkedin/drelephant/spark/heuristics/StagesHeuristicTest.scala
@@ -27,6 +27,9 @@ import com.linkedin.drelephant.spark.fetchers.statusapiv1.StageStatus
import org.apache.spark.scheduler.SparkListenerEnvironmentUpdate
import org.scalatest.{FunSpec, Matchers}
+/**
+ * Test class for Stages Heuristic. It checks whether all the values used in the heuristic are calculated correctly.
+ */
class StagesHeuristicTest extends FunSpec with Matchers {
import StagesHeuristicTest._
@@ -81,13 +84,6 @@ class StagesHeuristicTest extends FunSpec with Matchers {
|stage 4, attempt 0 (task failure rate: 0.800)""".stripMargin
)
}
-
- it("returns the list of stages with long runtimes") {
- heuristicResultDetails.get(4).getValue should be(
- s"""|stage 8, attempt 0 (runtime: 45 min)
- |stage 9, attempt 0 (runtime: 1 hr)""".stripMargin
- )
- }
}
describe(".Evaluator") {
@@ -113,15 +109,6 @@ class StagesHeuristicTest extends FunSpec with Matchers {
evaluator.stagesWithHighTaskFailureRates.map { case (stageData, taskFailureRate) => (stageData.stageId, taskFailureRate) }
stageIdsAndTaskFailureRates should contain theSameElementsInOrderAs(Seq((3, 0.6D), (4, 0.8D)))
}
-
- it("has the list of stages with long average executor runtimes") {
- val stageIdsAndRuntimes =
- evaluator.stagesWithLongAverageExecutorRuntimes.map { case (stageData, runtime) => (stageData.stageId, runtime) }
- stageIdsAndRuntimes should contain theSameElementsInOrderAs(
- Seq((8, Duration("45min").toMillis), (9, Duration("60min").toMillis))
- )
- }
-
it("computes the overall severity") {
evaluator.severity should be(Severity.CRITICAL)
}
diff --git a/test/com/linkedin/drelephant/spark/heuristics/StagesWithFailedTasksHeuristicTest.scala b/test/com/linkedin/drelephant/spark/heuristics/StagesWithFailedTasksHeuristicTest.scala
index 219e3ec05..cdfdc11ea 100644
--- a/test/com/linkedin/drelephant/spark/heuristics/StagesWithFailedTasksHeuristicTest.scala
+++ b/test/com/linkedin/drelephant/spark/heuristics/StagesWithFailedTasksHeuristicTest.scala
@@ -28,7 +28,9 @@ import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationDa
import com.linkedin.drelephant.spark.data.{SparkApplicationData, SparkRestDerivedData}
import com.linkedin.drelephant.spark.fetchers.statusapiv1.{ApplicationInfoImpl, StageDataImpl, TaskDataImpl}
-
+/**
+ * Test class for Stages With Failed Tasks Heuristic. It checks whether all the values used in the heuristic are calculated correctly.
+ */
class StagesWithFailedTasksHeuristicTest extends FunSpec with Matchers {
import StagesWithFailedTasksHeuristicTest._
diff --git a/test/com/linkedin/drelephant/spark/heuristics/UnifiedMemoryHeuristicTest.scala b/test/com/linkedin/drelephant/spark/heuristics/UnifiedMemoryHeuristicTest.scala
index 746b32741..68f668efe 100644
--- a/test/com/linkedin/drelephant/spark/heuristics/UnifiedMemoryHeuristicTest.scala
+++ b/test/com/linkedin/drelephant/spark/heuristics/UnifiedMemoryHeuristicTest.scala
@@ -2,36 +2,89 @@ package com.linkedin.drelephant.spark.heuristics
import com.linkedin.drelephant.analysis.{ApplicationType, Severity}
import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData
-import com.linkedin.drelephant.spark.data.{SparkApplicationData, SparkRestDerivedData}
+import com.linkedin.drelephant.spark.data.{SparkApplicationData, SparkLogDerivedData, SparkRestDerivedData}
import com.linkedin.drelephant.spark.fetchers.statusapiv1.{ApplicationInfoImpl, ExecutorSummaryImpl}
+import com.linkedin.drelephant.spark.heuristics.UnifiedMemoryHeuristic.Evaluator
+import org.apache.spark.scheduler.SparkListenerEnvironmentUpdate
import org.scalatest.{FunSpec, Matchers}
import scala.collection.JavaConverters
+/**
+ * Test class for Unified Memory Heuristic. It checks whether all the values used in the heuristic are calculated correctly.
+ */
class UnifiedMemoryHeuristicTest extends FunSpec with Matchers {
import UnifiedMemoryHeuristicTest._
val heuristicConfigurationData = newFakeHeuristicConfigurationData()
-
- val memoryFractionHeuristic = new UnifiedMemoryHeuristic(heuristicConfigurationData)
+ val unifiedMemoryHeuristic = new UnifiedMemoryHeuristic(heuristicConfigurationData)
+ val appConfigurationProperties = Map("spark.executor.memory"->"3147483647")
+ val appConfigurationProperties1 = Map("spark.executor.memory"->"214567874847")
val executorData = Seq(
- newDummyExecutorData("1", 400000, Map("executionMemory" -> 300000, "storageMemory" -> 94567)),
+ newDummyExecutorData("1", 999999999, Map("executionMemory" -> 300000, "storageMemory" -> 94567)),
newDummyExecutorData("2", 400000, Map("executionMemory" -> 200000, "storageMemory" -> 34568)),
newDummyExecutorData("3", 400000, Map("executionMemory" -> 300000, "storageMemory" -> 34569)),
newDummyExecutorData("4", 400000, Map("executionMemory" -> 20000, "storageMemory" -> 3456)),
newDummyExecutorData("5", 400000, Map("executionMemory" -> 200000, "storageMemory" -> 34564)),
newDummyExecutorData("6", 400000, Map("executionMemory" -> 300000, "storageMemory" -> 94561))
)
+
+ val executorData1 = Seq(
+ newDummyExecutorData("driver", 400000, Map("executionMemory" -> 300000, "storageMemory" -> 94567)),
+ newDummyExecutorData("2", 999999999, Map("executionMemory" -> 200, "storageMemory" -> 200))
+ )
+
+ val executorData2 = Seq(
+ newDummyExecutorData("driver", 400000, Map("executionMemory" -> 300000, "storageMemory" -> 94567)),
+ newDummyExecutorData("2", 999999999, Map("executionMemory" -> 999999990, "storageMemory" -> 9))
+ )
+
describe(".apply") {
- val data = newFakeSparkApplicationData(executorData)
- val heuristicResult = memoryFractionHeuristic.apply(data)
- val heuristicResultDetails = heuristicResult.getHeuristicResultDetails
+ val data = newFakeSparkApplicationData(appConfigurationProperties, executorData)
+ val data1 = newFakeSparkApplicationData(appConfigurationProperties1, executorData1)
+ val data2 = newFakeSparkApplicationData(appConfigurationProperties1, executorData2)
+ val heuristicResult = unifiedMemoryHeuristic.apply(data)
+ val heuristicResult1 = unifiedMemoryHeuristic.apply(data1)
+ val heuristicResult2 = unifiedMemoryHeuristic.apply(data2)
+ val evaluator = new Evaluator(unifiedMemoryHeuristic, data1)
it("has severity") {
heuristicResult.getSeverity should be(Severity.CRITICAL)
}
+
+ it("has max value") {
+ val details = heuristicResult.getHeuristicResultDetails.get(2)
+ details.getName should be("Max peak unified memory")
+ details.getValue should be("385.32 KB")
+ }
+
+ it("has mean value") {
+ val details = heuristicResult.getHeuristicResultDetails.get(1)
+ details.getName should be("Mean peak unified memory")
+ details.getValue should be("263.07 KB")
+ }
+
+ it("data1 has severity") {
+ heuristicResult1.getSeverity should be(Severity.CRITICAL)
+ }
+
+ it("data1 has maxMemory") {
+ evaluator.maxMemory should be(999999999)
+ }
+
+ it("data1 has max memory") {
+ evaluator.maxUnifiedMemory should be(400)
+ }
+
+ it("data1 has mean memory") {
+ evaluator.meanUnifiedMemory should be(400)
+ }
+
+ it("has no severity when max and allocated memory are the same") {
+ heuristicResult2.getSeverity should be(Severity.NONE)
+ }
}
}
@@ -69,7 +122,10 @@ object UnifiedMemoryHeuristicTest {
peakUnifiedMemory
)
- def newFakeSparkApplicationData(executorSummaries: Seq[ExecutorSummaryImpl]): SparkApplicationData = {
+ def newFakeSparkApplicationData(
+ appConfigurationProperties: Map[String, String],
+ executorSummaries: Seq[ExecutorSummaryImpl]): SparkApplicationData =
+ {
val appId = "application_1"
val restDerivedData = SparkRestDerivedData(
new ApplicationInfoImpl(appId, name = "app", Seq.empty),
@@ -78,7 +134,9 @@ object UnifiedMemoryHeuristicTest {
executorSummaries = executorSummaries,
stagesWithFailedTasks = Seq.empty
)
-
- SparkApplicationData(appId, restDerivedData, logDerivedData = None)
+ val logDerivedData = SparkLogDerivedData(
+ SparkListenerEnvironmentUpdate(Map("Spark Properties" -> appConfigurationProperties.toSeq))
+ )
+ SparkApplicationData(appId, restDerivedData, Some(logDerivedData))
}
}
diff --git a/test/com/linkedin/drelephant/util/MemoryFormatUtilsTest.java b/test/com/linkedin/drelephant/util/MemoryFormatUtilsTest.java
index 0ae064ebc..067f7427d 100644
--- a/test/com/linkedin/drelephant/util/MemoryFormatUtilsTest.java
+++ b/test/com/linkedin/drelephant/util/MemoryFormatUtilsTest.java
@@ -105,4 +105,16 @@ public void testStringToBytes() {
}
}
}
+
+ public void testRoundOffMemoryStringToNextInteger() {
+ assertEquals("157 MB", MemoryFormatUtils.roundOffMemoryStringToNextInteger("156.1 MB"));
+ assertEquals("155 MB", MemoryFormatUtils.roundOffMemoryStringToNextInteger("155.0 MB"));
+ assertEquals("0 MB", MemoryFormatUtils.roundOffMemoryStringToNextInteger("0 MB"));
+ assertEquals("156 GB", MemoryFormatUtils.roundOffMemoryStringToNextInteger("155.1 G"));
+ assertEquals("", MemoryFormatUtils.roundOffMemoryStringToNextInteger(null));
+ assertEquals("500 MB", MemoryFormatUtils.roundOffMemoryStringToNextInteger("500M"));
+ assertEquals("600 GB", MemoryFormatUtils.roundOffMemoryStringToNextInteger("600 gb"));
+ assertEquals("600 GB", MemoryFormatUtils.roundOffMemoryStringToNextInteger("600 g"));
+ assertEquals("", MemoryFormatUtils.roundOffMemoryStringToNextInteger(""));
+ }
}
From e15343b883bb880cef4ead3024fd6b590a755daa Mon Sep 17 00:00:00 2001
From: skakker
Date: Fri, 6 Apr 2018 16:46:17 +0530
Subject: [PATCH 08/15] Removing blocking keyword so as to prevent a large
number of threads being spawned (#362)
---
app/com/linkedin/drelephant/spark/fetchers/SparkLogClient.scala | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/com/linkedin/drelephant/spark/fetchers/SparkLogClient.scala b/app/com/linkedin/drelephant/spark/fetchers/SparkLogClient.scala
index 2a6464627..71023fab4 100644
--- a/app/com/linkedin/drelephant/spark/fetchers/SparkLogClient.scala
+++ b/app/com/linkedin/drelephant/spark/fetchers/SparkLogClient.scala
@@ -62,7 +62,7 @@ class SparkLogClient(hadoopConfiguration: Configuration, sparkConf: SparkConf, e
val (eventLogPath, eventLogCodec) =
sparkUtils.pathAndCodecforEventLog(sparkConf, eventLogFileSystem, baseEventLogPath, appId, attemptId)
- Future {
+ Future {
sparkUtils.withEventLog(eventLogFileSystem, eventLogPath, eventLogCodec)(findDerivedData(_))
}
}
From 8d7b64d5974448c73ec543318714f359d655f338 Mon Sep 17 00:00:00 2001
From: akrai
Date: Thu, 14 Jun 2018 09:54:47 +0530
Subject: [PATCH 09/15] Revert "Dr. Elephant Tez Support working patch (#313)"
This reverts commit a0470a3e6874527291bcc546a39cce28fc9cdd3e.
---
app-conf/AggregatorConf.xml | 4 -
app-conf/FetcherConf.xml | 7 -
app-conf/HeuristicConf.xml | 117 ------
app-conf/JobTypeConf.xml | 6 -
.../fetchers/MapReduceFetcherHadoop2.java | 84 +++-
.../drelephant/tez/TezMetricsAggregator.java | 109 -----
.../tez/TezTaskLevelAggregatedMetrics.java | 154 -------
.../tez/data/TezApplicationData.java | 137 ------
.../drelephant/tez/data/TezCounterData.java | 195 ---------
.../drelephant/tez/data/TezTaskData.java | 137 ------
.../drelephant/tez/fetchers/TezFetcher.java | 391 ------------------
.../heuristics/GenericDataSkewHeuristic.java | 215 ----------
.../tez/heuristics/GenericGCHeuristic.java | 142 -------
.../heuristics/GenericMemoryHeuristic.java | 185 ---------
.../heuristics/MapperDataSkewHeuristic.java | 47 ---
.../tez/heuristics/MapperGCHeuristic.java | 51 ---
.../tez/heuristics/MapperMemoryHeuristic.java | 45 --
.../tez/heuristics/MapperSpeedHeuristic.java | 165 --------
.../tez/heuristics/MapperSpillHeuristic.java | 142 -------
.../tez/heuristics/MapperTimeHeuristic.java | 182 --------
.../heuristics/ReducerDataSkewHeuristic.java | 42 --
.../tez/heuristics/ReducerGCHeuristic.java | 41 --
.../heuristics/ReducerMemoryHeuristic.java | 47 ---
.../tez/heuristics/ReducerTimeHeuristic.java | 167 --------
.../drelephant/util/InfoExtractor.java | 11 -
.../drelephant/util/ThreadContextMR2.java | 92 -----
.../fetchers/MapReduceFetcherHadoop2Test.java | 2 -
.../TezTaskLevelAggregatedMetricsTest.java | 62 ---
.../tez/fetchers/TezFetcherTest.java | 35 --
.../MapperDataSkewHeuristicTest.java | 146 -------
.../tez/heuristics/MapperGCHeuristicTest.java | 84 ----
.../heuristics/MapperMemoryHeuristicTest.java | 94 -----
.../heuristics/MapperSpeedHeuristicTest.java | 100 -----
.../heuristics/MapperSpillHeuristicTest.java | 90 ----
.../heuristics/MapperTimeHeuristicTest.java | 111 -----
.../ReducerDataSkewHeuristicTest.java | 142 -------
.../heuristics/ReducerGCHeuristicTest.java | 83 ----
.../ReducerMemoryHeuristicTest.java | 93 -----
.../heuristics/ReducerTimeHeuristicTest.java | 97 -----
39 files changed, 83 insertions(+), 3971 deletions(-)
delete mode 100644 app/com/linkedin/drelephant/tez/TezMetricsAggregator.java
delete mode 100644 app/com/linkedin/drelephant/tez/TezTaskLevelAggregatedMetrics.java
delete mode 100644 app/com/linkedin/drelephant/tez/data/TezApplicationData.java
delete mode 100644 app/com/linkedin/drelephant/tez/data/TezCounterData.java
delete mode 100644 app/com/linkedin/drelephant/tez/data/TezTaskData.java
delete mode 100644 app/com/linkedin/drelephant/tez/fetchers/TezFetcher.java
delete mode 100644 app/com/linkedin/drelephant/tez/heuristics/GenericDataSkewHeuristic.java
delete mode 100644 app/com/linkedin/drelephant/tez/heuristics/GenericGCHeuristic.java
delete mode 100644 app/com/linkedin/drelephant/tez/heuristics/GenericMemoryHeuristic.java
delete mode 100644 app/com/linkedin/drelephant/tez/heuristics/MapperDataSkewHeuristic.java
delete mode 100644 app/com/linkedin/drelephant/tez/heuristics/MapperGCHeuristic.java
delete mode 100644 app/com/linkedin/drelephant/tez/heuristics/MapperMemoryHeuristic.java
delete mode 100644 app/com/linkedin/drelephant/tez/heuristics/MapperSpeedHeuristic.java
delete mode 100644 app/com/linkedin/drelephant/tez/heuristics/MapperSpillHeuristic.java
delete mode 100644 app/com/linkedin/drelephant/tez/heuristics/MapperTimeHeuristic.java
delete mode 100644 app/com/linkedin/drelephant/tez/heuristics/ReducerDataSkewHeuristic.java
delete mode 100644 app/com/linkedin/drelephant/tez/heuristics/ReducerGCHeuristic.java
delete mode 100644 app/com/linkedin/drelephant/tez/heuristics/ReducerMemoryHeuristic.java
delete mode 100644 app/com/linkedin/drelephant/tez/heuristics/ReducerTimeHeuristic.java
delete mode 100644 app/com/linkedin/drelephant/util/ThreadContextMR2.java
delete mode 100644 test/com/linkedin/drelephant/tez/TezTaskLevelAggregatedMetricsTest.java
delete mode 100644 test/com/linkedin/drelephant/tez/fetchers/TezFetcherTest.java
delete mode 100644 test/com/linkedin/drelephant/tez/heuristics/MapperDataSkewHeuristicTest.java
delete mode 100644 test/com/linkedin/drelephant/tez/heuristics/MapperGCHeuristicTest.java
delete mode 100644 test/com/linkedin/drelephant/tez/heuristics/MapperMemoryHeuristicTest.java
delete mode 100644 test/com/linkedin/drelephant/tez/heuristics/MapperSpeedHeuristicTest.java
delete mode 100644 test/com/linkedin/drelephant/tez/heuristics/MapperSpillHeuristicTest.java
delete mode 100644 test/com/linkedin/drelephant/tez/heuristics/MapperTimeHeuristicTest.java
delete mode 100644 test/com/linkedin/drelephant/tez/heuristics/ReducerDataSkewHeuristicTest.java
delete mode 100644 test/com/linkedin/drelephant/tez/heuristics/ReducerGCHeuristicTest.java
delete mode 100644 test/com/linkedin/drelephant/tez/heuristics/ReducerMemoryHeuristicTest.java
delete mode 100644 test/com/linkedin/drelephant/tez/heuristics/ReducerTimeHeuristicTest.java
diff --git a/app-conf/AggregatorConf.xml b/app-conf/AggregatorConf.xml
index 1536b2d96..23586d587 100644
--- a/app-conf/AggregatorConf.xml
+++ b/app-conf/AggregatorConf.xml
@@ -33,10 +33,6 @@
mapreduce
com.linkedin.drelephant.mapreduce.MapReduceMetricsAggregator
-
- tez
- com.linkedin.drelephant.tez.TezMetricsAggregator
-
spark
com.linkedin.drelephant.spark.SparkMetricsAggregator
diff --git a/app-conf/FetcherConf.xml b/app-conf/FetcherConf.xml
index 89dafc887..1f813cf15 100644
--- a/app-conf/FetcherConf.xml
+++ b/app-conf/FetcherConf.xml
@@ -29,13 +29,6 @@
-->
-
-
- tez
- com.linkedin.drelephant.tez.fetchers.TezFetcher
-
-
-
-
- tez
- Mapper Data Skew
- com.linkedin.drelephant.tez.heuristics.MapperDataSkewHeuristic
- views.html.help.mapreduce.helpMapperSpill
-
-
-
-
- tez
- Mapper GC
- com.linkedin.drelephant.tez.heuristics.MapperGCHeuristic
- views.html.help.mapreduce.helpMapperSpill
-
-
-
-
- tez
- Mapper Time
- com.linkedin.drelephant.tez.heuristics.MapperTimeHeuristic
- views.html.help.mapreduce.helpMapperSpill
-
-
-
-
- tez
- Mapper Speed
- com.linkedin.drelephant.tez.heuristics.MapperSpeedHeuristic
- views.html.help.mapreduce.helpMapperSpill
-
-
-
-
- tez
- Mapper Memory
- com.linkedin.drelephant.tez.heuristics.MapperMemoryHeuristic
- views.html.help.mapreduce.helpMapperSpill
-
-
-
-
- tez
- Mapper Spill
- com.linkedin.drelephant.tez.heuristics.MapperSpillHeuristic
- views.html.help.mapreduce.helpMapperSpill
-
-
-
-
- tez
- Reducer Data Skew
- com.linkedin.drelephant.tez.heuristics.ReducerDataSkewHeuristic
- views.html.help.mapreduce.helpMapperSpill
-
-
-
-
- tez
- Reducer GC
- com.linkedin.drelephant.tez.heuristics.ReducerGCHeuristic
- views.html.help.mapreduce.helpMapperSpill
-
-
-
-
- tez
- Reducer Time
- com.linkedin.drelephant.tez.heuristics.ReducerTimeHeuristic
- views.html.help.mapreduce.helpMapperSpill
-
-
-
-
- tez
- Reducer Memory
- com.linkedin.drelephant.tez.heuristics.ReducerMemoryHeuristic
- views.html.help.mapreduce.helpMapperSpill
-
-
-
diff --git a/app-conf/JobTypeConf.xml b/app-conf/JobTypeConf.xml
index 9dc4aa5ba..8a4cae3eb 100644
--- a/app-conf/JobTypeConf.xml
+++ b/app-conf/JobTypeConf.xml
@@ -42,12 +42,6 @@
mapreduce
pig.script
-
- Tez
- tez
- hive.mapred.mode
-
-
Hive
mapreduce
diff --git a/app/com/linkedin/drelephant/mapreduce/fetchers/MapReduceFetcherHadoop2.java b/app/com/linkedin/drelephant/mapreduce/fetchers/MapReduceFetcherHadoop2.java
index 5a8a64425..4165971aa 100644
--- a/app/com/linkedin/drelephant/mapreduce/fetchers/MapReduceFetcherHadoop2.java
+++ b/app/com/linkedin/drelephant/mapreduce/fetchers/MapReduceFetcherHadoop2.java
@@ -20,23 +20,30 @@
import com.linkedin.drelephant.mapreduce.data.MapReduceApplicationData;
import com.linkedin.drelephant.mapreduce.data.MapReduceCounterData;
import com.linkedin.drelephant.mapreduce.data.MapReduceTaskData;
+import com.linkedin.drelephant.math.Statistics;
import com.linkedin.drelephant.configurations.fetcher.FetcherConfigurationData;
-import com.linkedin.drelephant.util.ThreadContextMR2;
import com.linkedin.drelephant.util.Utils;
import java.io.IOException;
+import java.lang.Integer;
+import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.security.authentication.client.AuthenticatedURL;
import org.apache.hadoop.security.authentication.client.AuthenticationException;
import org.apache.log4j.Logger;
import org.codehaus.jackson.JsonNode;
+import org.codehaus.jackson.map.ObjectMapper;
/**
@@ -380,3 +387,78 @@ private JsonNode getTaskFirstFailedAttempt(URL taskAllAttemptsUrl) throws IOExce
}
}
}
+
+final class ThreadContextMR2 {
+ private static final Logger logger = Logger.getLogger(ThreadContextMR2.class);
+ private static final AtomicInteger THREAD_ID = new AtomicInteger(1);
+
+ private static final ThreadLocal _LOCAL_THREAD_ID = new ThreadLocal() {
+ @Override
+ public Integer initialValue() {
+ return THREAD_ID.getAndIncrement();
+ }
+ };
+
+ private static final ThreadLocal _LOCAL_LAST_UPDATED = new ThreadLocal();
+ private static final ThreadLocal _LOCAL_UPDATE_INTERVAL = new ThreadLocal();
+
+ private static final ThreadLocal _LOCAL_DIAGNOSTIC_PATTERN = new ThreadLocal() {
+ @Override
+ public Pattern initialValue() {
+ // Example: "Task task_1443068695259_9143_m_000475 failed 1 times"
+ return Pattern.compile(
+ ".*[\\s\\u00A0]+(task_[0-9]+_[0-9]+_[m|r]_[0-9]+)[\\s\\u00A0]+.*");
+ }
+ };
+
+ private static final ThreadLocal _LOCAL_AUTH_TOKEN =
+ new ThreadLocal() {
+ @Override
+ public AuthenticatedURL.Token initialValue() {
+ _LOCAL_LAST_UPDATED.set(System.currentTimeMillis());
+ // Random an interval for each executor to avoid update token at the same time
+ _LOCAL_UPDATE_INTERVAL.set(Statistics.MINUTE_IN_MS * 30 + new Random().nextLong()
+ % (3 * Statistics.MINUTE_IN_MS));
+ logger.info("Executor " + _LOCAL_THREAD_ID.get() + " update interval " + _LOCAL_UPDATE_INTERVAL.get() * 1.0
+ / Statistics.MINUTE_IN_MS);
+ return new AuthenticatedURL.Token();
+ }
+ };
+
+ private static final ThreadLocal _LOCAL_AUTH_URL = new ThreadLocal() {
+ @Override
+ public AuthenticatedURL initialValue() {
+ return new AuthenticatedURL();
+ }
+ };
+
+ private static final ThreadLocal _LOCAL_MAPPER = new ThreadLocal() {
+ @Override
+ public ObjectMapper initialValue() {
+ return new ObjectMapper();
+ }
+ };
+
+ private ThreadContextMR2() {
+ // Empty on purpose
+ }
+
+ public static Matcher getDiagnosticMatcher(String diagnosticInfo) {
+ return _LOCAL_DIAGNOSTIC_PATTERN.get().matcher(diagnosticInfo);
+ }
+
+ public static JsonNode readJsonNode(URL url) throws IOException, AuthenticationException {
+ HttpURLConnection conn = _LOCAL_AUTH_URL.get().openConnection(url, _LOCAL_AUTH_TOKEN.get());
+ return _LOCAL_MAPPER.get().readTree(conn.getInputStream());
+ }
+
+ public static void updateAuthToken() {
+ long curTime = System.currentTimeMillis();
+ if (curTime - _LOCAL_LAST_UPDATED.get() > _LOCAL_UPDATE_INTERVAL.get()) {
+ logger.info("Executor " + _LOCAL_THREAD_ID.get() + " updates its AuthenticatedToken.");
+ _LOCAL_AUTH_TOKEN.set(new AuthenticatedURL.Token());
+ _LOCAL_AUTH_URL.set(new AuthenticatedURL());
+ _LOCAL_LAST_UPDATED.set(curTime);
+ }
+ }
+}
diff --git a/app/com/linkedin/drelephant/tez/TezMetricsAggregator.java b/app/com/linkedin/drelephant/tez/TezMetricsAggregator.java
deleted file mode 100644
index c04edd7fd..000000000
--- a/app/com/linkedin/drelephant/tez/TezMetricsAggregator.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- *
- * 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.drelephant.tez;
-
-import com.linkedin.drelephant.analysis.*;
-import com.linkedin.drelephant.configurations.aggregator.AggregatorConfigurationData;
-import com.linkedin.drelephant.tez.data.TezApplicationData;
-import org.apache.commons.io.FileUtils;
-import org.apache.log4j.Logger;
-
-/**
- * Aggregates task level metrics to application
- */
-
-public class TezMetricsAggregator implements HadoopMetricsAggregator {
-
- private static final Logger logger = Logger.getLogger(TezMetricsAggregator.class);
-
- private static final String TEZ_CONTAINER_CONFIG = "hive.tez.container.size";
- private static final String MAP_CONTAINER_CONFIG = "mapreduce.map.memory.mb";
- private static final String REDUCER_CONTAINER_CONFIG = "mapreduce.reduce.memory.mb";
- private static final String REDUCER_SLOW_START_CONFIG = "mapreduce.job.reduce.slowstart.completedmaps";
- private static final long CONTAINER_MEMORY_DEFAULT_BYTES = 2048L * FileUtils.ONE_MB;
-
- private HadoopAggregatedData _hadoopAggregatedData = null;
- private TezTaskLevelAggregatedMetrics _mapTasks;
- private TezTaskLevelAggregatedMetrics _reduceTasks;
-
- private AggregatorConfigurationData _aggregatorConfigurationData;
-
- public TezMetricsAggregator(AggregatorConfigurationData _aggregatorConfigurationData) {
- this._aggregatorConfigurationData = _aggregatorConfigurationData;
- _hadoopAggregatedData = new HadoopAggregatedData();
- }
-
- @Override
- public void aggregate(HadoopApplicationData hadoopData) {
-
- TezApplicationData data = (TezApplicationData) hadoopData;
-
- long mapTaskContainerSize = getMapContainerSize(data);
- long reduceTaskContainerSize = getReducerContainerSize(data);
-
- int reduceTaskSlowStartPercentage =
- (int) (Double.parseDouble(data.getConf().getProperty(REDUCER_SLOW_START_CONFIG)) * 100);
-
-
- //overwrite reduceTaskSlowStartPercentage to 100%. TODO: make use of the slow start percent
- reduceTaskSlowStartPercentage = 100;
-
- _mapTasks = new TezTaskLevelAggregatedMetrics(data.getMapTaskData(), mapTaskContainerSize, data.getStartTime());
-
- long reduceIdealStartTime = _mapTasks.getNthPercentileFinishTime(reduceTaskSlowStartPercentage);
-
- // Mappers list is empty
- if(reduceIdealStartTime == -1) {
- // ideal start time for reducer is infinite since it cannot start
- reduceIdealStartTime = Long.MAX_VALUE;
- }
-
- _reduceTasks = new TezTaskLevelAggregatedMetrics(data.getReduceTaskData(), reduceTaskContainerSize, reduceIdealStartTime);
-
- _hadoopAggregatedData.setResourceUsed(_mapTasks.getResourceUsed() + _reduceTasks.getResourceUsed());
- _hadoopAggregatedData.setTotalDelay(_mapTasks.getDelay() + _reduceTasks.getDelay());
- _hadoopAggregatedData.setResourceWasted(_mapTasks.getResourceWasted() + _reduceTasks.getResourceWasted());
- }
-
- @Override
- public HadoopAggregatedData getResult() {
- return _hadoopAggregatedData;
- }
-
- private long getMapContainerSize(HadoopApplicationData data) {
- try {
- long mapContainerSize = Long.parseLong(data.getConf().getProperty(TEZ_CONTAINER_CONFIG));
- if (mapContainerSize > 0)
- return mapContainerSize;
- else
- return Long.parseLong(data.getConf().getProperty(MAP_CONTAINER_CONFIG));
- } catch ( NumberFormatException ex) {
- return CONTAINER_MEMORY_DEFAULT_BYTES;
- }
- }
-
- private long getReducerContainerSize(HadoopApplicationData data) {
- try {
- long reducerContainerSize = Long.parseLong(data.getConf().getProperty(TEZ_CONTAINER_CONFIG));
- if (reducerContainerSize > 0)
- return reducerContainerSize;
- else
- return Long.parseLong(data.getConf().getProperty(REDUCER_CONTAINER_CONFIG));
- } catch ( NumberFormatException ex) {
- return CONTAINER_MEMORY_DEFAULT_BYTES;
- }
- }
-}
diff --git a/app/com/linkedin/drelephant/tez/TezTaskLevelAggregatedMetrics.java b/app/com/linkedin/drelephant/tez/TezTaskLevelAggregatedMetrics.java
deleted file mode 100644
index 0f5eaa5f6..000000000
--- a/app/com/linkedin/drelephant/tez/TezTaskLevelAggregatedMetrics.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- *
- * 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.drelephant.tez;
-
-import com.linkedin.drelephant.tez.data.TezCounterData;
-import com.linkedin.drelephant.tez.data.TezTaskData;
-import com.linkedin.drelephant.math.Statistics;
-import java.util.ArrayList;
-import java.util.List;
-import org.apache.commons.io.FileUtils;
-import org.apache.log4j.Logger;
-
-/**
- * Aggregation functionality for task level metrics
- */
-
-public class TezTaskLevelAggregatedMetrics {
-
- private static final Logger logger = Logger.getLogger(TezTaskLevelAggregatedMetrics.class);
-
- private long _delay = 0;
- private long _resourceWasted = 0;
- private long _resourceUsed = 0;
-
- private List finishTimes = new ArrayList();
- private List durations = new ArrayList();
-
- private static final double MEMORY_BUFFER = 1.5;
- private static final double CLUSTER_MEMORY_FACTOR = 2.1;
-
- /**
- * Returns the nth percentile finish job
- * @param percentile The percentile of finish job to return
- * @return The nth percentile finish job
- */
- public long getNthPercentileFinishTime(int percentile)
- {
- if(finishTimes == null || finishTimes.size() == 0 ) {
- return -1;
- }
- return Statistics.percentile(finishTimes, percentile);
- }
-
- /**
- * Constructor for TaskLevelAggregatedMetrics
- * @param taskData Array containing the task data for mappers and/or reducers
- * @param containerSize The container size of the tasks
- * @param idealStartTime The ideal start time for the task. For mappers it is the submit time, for
- * reducers, it is the time when the number of completed maps become more than
- * the slow start time.
- */
- public TezTaskLevelAggregatedMetrics(TezTaskData[] taskData, long containerSize, long idealStartTime) {
- compute(taskData, containerSize, idealStartTime);
- }
-
- /**
- * Returns the overall delay for the tasks.
- * @return The delay of the tasks.
- */
- public long getDelay() {
- return _delay;
- }
-
- /**
- * Retruns the resources wasted by all the tasks in MB Seconds
- * @return The wasted resources of all the tasks in MB Seconds
- */
- public long getResourceWasted() {
- return _resourceWasted;
- }
-
- /**
- * Returns the resource used by all the tasks in MB Seconds
- * @return The total resources used by all tasks in MB Seconds
- */
- public long getResourceUsed() {
- return _resourceUsed;
- }
-
- /**
- * Computes the aggregated metrics -> peakMemory, delay, total task duration, wasted resources and memory usage.
- * @param taskDatas
- * @param containerSize
- * @param idealStartTime
- */
- private void compute(TezTaskData[] taskDatas, long containerSize, long idealStartTime) {
-
- long peakMemoryNeed = 0;
- long taskFinishTimeMax = 0;
- long taskDurationMax = 0;
-
- // if there are zero tasks, then nothing to compute.
- if(taskDatas == null || taskDatas.length == 0) {
- return;
- }
-
- for (TezTaskData taskData: taskDatas) {
- if (!taskData.isSampled()) {
- continue;
- }
- long taskMemory = taskData.getCounters().get(TezCounterData.CounterName.PHYSICAL_MEMORY_BYTES)/ FileUtils.ONE_MB; // MB
- long taskVM = taskData.getCounters().get(TezCounterData.CounterName.VIRTUAL_MEMORY_BYTES)/ FileUtils.ONE_MB; // MB
- long taskDuration = taskData.getFinishTime() - taskData.getStartTime(); // Milliseconds
- long taskCost = (containerSize) * (taskDuration / Statistics.SECOND_IN_MS); // MB Seconds
-
- durations.add(taskDuration);
- finishTimes.add(taskData.getFinishTime());
-
- //peak Memory usage
- long memoryRequiredForVM = (long) (taskVM/CLUSTER_MEMORY_FACTOR);
- long biggerMemoryRequirement = memoryRequiredForVM > taskMemory ? memoryRequiredForVM : taskMemory;
- peakMemoryNeed = biggerMemoryRequirement > peakMemoryNeed ? biggerMemoryRequirement : peakMemoryNeed;
-
- if(taskFinishTimeMax < taskData.getFinishTime()) {
- taskFinishTimeMax = taskData.getFinishTime();
- }
-
- if(taskDurationMax < taskDuration) {
- taskDurationMax = taskDuration;
- }
- _resourceUsed += taskCost;
- }
-
- // Compute the delay in starting the task.
- _delay = taskFinishTimeMax - (idealStartTime + taskDurationMax);
-
- // invalid delay
- if(_delay < 0) {
- _delay = 0;
- }
-
- // wastedResources
- long wastedMemory = containerSize - (long) (peakMemoryNeed * MEMORY_BUFFER);
- if(wastedMemory > 0) {
- for (long duration : durations) {
- _resourceWasted += (wastedMemory) * (duration / Statistics.SECOND_IN_MS); // MB Seconds
- }
- }
- }
-
-}
\ No newline at end of file
diff --git a/app/com/linkedin/drelephant/tez/data/TezApplicationData.java b/app/com/linkedin/drelephant/tez/data/TezApplicationData.java
deleted file mode 100644
index f38f527bc..000000000
--- a/app/com/linkedin/drelephant/tez/data/TezApplicationData.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- *
- * 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.drelephant.tez.data;
-
-import com.linkedin.drelephant.analysis.ApplicationType;
-import com.linkedin.drelephant.analysis.HadoopApplicationData;
-
-import java.util.Properties;
-
-/**
- * Tez Application level data structure which hold all task data
- */
-public class TezApplicationData implements HadoopApplicationData {
-
- private static final ApplicationType APPLICATION_TYPE = new ApplicationType("TEZ");
-
- private String _appId = "";
- private Properties _conf;
- private boolean _succeeded = true;
- private TezTaskData[] _reduceTasks;
- private TezTaskData[] _mapTasks;
- private TezCounterData _counterHolder;
-
- private long _submitTime = 0;
- private long _startTime = 0;
- private long _finishTime = 0;
-
- public boolean getSucceeded() {
- return _succeeded;
- }
-
- @Override
- public String getAppId() {
- return _appId;
- }
-
- @Override
- public Properties getConf() {
- return _conf;
- }
-
- @Override
- public ApplicationType getApplicationType() {
- return APPLICATION_TYPE;
- }
-
- @Override
- public boolean isEmpty() {
- return _succeeded && getMapTaskData().length == 0 && getReduceTaskData().length == 0;
- }
-
- public TezTaskData[] getReduceTaskData() {
- return _reduceTasks;
- }
-
- public TezTaskData[] getMapTaskData() {
- return _mapTasks;
- }
-
- public long getSubmitTime() {
- return _submitTime;
- }
-
- public long getStartTime() {
- return _startTime;
- }
-
- public long getFinishTime() {
- return _finishTime;
- }
-
- public TezCounterData getCounters() {
- return _counterHolder;
- }
-
- public TezApplicationData setCounters(TezCounterData counterHolder) {
- this._counterHolder = counterHolder;
- return this;
- }
-
- public TezApplicationData setAppId(String appId) {
- this._appId = appId;
- return this;
- }
-
- public TezApplicationData setConf(Properties conf) {
- this._conf = conf;
- return this;
- }
-
- public TezApplicationData setSucceeded(boolean succeeded) {
- this._succeeded = succeeded;
- return this;
- }
-
- public TezApplicationData setReduceTaskData(TezTaskData[] reduceTasks) {
- this._reduceTasks = reduceTasks;
- return this;
- }
-
- public TezApplicationData setMapTaskData(TezTaskData[] mapTasks) {
- this._mapTasks = mapTasks;
- return this;
- }
-
- public TezApplicationData setSubmitTime(long submitTime) {
- this._submitTime = submitTime;
- return this;
- }
-
- public TezApplicationData setStartTime(long startTime) {
- this._startTime = startTime;
- return this;
- }
-
- public TezApplicationData setFinishTime(long finishTime) {
- this._finishTime = finishTime;
- return this;
- }
-
- public String toString(){
- return APPLICATION_TYPE.toString() + " " + _appId;
- }
-}
\ No newline at end of file
diff --git a/app/com/linkedin/drelephant/tez/data/TezCounterData.java b/app/com/linkedin/drelephant/tez/data/TezCounterData.java
deleted file mode 100644
index 9aac00117..000000000
--- a/app/com/linkedin/drelephant/tez/data/TezCounterData.java
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- *
- * 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.drelephant.tez.data;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Tez Counter Data defining data structure.
- */
-public class TezCounterData {
- // Map to group counters into DAG, Task and application levels.
- private final Map> _pubCounters;
-
- public String toString() {
- return _pubCounters.toString();
- }
-
- public TezCounterData() {
- _pubCounters = new HashMap>(8);
- }
-
- public long get(CounterName counterName) {
- for(Map counterGrp : _pubCounters.values()) {
- if(counterGrp.containsKey(counterName._name)) {
- return counterGrp.get(counterName._name);
- }
- }
- return 0;
- }
-
- public void set(CounterName counterName, long value) {
- set(counterName.getGroupName(), counterName.getName(), value);
- }
-
- public void set(String groupName, String counterName, long value) {
- Map counterMap = _pubCounters.get(groupName);
- if (counterMap == null) {
- counterMap = new HashMap(4);
- _pubCounters.put(groupName, counterMap);
- }
- counterMap.put(counterName, value);
- }
-
- public Set getGroupNames() {
- Set groupNames = _pubCounters.keySet();
- return Collections.unmodifiableSet(groupNames);
- }
-
- public Map getAllCountersInGroup(String groupName) {
- Map counterMap = _pubCounters.get(groupName);
- if (counterMap == null) {
- counterMap = new HashMap(1);
- }
- return counterMap;
- }
-
- public static enum GroupName {
- FileSystemCounters("org.apache.tez.common.counters.FileSystemCounter"),
- TezTask("org.apache.tez.common.counters.TaskCounter"),
- TezDag("org.apache.tez.common.counters.DAGCounter");
-
-
- String _name;
- GroupName(String name) {
- _name = name;
- }
- }
-
- public static enum CounterName {
-
- NUM_SUCCEEDED_TASKS(GroupName.TezDag, "NUM_SUCCEEDED_TASKS", "NUM_SUCCEEDED_TASKS"),
- TOTAL_LAUNCHED_TASKS(GroupName.TezDag, "TOTAL_LAUNCHED_TASKS", "TOTAL_LAUNCHED_TASKS"),
- RACK_LOCAL_TASKS(GroupName.TezDag, "RACK_LOCAL_TASKS", "RACK_LOCAL_TASKS"),
- AM_CPU_MILLISECONDS(GroupName.TezDag, "AM_CPU_MILLISECONDS", "AM_CPU_MILLISECONDS"),
- AM_GC_TIME_MILLIS(GroupName.TezDag, "AM_GC_TIME_MILLIS", "AM_GC_TIME_MILLIS"),
-
- FILE_BYTES_READ(GroupName.FileSystemCounters, "FILE_BYTES_READ", "FILE_BYTES_READ"),
- FILE_BYTES_WRITTEN(GroupName.FileSystemCounters, "FILE_BYTES_WRITTEN", "FILE_BYTES_WRITTEN"),
- FILE_READ_OPS(GroupName.FileSystemCounters, "FILE_READ_OPS", "FILE_READ_OPS"),
- FILE_LARGE_READ_OPS(GroupName.FileSystemCounters, "FILE_LARGE_READ_OPS", "FILE_LARGE_READ_OPS"),
- FILE_WRITE_OPS(GroupName.FileSystemCounters, "FILE_WRITE_OPS", "FILE_WRITE_OPS"),
- HDFS_BYTES_READ(GroupName.FileSystemCounters, "HDFS_BYTES_READ", "HDFS_BYTES_READ"),
- HDFS_BYTES_WRITTEN(GroupName.FileSystemCounters, "HDFS_BYTES_WRITTEN", "HDFS_BYTES_WRITTEN"),
- HDFS_READ_OPS(GroupName.FileSystemCounters, "HDFS_READ_OPS", "HDFS_READ_OPS"),
- HDFS_LARGE_READ_OPS(GroupName.FileSystemCounters, "HDFS_LARGE_READ_OPS", "HDFS_LARGE_READ_OPS"),
- HDFS_WRITE_OPS(GroupName.FileSystemCounters, "HDFS_WRITE_OPS", "HDFS_WRITE_OPS"),
- S3A_BYTES_READ(GroupName.FileSystemCounters, "S3A_BYTES_READ", "S3A_BYTES_READ"),
- S3A_BYTES_WRITTEN(GroupName.FileSystemCounters, "S3A_BYTES_WRITTEN", "S3A_BYTES_WRITTEN"),
- S3A_READ_OPS(GroupName.FileSystemCounters, "S3A_READ_OPS", "S3A_READ_OPS"),
- S3A_LARGE_READ_OPS(GroupName.FileSystemCounters, "S3A_LARGE_READ_OPS", "S3A_LARGE_READ_OPS"),
- S3A_WRITE_OPS(GroupName.FileSystemCounters, "S3A_WRITE_OPS", "S3_WRITE_OPS"),
- S3N_BYTES_READ(GroupName.FileSystemCounters, "S3N_BYTES_READ", "S3N_BYTES_READ"),
- S3N_BYTES_WRITTEN(GroupName.FileSystemCounters, "S3N_BYTES_WRITTEN", "S3N_BYTES_WRITTEN"),
- S3N_READ_OPS(GroupName.FileSystemCounters, "S3N_READ_OPS", "S3N_READ_OPS"),
- S3N_LARGE_READ_OPS(GroupName.FileSystemCounters, "S3N_LARGE_READ_OPS", "S3N_LARGE_READ_OPS"),
- S3N_WRITE_OPS(GroupName.FileSystemCounters, "S3N_WRITE_OPS", "S3N_WRITE_OPS"),
-
- REDUCE_INPUT_GROUPS(GroupName.TezTask, "REDUCE_INPUT_GROUPS", "REDUCE_INPUT_GROUPS"),
- REDUCE_INPUT_RECORDS(GroupName.TezTask, "REDUCE_INPUT_RECORDS", "REDUCE_INPUT_RECORDS"),
- COMBINE_INPUT_RECORDS(GroupName.TezTask, "COMBINE_INPUT_RECORDS", "COMBINE_INPUT_RECORDS"),
- SPILLED_RECORDS(GroupName.TezTask, "SPILLED_RECORDS", "SPILLED_RECORDS"),
- NUM_SHUFFLED_INPUTS(GroupName.TezTask, "NUM_SHUFFLED_INPUTS", "NUM_SHUFFLED_INPUTS"),
- NUM_SKIPPED_INPUTS(GroupName.TezTask, "NUM_SKIPPED_INPUTS", "NUM_SKIPPED_INPUTS"),
- NUM_FAILED_SHUFFLE_INPUTS(GroupName.TezTask, "NUM_FAILED_SHUFFLE_INPUTS", "NUM_FAILED_SHUFFLE_INPUTS"),
- MERGED_MAP_OUTPUTS(GroupName.TezTask, "MERGED_MAP_OUTPUTS", "MERGED_MAP_OUTPUTS"),
- GC_TIME_MILLIS(GroupName.TezTask, "GC_TIME_MILLIS", "GC_TIME_MILLIS"),
- COMMITTED_HEAP_BYTES(GroupName.TezTask, "COMMITTED_HEAP_BYTES", "COMMITTED_HEAP_BYTES"),
- INPUT_RECORDS_PROCESSED(GroupName.TezTask, "INPUT_RECORDS_PROCESSED", "INPUT_RECORDS_PROCESSED"),
- OUTPUT_RECORDS(GroupName.TezTask, "OUTPUT_RECORDS", "OUTPUT_RECORDS"),
- OUTPUT_BYTES(GroupName.TezTask, "OUTPUT_BYTES", "OUTPUT_BYTES"),
- OUTPUT_BYTES_WITH_OVERHEAD(GroupName.TezTask, "OUTPUT_BYTES_WITH_OVERHEAD", "OUTPUT_BYTES_WITH_OVERHEAD"),
- OUTPUT_BYTES_PHYSICAL(GroupName.TezTask, "OUTPUT_BYTES_PHYSICAL", "OUTPUT_BYTES_PHYSICAL"),
- ADDITIONAL_SPILLS_BYTES_WRITTEN(GroupName.TezTask, "ADDITIONAL_SPILLS_BYTES_WRITTEN", "ADDITIONAL_SPILLS_BYTES_WRITTEN"),
- ADDITIONAL_SPILLS_BYTES_READ(GroupName.TezTask, "ADDITIONAL_SPILLS_BYTES_READ", "ADDITIONAL_SPILLS_BYTES_READ"),
- ADDITIONAL_SPILL_COUNT(GroupName.TezTask, "ADDITIONAL_SPILL_COUNT", "ADDITIONAL_SPILL_COUNT"),
- SHUFFLE_BYTES(GroupName.TezTask, "SHUFFLE_BYTES", "SHUFFLE_BYTES"),
- SHUFFLE_BYTES_DECOMPRESSED(GroupName.TezTask, "SHUFFLE_BYTES_DECOMPRESSED", "SHUFFLE_BYTES_DECOMPRESSED"),
- SHUFFLE_BYTES_TO_MEM(GroupName.TezTask, "SHUFFLE_BYTES_TO_MEM", "SHUFFLE_BYTES_TO_MEM"),
- SHUFFLE_BYTES_TO_DISK(GroupName.TezTask, "SHUFFLE_BYTES_TO_DISK", "SHUFFLE_BYTES_TO_DISK"),
- SHUFFLE_BYTES_DISK_DIRECT(GroupName.TezTask, "SHUFFLE_BYTES_DISK_DIRECT", "SHUFFLE_BYTES_DISK_DIRECT"),
- NUM_MEM_TO_DISK_MERGES(GroupName.TezTask, "NUM_MEM_TO_DISK_MERGES", "NUM_MEM_TO_DISK_MERGES"),
- CPU_MILLISECONDS(GroupName.TezTask,"CPU_MILLISECONDS","CPU_MILLISECONDS"),
- PHYSICAL_MEMORY_BYTES(GroupName.TezTask,"PHYSICAL_MEMORY_BYTES","PHYSICAL_MEMORY_BYTES"),
- VIRTUAL_MEMORY_BYTES(GroupName.TezTask,"VIRTUAL_MEMORY_BYTES","VIRTUAL_MEMORY_BYTES"),
- NUM_DISK_TO_DISK_MERGES(GroupName.TezTask, "NUM_DISK_TO_DISK_MERGES", "NUM_DISK_TO_DISK_MERGES"),
- SHUFFLE_PHASE_TIME(GroupName.TezTask, "SHUFFLE_PHASE_TIME", "SHUFFLE_PHASE_TIME"),
- MERGE_PHASE_TIME(GroupName.TezTask, "MERGE_PHASE_TIME", "MERGE_PHASE_TIME"),
- FIRST_EVENT_RECEIVED(GroupName.TezTask, "FIRST_EVENT_RECEIVED", "FIRST_EVENT_RECEIVED"),
- LAST_EVENT_RECEIVED(GroupName.TezTask, "LAST_EVENT_RECEIVED", "LAST_EVENT_RECEIVED");
-
-
- GroupName _group;
- String _name;
- String _displayName;
-
- CounterName(GroupName group, String name, String displayName) {
- this._group = group;
- this._name = name;
- this._displayName = displayName;
- }
-
- static Map _counterDisplayNameMap;
- static Map _counterNameMap;
- static {
- _counterDisplayNameMap = new HashMap();
- _counterNameMap = new HashMap();
- for (CounterName cn : CounterName.values()) {
- _counterDisplayNameMap.put(cn._displayName, cn);
- _counterNameMap.put(cn._name, cn);
- }
- }
-
- public static CounterName getCounterFromName(String name) {
- if (_counterNameMap.containsKey(name)) {
- return _counterNameMap.get(name);
- }
- return null;
- }
-
- public static CounterName getCounterFromDisplayName(String displayName) {
- if (_counterDisplayNameMap.containsKey(displayName)) {
- return _counterDisplayNameMap.get(displayName);
- }
- return null;
- }
-
- public String getName() {
- return _name;
- }
-
- public String getDisplayName() {
- return _displayName;
- }
-
- public String getGroupName() {
- return _group.name();
- }
- }
-}
\ No newline at end of file
diff --git a/app/com/linkedin/drelephant/tez/data/TezTaskData.java b/app/com/linkedin/drelephant/tez/data/TezTaskData.java
deleted file mode 100644
index eb6aa832c..000000000
--- a/app/com/linkedin/drelephant/tez/data/TezTaskData.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- *
- * 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.drelephant.tez.data;
-
-/**
- * Tez Task Level metadata holding data structure
- */
-
-public class TezTaskData {
- private TezCounterData _counterHolder;
- private String _taskId;
- // The successful attempt id
- private String _attemptId;
-
- private long _totalTimeMs = 0;
- private long _shuffleTimeMs = 0;
- private long _sortTimeMs = 0;
- private long _startTime = 0;
- private long _finishTime = 0;
- private boolean _isSampled = false;
-
- //Constructor used only in Test Cases , if provided to partially assign needed time values while ignoring the others
- public TezTaskData(TezCounterData counterHolder, long[] time) {
- if(time == null || time.length<3){
- time = new long[5];
- }
- this._counterHolder = counterHolder;
- this._totalTimeMs = time[0];
- this._shuffleTimeMs = time[1];
- this._sortTimeMs = time[2];
- if (time.length > 3)
- this._startTime = time[3];
- if (time.length > 4)
- this._finishTime = time[4];
- this._isSampled = true;
- }
-
- public TezTaskData(TezCounterData counterHolder) {
- this._counterHolder = counterHolder;
- }
-
- public TezTaskData(String taskId, String taskAttemptId) {
- this._taskId = taskId;
- this._attemptId = taskAttemptId;
- }
-
- public void setCounter(TezCounterData counterHolder) {
- this._counterHolder = counterHolder;
- this._isSampled = true;
- }
-
- public void setTime(long[] time) {
- //No Validation needed here as time array will always be of fixed length 5 from upstream methods.
- this._totalTimeMs = time[0];
- this._shuffleTimeMs = time[1];
- this._sortTimeMs = time[2];
- this._startTime = time[3];
- this._finishTime = time[4];
- this._isSampled = true;
- }
-
- //Used only in Test Cases
- public void setTimeAndCounter(long[] time, TezCounterData counterHolder){
- if(time == null || time.length<3){
- time = new long[5];
- }
- this._totalTimeMs = time[0];
- this._shuffleTimeMs = time[1];
- this._sortTimeMs = time[2];
- if (time.length > 3)
- this._startTime = time[3];
- if (time.length > 4)
- this._finishTime = time[4];
- this._isSampled = true;
- this._counterHolder = counterHolder;
-
-
- }
-
- public TezCounterData getCounters() {
- return _counterHolder;
- }
-
- public long getTotalRunTimeMs() {
- return _totalTimeMs;
- }
-
- public long getCodeExecutionTimeMs() {
- return _totalTimeMs - _shuffleTimeMs - _sortTimeMs;
- }
-
- public long getShuffleTimeMs() {
- return _shuffleTimeMs;
- }
-
- public long getSortTimeMs() {
- return _sortTimeMs;
- }
-
- public boolean isSampled() {
- return _isSampled;
- }
-
- public String getTaskId() {
- return _taskId;
- }
-
- public String getAttemptId() {
- return _attemptId;
- }
-
- public long getStartTime() {
- return _startTime;
- }
-
- public long getFinishTime() {
- return _finishTime;
- }
-
- public void setTotalTimeMs(long totalTimeMs, boolean isSampled) {
- this._totalTimeMs = totalTimeMs;
- this._isSampled = isSampled;
- }
-}
diff --git a/app/com/linkedin/drelephant/tez/fetchers/TezFetcher.java b/app/com/linkedin/drelephant/tez/fetchers/TezFetcher.java
deleted file mode 100644
index 294fe20e0..000000000
--- a/app/com/linkedin/drelephant/tez/fetchers/TezFetcher.java
+++ /dev/null
@@ -1,391 +0,0 @@
-/*
- * 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.drelephant.tez.fetchers;
-
-import com.linkedin.drelephant.analysis.AnalyticJob;
-import com.linkedin.drelephant.analysis.ElephantFetcher;
-import com.linkedin.drelephant.configurations.fetcher.FetcherConfigurationData;
-import com.linkedin.drelephant.tez.data.TezApplicationData;
-import com.linkedin.drelephant.tez.data.TezCounterData;
-import com.linkedin.drelephant.tez.data.TezTaskData;
-import com.linkedin.drelephant.util.ThreadContextMR2;
-import org.apache.log4j.Logger;
-
-import org.apache.hadoop.conf.Configuration;
-import org.apache.hadoop.security.authentication.client.AuthenticationException;
-import org.codehaus.jackson.JsonNode;
-
-import java.io.IOException;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLConnection;
-import java.util.*;
-
-/**
- * Task level data mining for Tez Tasks from timeline server API
- */
-
-public class TezFetcher implements ElephantFetcher {
-
- private static final Logger logger = Logger.getLogger(TezFetcher.class);
-
- private static final String TIMELINE_SERVER_URL = "yarn.timeline-service.webapp.address";
-
- private URLFactory _urlFactory;
- private JSONFactory _jsonFactory;
- private String _timelineWebAddr;
-
- private FetcherConfigurationData _fetcherConfigurationData;
-
- public TezFetcher(FetcherConfigurationData fetcherConfData) throws IOException {
- this._fetcherConfigurationData = fetcherConfData;
- final String applicationHistoryAddr = new Configuration().get(TIMELINE_SERVER_URL);
-
- //Connection validity checked using method verifyURL(_timelineWebAddr) inside URLFactory constructor;
- _urlFactory = new URLFactory(applicationHistoryAddr);
- logger.info("Connection success.");
-
- _jsonFactory = new JSONFactory();
- _timelineWebAddr = "http://" + _timelineWebAddr + "/ws/v1/timeline/";
-
- }
-
- public TezApplicationData fetchData(AnalyticJob analyticJob) throws IOException, AuthenticationException {
-
- int maxSize = 0;
- String appId = analyticJob.getAppId();
- TezApplicationData jobData = new TezApplicationData();
- jobData.setAppId(appId);
- Properties jobConf = _jsonFactory.getProperties(_urlFactory.getApplicationURL(appId));
- jobData.setConf(jobConf);
- URL dagIdsUrl = _urlFactory.getDagURLByTezApplicationId(appId);
-
- List dagIdsByApplicationId = _jsonFactory.getDagIdsByApplicationId(dagIdsUrl);
-
- List mapperListAggregate = new ArrayList();
- List reducerListAggregate = new ArrayList();
-
- //Iterate over dagIds and choose the dagId with the highest no. of tasks/highest impact as settings changes can be made only at DAG level.
- for(String dagId : dagIdsByApplicationId){
- try {
- //set job task independent properties
-
- URL dagUrl = _urlFactory.getDagURL(dagId);
- String state = _jsonFactory.getState(dagUrl);
-
- jobData.setStartTime(_jsonFactory.getDagStartTime(dagUrl));
- jobData.setFinishTime(_jsonFactory.getDagEndTime(dagUrl));
-
- if (state.equals("SUCCEEDED")) {
- jobData.setSucceeded(true);
-
- List mapperList = new ArrayList();
- List reducerList = new ArrayList();
-
- // Fetch task data
- URL vertexListUrl = _urlFactory.getVertexListURL(dagId);
- _jsonFactory.getTaskDataAll(vertexListUrl, dagId, mapperList, reducerList);
-
- if(mapperList.size() + reducerList.size() > maxSize){
- mapperListAggregate = mapperList;
- reducerListAggregate = reducerList;
- maxSize = mapperList.size() + reducerList.size();
- }
- }
- if (state.equals("FAILED")) {
- jobData.setSucceeded(false);
- }
- }
- finally {
- ThreadContextMR2.updateAuthToken();
- }
- }
-
- TezTaskData[] mapperData = mapperListAggregate.toArray(new TezTaskData[mapperListAggregate.size()]);
- TezTaskData[] reducerData = reducerListAggregate.toArray(new TezTaskData[reducerListAggregate.size()]);
-
- TezCounterData dagCounter = _jsonFactory.getDagCounter(_urlFactory.getDagURL(_jsonFactory.getDagIdsByApplicationId(dagIdsUrl).get(0)));
-
- jobData.setCounters(dagCounter).setMapTaskData(mapperData).setReduceTaskData(reducerData);
-
- return jobData;
- }
-
- private URL getTaskListByVertexURL(String dagId, String vertexId) throws MalformedURLException {
- return _urlFactory.getTaskListByVertexURL(dagId, vertexId);
- }
-
- private URL getTaskURL(String taskId) throws MalformedURLException {
- return _urlFactory.getTasksURL(taskId);
- }
-
- private URL getTaskAttemptURL(String dagId, String taskId, String attemptId) throws MalformedURLException {
- return _urlFactory.getTaskAttemptURL(dagId, taskId, attemptId);
- }
-
- private class URLFactory {
-
- private String _timelineWebAddr;
-
- private URLFactory(String hserverAddr) throws IOException {
- _timelineWebAddr = "http://" + hserverAddr + "/ws/v1/timeline";
- verifyURL(_timelineWebAddr);
- }
-
- private void verifyURL(String url) throws IOException {
- final URLConnection connection = new URL(url).openConnection();
- // Check service availability
- connection.connect();
- return;
- }
-
- private URL getDagURLByTezApplicationId(String applicationId) throws MalformedURLException {
- return new URL(_timelineWebAddr + "/TEZ_DAG_ID?primaryFilter=applicationId:" + applicationId);
- }
-
- private URL getApplicationURL(String applicationId) throws MalformedURLException {
- return new URL(_timelineWebAddr + "/TEZ_APPLICATION/tez_" + applicationId);
- }
-
- private URL getDagURL(String dagId) throws MalformedURLException {
- return new URL(_timelineWebAddr + "/TEZ_DAG_ID/" + dagId);
- }
-
- private URL getVertexListURL(String dagId) throws MalformedURLException {
- return new URL(_timelineWebAddr + "/TEZ_VERTEX_ID?primaryFilter=TEZ_DAG_ID:" + dagId);
- }
-
- private URL getTaskListByVertexURL(String dagId, String vertexId) throws MalformedURLException {
- return new URL(_timelineWebAddr + "/TEZ_TASK_ID?primaryFilter=TEZ_DAG_ID:" + dagId +
- "&secondaryFilter=TEZ_VERTEX_ID:" + vertexId + "&limit=500000");
- }
-
- private URL getTasksURL(String taskId) throws MalformedURLException {
- return new URL(_timelineWebAddr + "/TEZ_TASK_ID/" + taskId);
- }
-
- private URL getTaskAllAttemptsURL(String dagId, String taskId) throws MalformedURLException {
- return new URL(_timelineWebAddr + "/TEZ_TASK_ATTEMPT_ID?primaryFilter=TEZ_DAG_ID:" + dagId +
- "&secondaryFilter=TEZ_TASK_ID:" + taskId);
- }
-
- private URL getTaskAttemptURL(String dagId, String taskId, String attemptId) throws MalformedURLException {
- return new URL(_timelineWebAddr + "/TEZ_TASK_ATTEMPT_ID/" + attemptId);
- }
-
- }
-
- /**
- * JSONFactory class provides functionality to parse mined job data from timeline server.
- */
-
- private class JSONFactory {
-
- private String getState(URL url) throws IOException, AuthenticationException {
- JsonNode rootNode = ThreadContextMR2.readJsonNode(url);
- return rootNode.path("otherinfo").path("status").getTextValue();
- }
-
- private Properties getProperties(URL url) throws IOException, AuthenticationException {
- Properties jobConf = new Properties();
- JsonNode rootNode = ThreadContextMR2.readJsonNode(url);
- JsonNode configs = rootNode.path("otherinfo").path("config");
- Iterator keys = configs.getFieldNames();
- String key = "";
- String value = "";
- while (keys.hasNext()) {
- key = keys.next();
- value = configs.get(key).getTextValue();
- jobConf.put(key, value);
- }
- return jobConf;
- }
-
- private List getDagIdsByApplicationId(URL dagIdsUrl) throws IOException, AuthenticationException {
- List dagIds = new ArrayList();
- JsonNode nodes = ThreadContextMR2.readJsonNode(dagIdsUrl).get("entities");
-
- for (JsonNode node : nodes) {
- String dagId = node.get("entity").getTextValue();
- dagIds.add(dagId);
- }
-
- return dagIds;
- }
-
- private TezCounterData getDagCounter(URL url) throws IOException, AuthenticationException {
- TezCounterData holder = new TezCounterData();
- JsonNode rootNode = ThreadContextMR2.readJsonNode(url);
- JsonNode groups = rootNode.path("otherinfo").path("counters").path("counterGroups");
-
- for (JsonNode group : groups) {
- for (JsonNode counter : group.path("counters")) {
- String name = counter.get("counterName").getTextValue();
- String groupName = group.get("counterGroupName").getTextValue();
- Long value = counter.get("counterValue").getLongValue();
- holder.set(groupName, name, value);
- }
- }
-
- return holder;
- }
-
- private long getDagStartTime(URL url) throws IOException, AuthenticationException {
- JsonNode rootNode = ThreadContextMR2.readJsonNode(url);
- long startTime = rootNode.path("otherinfo").get("startTime").getLongValue();
- return startTime;
- }
-
- private long getDagEndTime(URL url) throws IOException, AuthenticationException {
- JsonNode rootNode = ThreadContextMR2.readJsonNode(url);
- long endTime = rootNode.path("otherinfo").get("endTime").getLongValue();
- return endTime;
- }
-
- private void getTaskDataAll(URL vertexListUrl, String dagId, List mapperList,
- List reducerList) throws IOException, AuthenticationException {
-
- JsonNode rootVertexNode = ThreadContextMR2.readJsonNode(vertexListUrl);
- JsonNode vertices = rootVertexNode.path("entities");
- boolean isMapVertex = false;
-
- for (JsonNode vertex : vertices) {
- String vertexId = vertex.get("entity").getTextValue();
- String vertexClass = vertex.path("otherinfo").path("processorClassName").getTextValue();
-
- if (vertexClass.equals("org.apache.hadoop.hive.ql.exec.tez.MapTezProcessor"))
- isMapVertex = true;
- else if (vertexClass.equals("org.apache.hadoop.hive.ql.exec.tez.ReduceTezProcessor"))
- isMapVertex = false;
-
- URL tasksByVertexURL = getTaskListByVertexURL(dagId, vertexId);
- if(isMapVertex)
- getTaskDataByVertexId(tasksByVertexURL, dagId, vertexId, mapperList, true);
- else
- getTaskDataByVertexId(tasksByVertexURL, dagId, vertexId, reducerList, false);
- }
- }
-
- private void getTaskDataByVertexId(URL url, String dagId, String vertexId, List taskList,
- boolean isMapVertex) throws IOException, AuthenticationException {
-
- JsonNode rootNode = ThreadContextMR2.readJsonNode(url);
- JsonNode tasks = rootNode.path("entities");
- for (JsonNode task : tasks) {
- String state = task.path("otherinfo").path("status").getTextValue();
- String taskId = task.get("entity").getValueAsText();
- String attemptId = task.path("otherinfo").path("successfulAttemptId").getTextValue();
- if (state.equals("SUCCEEDED")) {
- attemptId = task.path("otherinfo").path("successfulAttemptId").getTextValue();
- }
- else{
- JsonNode firstAttempt = getTaskFirstFailedAttempt(_urlFactory.getTaskAllAttemptsURL(dagId,taskId));
- if(firstAttempt != null){
- attemptId = firstAttempt.get("entity").getTextValue();
- }
- }
-
- taskList.add(new TezTaskData(taskId, attemptId));
- }
-
- getTaskData(dagId, taskList, isMapVertex);
-
- }
-
- private JsonNode getTaskFirstFailedAttempt(URL taskAllAttemptsUrl) throws IOException, AuthenticationException {
- JsonNode rootNode = ThreadContextMR2.readJsonNode(taskAllAttemptsUrl);
- long firstAttemptFinishTime = Long.MAX_VALUE;
- JsonNode firstAttempt = null;
- JsonNode taskAttempts = rootNode.path("entities");
- for (JsonNode taskAttempt : taskAttempts) {
- String state = taskAttempt.path("otherinfo").path("counters").path("status").getTextValue();
- if (state.equals("SUCCEEDED")) {
- continue;
- }
- long finishTime = taskAttempt.path("otherinfo").path("counters").path("endTime").getLongValue();
- if( finishTime < firstAttemptFinishTime) {
- firstAttempt = taskAttempt;
- firstAttemptFinishTime = finishTime;
- }
- }
- return firstAttempt;
- }
-
-
-
- private void getTaskData(String dagId, List taskList, boolean isMapTask)
- throws IOException, AuthenticationException {
-
- for(int i=0; i {
- private static final Logger logger = Logger.getLogger(GenericDataSkewHeuristic.class);
-
- // Severity Parameters
- private static final String NUM_TASKS_SEVERITY = "num_tasks_severity";
- private static final String DEVIATION_SEVERITY = "deviation_severity";
- private static final String FILES_SEVERITY = "files_severity";
-
- // Default value of parameters
- private double[] numTasksLimits = {10, 50, 100, 200}; // Number of map or reduce tasks
- private double[] deviationLimits = {2, 4, 8, 16}; // Deviation in i/p bytes btw 2 groups
- private double[] filesLimits = {1d / 8, 1d / 4, 1d / 2, 1d}; // Fraction of HDFS Block Size
-
- private HeuristicConfigurationData _heuristicConfData;
- private List _counterNames;
-
- private void loadParameters() {
- Map paramMap = _heuristicConfData.getParamMap();
- String heuristicName = _heuristicConfData.getHeuristicName();
-
- double[] confNumTasksThreshold = Utils.getParam(paramMap.get(NUM_TASKS_SEVERITY), numTasksLimits.length);
- if (confNumTasksThreshold != null) {
- numTasksLimits = confNumTasksThreshold;
- }
- logger.info(heuristicName + " will use " + NUM_TASKS_SEVERITY + " with the following threshold settings: "
- + Arrays.toString(numTasksLimits));
-
- double[] confDeviationThreshold = Utils.getParam(paramMap.get(DEVIATION_SEVERITY), deviationLimits.length);
- if (confDeviationThreshold != null) {
- deviationLimits = confDeviationThreshold;
- }
- logger.info(heuristicName + " will use " + DEVIATION_SEVERITY + " with the following threshold settings: "
- + Arrays.toString(deviationLimits));
-
- double[] confFilesThreshold = Utils.getParam(paramMap.get(FILES_SEVERITY), filesLimits.length);
- if (confFilesThreshold != null) {
- filesLimits = confFilesThreshold;
- }
- logger.info(
- heuristicName + " will use " + FILES_SEVERITY + " with the following threshold settings: " + Arrays.toString(
- filesLimits));
- for (int i = 0; i < filesLimits.length; i++) {
- filesLimits[i] = filesLimits[i] * HDFSContext.HDFS_BLOCK_SIZE;
- }
- }
-
- public GenericDataSkewHeuristic(List counterNames, HeuristicConfigurationData heuristicConfData) {
- this._counterNames = counterNames;
- this._heuristicConfData = heuristicConfData;
- loadParameters();
- }
-
- protected abstract TezTaskData[] getTasks(TezApplicationData data);
-
- public HeuristicConfigurationData getHeuristicConfData() {
- return _heuristicConfData;
- }
-
- public HeuristicResult apply(TezApplicationData data) {
-
- if (!data.getSucceeded()) {
- return null;
- }
-
- TezTaskData[] tasks = getTasks(data);
-
- //Gathering data for checking time skew
- List timeTaken = new ArrayList();
-
- for(int i = 0; i < tasks.length; i++) {
- if (tasks[i].isSampled()) {
- timeTaken.add(tasks[i].getTotalRunTimeMs());
- }
- }
-
- long[][] groupsTime = Statistics.findTwoGroups(Longs.toArray(timeTaken));
-
- long timeAvg1 = Statistics.average(groupsTime[0]);
- long timeAvg2 = Statistics.average(groupsTime[1]);
-
- //seconds are used for calculating deviation as they provide a better idea than millisecond.
- long timeAvgSec1 = TimeUnit.MILLISECONDS.toSeconds(timeAvg1);
- long timeAvgSec2 = TimeUnit.MILLISECONDS.toSeconds(timeAvg2);
-
- long minTime = Math.min(timeAvgSec1, timeAvgSec2);
- long diffTime = Math.abs(timeAvgSec1 - timeAvgSec2);
-
- //using the same deviation limits for time skew as for data skew. It can be changed in the fututre.
- Severity severityTime = getDeviationSeverity(minTime, diffTime);
-
- //This reduces severity if number of tasks is insignificant
- severityTime = Severity.min(severityTime,
- Severity.getSeverityAscending(groupsTime[0].length, numTasksLimits[0], numTasksLimits[1], numTasksLimits[2],
- numTasksLimits[3]));
-
- //Gather data
- List inputSizes = new ArrayList();
-
- for (int i = 0; i < tasks.length; i++) {
- if (tasks[i].isSampled()) {
-
- long inputByte = 0;
- for (TezCounterData.CounterName counterName : _counterNames) {
- inputByte += tasks[i].getCounters().get(counterName);
- }
-
- inputSizes.add(inputByte);
- }
- }
-
- long[][] groups = Statistics.findTwoGroups(Longs.toArray(inputSizes));
-
- long avg1 = Statistics.average(groups[0]);
- long avg2 = Statistics.average(groups[1]);
-
- long min = Math.min(avg1, avg2);
- long diff = Math.abs(avg2 - avg1);
-
- Severity severityData = getDeviationSeverity(min, diff);
-
- //This reduces severity if the largest file sizes are insignificant
- severityData = Severity.min(severityData, getFilesSeverity(avg2));
-
- //This reduces severity if number of tasks is insignificant
- severityData = Severity.min(severityData,
- Severity.getSeverityAscending(groups[0].length, numTasksLimits[0], numTasksLimits[1], numTasksLimits[2],
- numTasksLimits[3]));
-
- Severity severity = Severity.max(severityData, severityTime);
-
- HeuristicResult result =
- new HeuristicResult(_heuristicConfData.getClassName(), _heuristicConfData.getHeuristicName(), severity,
- Utils.getHeuristicScore(severityData, tasks.length));
-
- result.addResultDetail("Data skew (Number of tasks)", Integer.toString(tasks.length));
- result.addResultDetail("Data skew (Group A)",
- groups[0].length + " tasks @ " + FileUtils.byteCountToDisplaySize(avg1) + " avg");
- result.addResultDetail("Data skew (Group B)",
- groups[1].length + " tasks @ " + FileUtils.byteCountToDisplaySize(avg2) + " avg");
-
- result.addResultDetail("Time skew (Number of tasks)", Integer.toString(tasks.length));
- result.addResultDetail("Time skew (Group A)",
- groupsTime[0].length + " tasks @ " + convertTimeMs(timeAvg1) + " avg");
- result.addResultDetail("Time skew (Group B)",
- groupsTime[1].length + " tasks @ " + convertTimeMs(timeAvg2) + " avg");
-
- return result;
- }
-
- private String convertTimeMs(long timeMs) {
- if (timeMs < 1000) {
- return Long.toString(timeMs) + " msec";
- }
- return DurationFormatUtils.formatDuration(timeMs, "HH:mm:ss") + " HH:MM:SS";
- }
-
- private Severity getDeviationSeverity(long averageMin, long averageDiff) {
- if (averageMin <= 0) {
- averageMin = 1;
- }
- long value = averageDiff / averageMin;
- return Severity.getSeverityAscending(value, deviationLimits[0], deviationLimits[1], deviationLimits[2],
- deviationLimits[3]);
- }
-
- private Severity getFilesSeverity(long value) {
- return Severity.getSeverityAscending(value, filesLimits[0], filesLimits[1], filesLimits[2], filesLimits[3]);
- }
-}
\ No newline at end of file
diff --git a/app/com/linkedin/drelephant/tez/heuristics/GenericGCHeuristic.java b/app/com/linkedin/drelephant/tez/heuristics/GenericGCHeuristic.java
deleted file mode 100644
index d5726ef71..000000000
--- a/app/com/linkedin/drelephant/tez/heuristics/GenericGCHeuristic.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright 2017 Electronic Arts Inc.
- *
- * 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.drelephant.tez.heuristics;
-
-import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
-import com.linkedin.drelephant.tez.data.TezApplicationData;
-import com.linkedin.drelephant.tez.data.TezCounterData;
-import com.linkedin.drelephant.tez.data.TezTaskData;
-import com.linkedin.drelephant.util.Utils;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import com.linkedin.drelephant.analysis.Heuristic;
-import com.linkedin.drelephant.analysis.HeuristicResult;
-import com.linkedin.drelephant.analysis.Severity;
-import com.linkedin.drelephant.math.Statistics;
-import java.util.Map;
-import org.apache.log4j.Logger;
-
-
-/**
- * Analyses garbage collection efficiency
- */
-public abstract class GenericGCHeuristic implements Heuristic {
- private static final Logger logger = Logger.getLogger(GenericGCHeuristic.class);
-
- // Severity Parameters
- private static final String GC_RATIO_SEVERITY = "gc_ratio_severity";
- private static final String RUNTIME_SEVERITY = "runtime_severity_in_min";
-
- // Default value of parameters
- private double[] gcRatioLimits = {0.01d, 0.02d, 0.03d, 0.04d}; // Garbage Collection Time / CPU Time
- private double[] runtimeLimits = {5, 10, 12, 15}; // Task Runtime in milli sec
-
- private HeuristicConfigurationData _heuristicConfData;
-
- private void loadParameters() {
- Map paramMap = _heuristicConfData.getParamMap();
- String heuristicName = _heuristicConfData.getHeuristicName();
-
- double[] confGcRatioThreshold = Utils.getParam(paramMap.get(GC_RATIO_SEVERITY), gcRatioLimits.length);
- if (confGcRatioThreshold != null) {
- gcRatioLimits = confGcRatioThreshold;
- }
- logger.info(heuristicName + " will use " + GC_RATIO_SEVERITY + " with the following threshold settings: "
- + Arrays.toString(gcRatioLimits));
-
- double[] confRuntimeThreshold = Utils.getParam(paramMap.get(RUNTIME_SEVERITY), runtimeLimits.length);
- if (confRuntimeThreshold != null) {
- runtimeLimits = confRuntimeThreshold;
- }
- logger.info(heuristicName + " will use " + RUNTIME_SEVERITY + " with the following threshold settings: "
- + Arrays.toString(runtimeLimits));
- for (int i = 0; i < runtimeLimits.length; i++) {
- runtimeLimits[i] = runtimeLimits[i] * Statistics.MINUTE_IN_MS;
- }
- }
-
- public GenericGCHeuristic(HeuristicConfigurationData heuristicConfData) {
- this._heuristicConfData = heuristicConfData;
- loadParameters();
- }
-
- protected abstract TezTaskData[] getTasks(TezApplicationData data);
-
- public HeuristicConfigurationData getHeuristicConfData() {
- return _heuristicConfData;
- }
-
- public HeuristicResult apply(TezApplicationData data) {
-
- if(!data.getSucceeded()) {
- return null;
- }
-
- TezTaskData[] tasks = getTasks(data) ;
- List gcMs = new ArrayList();
- List cpuMs = new ArrayList();
- List runtimesMs = new ArrayList();
-
- for (TezTaskData task : tasks) {
- if (task.isSampled()) {
- runtimesMs.add(task.getTotalRunTimeMs());
- gcMs.add(task.getCounters().get(TezCounterData.CounterName.GC_TIME_MILLIS));
- cpuMs.add(task.getCounters().get(TezCounterData.CounterName.CPU_MILLISECONDS));
- }
- }
-
- long avgRuntimeMs = Statistics.average(runtimesMs);
- long avgCpuMs = Statistics.average(cpuMs);
- long avgGcMs = Statistics.average(gcMs);
- double ratio = avgCpuMs != 0 ? avgGcMs*(1.0)/avgCpuMs: 0;
-
- Severity severity;
- if (tasks.length == 0) {
- severity = Severity.NONE;
- } else {
- severity = getGcRatioSeverity(avgRuntimeMs, avgCpuMs, avgGcMs);
- }
-
- HeuristicResult result = new HeuristicResult(_heuristicConfData.getClassName(),
- _heuristicConfData.getHeuristicName(), severity, Utils.getHeuristicScore(severity, tasks.length));
-
- result.addResultDetail("Number of tasks", Integer.toString(tasks.length));
- result.addResultDetail("Avg task runtime (ms)", Long.toString(avgRuntimeMs));
- result.addResultDetail("Avg task CPU time (ms)", Long.toString(avgCpuMs));
- result.addResultDetail("Avg task GC time (ms)", Long.toString(avgGcMs));
- result.addResultDetail("Task GC/CPU ratio", Double.toString(ratio));
- return result;
- }
-
- private Severity getGcRatioSeverity(long runtimeMs, long cpuMs, long gcMs) {
- double gcRatio = ((double)gcMs)/cpuMs;
- Severity ratioSeverity = Severity.getSeverityAscending(
- gcRatio, gcRatioLimits[0], gcRatioLimits[1], gcRatioLimits[2], gcRatioLimits[3]);
-
- // Severity is reduced if task runtime is insignificant
- Severity runtimeSeverity = getRuntimeSeverity(runtimeMs);
-
- return Severity.min(ratioSeverity, runtimeSeverity);
- }
-
- private Severity getRuntimeSeverity(long runtimeMs) {
- return Severity.getSeverityAscending(
- runtimeMs, runtimeLimits[0], runtimeLimits[1], runtimeLimits[2], runtimeLimits[3]);
- }
-
-}
\ No newline at end of file
diff --git a/app/com/linkedin/drelephant/tez/heuristics/GenericMemoryHeuristic.java b/app/com/linkedin/drelephant/tez/heuristics/GenericMemoryHeuristic.java
deleted file mode 100644
index 3a575092b..000000000
--- a/app/com/linkedin/drelephant/tez/heuristics/GenericMemoryHeuristic.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright 2017 Electronic Arts Inc.
- *
- * 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.drelephant.tez.heuristics;
-
-import com.google.common.base.Strings;
-import com.linkedin.drelephant.analysis.Heuristic;
-import com.linkedin.drelephant.analysis.HeuristicResult;
-import com.linkedin.drelephant.analysis.Severity;
-import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
-import com.linkedin.drelephant.math.Statistics;
-import com.linkedin.drelephant.tez.data.TezApplicationData;
-import com.linkedin.drelephant.tez.data.TezCounterData;
-import com.linkedin.drelephant.tez.data.TezTaskData;
-import com.linkedin.drelephant.util.Utils;
-import org.apache.log4j.Logger;
-
-import org.apache.commons.io.FileUtils;
-
-import java.util.*;
-
-/**
- * Analyzes mapper memory allocation and requirements
- */
-public abstract class GenericMemoryHeuristic implements Heuristic {
-
- private static final Logger logger = Logger.getLogger(GenericMemoryHeuristic.class);
-
- //Severity Parameters
- private static final String MEM_RATIO_SEVERITY = "memory_ratio_severity";
- private static final String DEFAULT_MAPPER_CONTAINER_SIZE = "2048";
- private static final String CONTAINER_MEM_DEFAULT_MB = "container_memory_default_mb";
- private String _containerMemConf;
-
- //Default Value of parameters
-
- private double [] memoryRatioLimits = {0.6d, 0.5d, 0.4d, 0.3d}; //Ratio of successful tasks
-
- private HeuristicConfigurationData _heuristicConfData;
-
- private String getContainerMemDefaultMBytes() {
- Map paramMap = _heuristicConfData.getParamMap();
- if (paramMap.containsKey(CONTAINER_MEM_DEFAULT_MB)) {
- String strValue = paramMap.get(CONTAINER_MEM_DEFAULT_MB);
- try {
- return strValue;
- }
- catch (NumberFormatException e) {
- logger.warn(CONTAINER_MEM_DEFAULT_MB + ": expected number [" + strValue + "]");
- }
- }
- return DEFAULT_MAPPER_CONTAINER_SIZE;
- }
-
- private void loadParameters() {
- Map paramMap = _heuristicConfData.getParamMap();
- String heuristicName = _heuristicConfData.getHeuristicName();
-
- double[] confSuccessRatioLimits = Utils.getParam(paramMap.get(MEM_RATIO_SEVERITY), memoryRatioLimits.length);
- if (confSuccessRatioLimits != null) {
- memoryRatioLimits = confSuccessRatioLimits;
- }
- logger.info(heuristicName + " will use " + MEM_RATIO_SEVERITY + " with the following threshold settings: "
- + Arrays.toString(memoryRatioLimits));
-
-
- }
-
- public GenericMemoryHeuristic(String containerMemConf, HeuristicConfigurationData heuristicConfData) {
- this._containerMemConf = containerMemConf;
- this._heuristicConfData = heuristicConfData;
- loadParameters();
- }
-
- public HeuristicConfigurationData getHeuristicConfData() {
- return _heuristicConfData;
- }
-
- protected abstract TezTaskData[] getTasks(TezApplicationData data);
-
- public HeuristicResult apply(TezApplicationData data) {
- if(!data.getSucceeded()) {
- return null;
- }
- TezTaskData[] tasks = getTasks(data);
-
-
- List totalPhysicalMemory = new LinkedList();
- List totalVirtualMemory = new LinkedList();
- List runTime = new LinkedList();
-
- for (TezTaskData task : tasks) {
-
- if (task.isSampled()) {
- totalPhysicalMemory.add(task.getCounters().get(TezCounterData.CounterName.PHYSICAL_MEMORY_BYTES));
- totalVirtualMemory.add(task.getCounters().get(TezCounterData.CounterName.VIRTUAL_MEMORY_BYTES));
- runTime.add(task.getTotalRunTimeMs());
- }
-
-
- }
-
- long averagePMem = Statistics.average(totalPhysicalMemory);
- long averageVMem = Statistics.average(totalVirtualMemory);
- long maxPMem;
- long minPMem;
- try{
- maxPMem = Collections.max(totalPhysicalMemory);
- minPMem = Collections.min(totalPhysicalMemory);
-
- }
- catch(Exception exception){
- maxPMem = 0;
- minPMem = 0;
- }
- long averageRunTime = Statistics.average(runTime);
-
- String containerSizeStr;
-
- if(!Strings.isNullOrEmpty(data.getConf().getProperty(_containerMemConf))){
- containerSizeStr = data.getConf().getProperty(_containerMemConf);
- }
- else {
- containerSizeStr = getContainerMemDefaultMBytes();
- }
-
- long containerSize = Long.valueOf(containerSizeStr) * FileUtils.ONE_MB;
-
- double averageMemMb = (double)((averagePMem) /FileUtils.ONE_MB) ;
-
- double ratio = averageMemMb / ((double)(containerSize / FileUtils.ONE_MB));
-
- Severity severity ;
-
- if(tasks.length == 0){
- severity = Severity.NONE;
- }
- else{
- severity = getMemoryRatioSeverity(ratio);
- }
-
- HeuristicResult result = new HeuristicResult(_heuristicConfData.getClassName(),
- _heuristicConfData.getHeuristicName(), severity, Utils.getHeuristicScore(severity, tasks.length));
-
- result.addResultDetail("Number of tasks", Integer.toString(tasks.length));
- result.addResultDetail("Maximum Physical Memory (MB)",
- tasks.length == 0 ? "0" : Long.toString(maxPMem/FileUtils.ONE_MB));
- result.addResultDetail("Minimum Physical memory (MB)",
- tasks.length == 0 ? "0" : Long.toString(minPMem/FileUtils.ONE_MB));
- result.addResultDetail("Average Physical Memory (MB)",
- tasks.length == 0 ? "0" : Long.toString(averagePMem/FileUtils.ONE_MB));
- result.addResultDetail("Average Virtual Memory (MB)",
- tasks.length == 0 ? "0" : Long.toString(averageVMem/FileUtils.ONE_MB));
- result.addResultDetail("Average Task RunTime",
- tasks.length == 0 ? "0" : Statistics.readableTimespan(averageRunTime));
- result.addResultDetail("Requested Container Memory (MB)",
- (tasks.length == 0 || containerSize == 0 || containerSize == -1) ? "0" : String.valueOf(containerSize / FileUtils.ONE_MB));
-
-
- return result;
-
- }
-
- private Severity getMemoryRatioSeverity(double ratio) {
- return Severity.getSeverityDescending(
- ratio, memoryRatioLimits[0], memoryRatioLimits[1], memoryRatioLimits[2], memoryRatioLimits[3]);
- }
-
-
-
-
-}
\ No newline at end of file
diff --git a/app/com/linkedin/drelephant/tez/heuristics/MapperDataSkewHeuristic.java b/app/com/linkedin/drelephant/tez/heuristics/MapperDataSkewHeuristic.java
deleted file mode 100644
index b17ebe3b2..000000000
--- a/app/com/linkedin/drelephant/tez/heuristics/MapperDataSkewHeuristic.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2017 Electronic Arts Inc.
- *
- * 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.drelephant.tez.heuristics;
-
-
-import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
-import com.linkedin.drelephant.tez.data.TezApplicationData;
-import com.linkedin.drelephant.tez.data.TezCounterData;
-import com.linkedin.drelephant.tez.data.TezTaskData;
-
-import java.util.Arrays;
-import org.apache.log4j.Logger;
-
-
-/**
- * This Heuristic analyses the skewness in the task input data
- */
-public class MapperDataSkewHeuristic extends GenericDataSkewHeuristic {
- private static final Logger logger = Logger.getLogger(MapperDataSkewHeuristic.class);
-
- public MapperDataSkewHeuristic(HeuristicConfigurationData heuristicConfData) {
- super(Arrays.asList(
- TezCounterData.CounterName.HDFS_BYTES_READ,
- TezCounterData.CounterName.S3A_BYTES_READ,
- TezCounterData.CounterName.S3N_BYTES_READ
- ), heuristicConfData);
- }
-
- @Override
- protected TezTaskData[] getTasks(TezApplicationData data) {
- return data.getMapTaskData();
- }
-}
\ No newline at end of file
diff --git a/app/com/linkedin/drelephant/tez/heuristics/MapperGCHeuristic.java b/app/com/linkedin/drelephant/tez/heuristics/MapperGCHeuristic.java
deleted file mode 100644
index 3a82ae5f9..000000000
--- a/app/com/linkedin/drelephant/tez/heuristics/MapperGCHeuristic.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2017 Electronic Arts Inc.
- *
- * 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.drelephant.tez.heuristics;
-
-
-import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
-import com.linkedin.drelephant.tez.data.TezApplicationData;
-import com.linkedin.drelephant.tez.data.TezCounterData;
-import com.linkedin.drelephant.tez.data.TezTaskData;
-import com.linkedin.drelephant.util.Utils;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import com.linkedin.drelephant.analysis.Heuristic;
-import com.linkedin.drelephant.analysis.HeuristicResult;
-import com.linkedin.drelephant.analysis.Severity;
-import com.linkedin.drelephant.math.Statistics;
-import java.util.Map;
-import org.apache.log4j.Logger;
-
-
-/**
- * Analyses garbage collection efficiency
- */
-public class MapperGCHeuristic extends GenericGCHeuristic{
- private static final Logger logger = Logger.getLogger(MapperGCHeuristic.class);
-
- public MapperGCHeuristic(HeuristicConfigurationData heuristicConfData) {
- super(heuristicConfData);
- }
-
- @Override
- protected TezTaskData[] getTasks(TezApplicationData data) {
- return data.getMapTaskData();
- }
-
-}
\ No newline at end of file
diff --git a/app/com/linkedin/drelephant/tez/heuristics/MapperMemoryHeuristic.java b/app/com/linkedin/drelephant/tez/heuristics/MapperMemoryHeuristic.java
deleted file mode 100644
index 96d5bb12e..000000000
--- a/app/com/linkedin/drelephant/tez/heuristics/MapperMemoryHeuristic.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2017 Electronic Arts Inc.
- *
- * 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.drelephant.tez.heuristics;
-
-import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
-
-import com.linkedin.drelephant.tez.data.TezApplicationData;
-
-import com.linkedin.drelephant.tez.data.TezTaskData;
-
-import org.apache.log4j.Logger;
-
-/**
- * Analyzes mapper memory allocation and requirements
- */
-public class MapperMemoryHeuristic extends GenericMemoryHeuristic {
-
- private static final Logger logger = Logger.getLogger(MapperMemoryHeuristic.class);
-
- public static final String MAPPER_MEMORY_CONF = "mapreduce.map.memory.mb";
-
- public MapperMemoryHeuristic(HeuristicConfigurationData __heuristicConfData){
- super(MAPPER_MEMORY_CONF, __heuristicConfData);
- }
-
- @Override
- protected TezTaskData[] getTasks(TezApplicationData data) {
- return data.getMapTaskData();
- }
-
-}
\ No newline at end of file
diff --git a/app/com/linkedin/drelephant/tez/heuristics/MapperSpeedHeuristic.java b/app/com/linkedin/drelephant/tez/heuristics/MapperSpeedHeuristic.java
deleted file mode 100644
index 1694e32ea..000000000
--- a/app/com/linkedin/drelephant/tez/heuristics/MapperSpeedHeuristic.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright 2017 Electronic Arts Inc.
- *
- * 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.drelephant.tez.heuristics;
-
-
-import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
-import com.linkedin.drelephant.tez.data.TezApplicationData;
-import com.linkedin.drelephant.tez.data.TezCounterData;
-import com.linkedin.drelephant.tez.data.TezTaskData;
-import com.linkedin.drelephant.util.Utils;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import com.linkedin.drelephant.analysis.HDFSContext;
-import com.linkedin.drelephant.analysis.Heuristic;
-import com.linkedin.drelephant.analysis.HeuristicResult;
-import com.linkedin.drelephant.analysis.Severity;
-import com.linkedin.drelephant.math.Statistics;
-
-import java.util.Map;
-
-import org.apache.commons.io.FileUtils;
-import org.apache.log4j.Logger;
-
-/**
- * Analyzes mapper task speed and efficiency
- */
-public class MapperSpeedHeuristic implements Heuristic {
- private static final Logger logger = Logger.getLogger(MapperSpeedHeuristic.class);
-
- // Severity parameters.
- private static final String DISK_SPEED_SEVERITY = "disk_speed_severity";
- private static final String RUNTIME_SEVERITY = "runtime_severity_in_min";
-
- // Default value of parameters
- private double[] diskSpeedLimits = {1d/2, 1d/4, 1d/8, 1d/32}; // Fraction of HDFS block size
- private double[] runtimeLimits = {5, 10, 15, 30}; // The Map task runtime in milli sec
-
- private List _counterNames = Arrays.asList(
- TezCounterData.CounterName.HDFS_BYTES_READ,
- TezCounterData.CounterName.S3A_BYTES_READ,
- TezCounterData.CounterName.S3N_BYTES_READ
- );
-
- private HeuristicConfigurationData _heuristicConfData;
-
- private void loadParameters() {
- Map paramMap = _heuristicConfData.getParamMap();
- String heuristicName = _heuristicConfData.getHeuristicName();
-
- double[] confDiskSpeedThreshold = Utils.getParam(paramMap.get(DISK_SPEED_SEVERITY), diskSpeedLimits.length);
- if (confDiskSpeedThreshold != null) {
- diskSpeedLimits = confDiskSpeedThreshold;
- }
- logger.info(heuristicName + " will use " + DISK_SPEED_SEVERITY + " with the following threshold settings: "
- + Arrays.toString(diskSpeedLimits));
- for (int i = 0; i < diskSpeedLimits.length; i++) {
- diskSpeedLimits[i] = diskSpeedLimits[i] * HDFSContext.DISK_READ_SPEED;
- }
-
- double[] confRuntimeThreshold = Utils.getParam(paramMap.get(RUNTIME_SEVERITY), runtimeLimits.length);
- if (confRuntimeThreshold != null) {
- runtimeLimits = confRuntimeThreshold;
- }
- logger.info(heuristicName + " will use " + RUNTIME_SEVERITY + " with the following threshold settings: " + Arrays
- .toString(runtimeLimits));
- for (int i = 0; i < runtimeLimits.length; i++) {
- runtimeLimits[i] = runtimeLimits[i] * Statistics.MINUTE_IN_MS;
- }
- }
-
- public MapperSpeedHeuristic(HeuristicConfigurationData heuristicConfData) {
- this._heuristicConfData = heuristicConfData;
- loadParameters();
- }
-
- public HeuristicConfigurationData getHeuristicConfData() {
- return _heuristicConfData;
- }
-
- public HeuristicResult apply(TezApplicationData data) {
-
- if(!data.getSucceeded()) {
- return null;
- }
-
- TezTaskData[] tasks = data.getMapTaskData();
-
- List inputSizes = new ArrayList();
- List speeds = new ArrayList();
- List runtimesMs = new ArrayList();
-
- for (TezTaskData task : tasks) {
-
- if (task.isSampled()) {
-
- long inputBytes = 0;
-
- for (TezCounterData.CounterName counterName: _counterNames) {
- inputBytes += task.getCounters().get(counterName);
- }
-
- long runtimeMs = task.getTotalRunTimeMs();
- inputSizes.add(inputBytes);
- runtimesMs.add(runtimeMs);
- //Speed is records per second
- speeds.add((1000 * inputBytes) / (runtimeMs));
- }
- }
-
- long medianSpeed;
- long medianSize;
- long medianRuntimeMs;
-
- if (tasks.length != 0) {
- medianSpeed = Statistics.median(speeds);
- medianSize = Statistics.median(inputSizes);
- medianRuntimeMs = Statistics.median(runtimesMs);
- } else {
- medianSpeed = 0;
- medianSize = 0;
- medianRuntimeMs = 0;
- }
-
- Severity severity = getDiskSpeedSeverity(medianSpeed);
-
- //This reduces severity if task runtime is insignificant
- severity = Severity.min(severity, getRuntimeSeverity(medianRuntimeMs));
-
- HeuristicResult result = new HeuristicResult(_heuristicConfData.getClassName(),
- _heuristicConfData.getHeuristicName(), severity, Utils.getHeuristicScore(severity, tasks.length));
-
- result.addResultDetail("Number of tasks", Integer.toString(tasks.length));
- result.addResultDetail("Median task input ", FileUtils.byteCountToDisplaySize(medianSize));
- result.addResultDetail("Median task runtime", Statistics.readableTimespan(medianRuntimeMs));
- result.addResultDetail("Median task speed", FileUtils.byteCountToDisplaySize(medianSpeed) + "/s");
-
- return result;
- }
-
- private Severity getDiskSpeedSeverity(long speed) {
- return Severity.getSeverityDescending(
- speed, diskSpeedLimits[0], diskSpeedLimits[1], diskSpeedLimits[2], diskSpeedLimits[3]);
- }
-
- private Severity getRuntimeSeverity(long runtimeMs) {
- return Severity.getSeverityAscending(
- runtimeMs, runtimeLimits[0], runtimeLimits[1], runtimeLimits[2], runtimeLimits[3]);
- }
-}
\ No newline at end of file
diff --git a/app/com/linkedin/drelephant/tez/heuristics/MapperSpillHeuristic.java b/app/com/linkedin/drelephant/tez/heuristics/MapperSpillHeuristic.java
deleted file mode 100644
index f7ea997e5..000000000
--- a/app/com/linkedin/drelephant/tez/heuristics/MapperSpillHeuristic.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * 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.drelephant.tez.heuristics;
-
-import com.linkedin.drelephant.analysis.Heuristic;
-import com.linkedin.drelephant.analysis.HeuristicResult;
-import com.linkedin.drelephant.analysis.Severity;
-import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
-import com.linkedin.drelephant.tez.data.TezCounterData;
-import com.linkedin.drelephant.tez.data.TezApplicationData;
-import com.linkedin.drelephant.tez.data.TezTaskData;
-import com.linkedin.drelephant.util.Utils;
-import org.apache.log4j.Logger;
-
-
-import java.util.Arrays;
-import java.util.Map;
-
-/**
- * Analyzes mapper task data spill rates
- */
-public class MapperSpillHeuristic implements Heuristic {
- private static final Logger logger = Logger.getLogger(MapperSpillHeuristic.class);
- private static final long THRESHOLD_SPILL_FACTOR = 10000;
-
- // Severity parameters.
- private static final String SPILL_SEVERITY = "spill_severity";
- private static final String NUM_TASKS_SEVERITY = "num_tasks_severity";
-
- // Default value of parameters
- private double[] numTasksLimits = {50, 100, 500, 1000}; // Number of Map tasks.
- private double[] spillLimits = {2.01d, 2.2d, 2.5d, 3.0d}; // Records spilled/total output records
-
- private HeuristicConfigurationData _heuristicConfData;
-
- private void loadParameters() {
- Map paramMap = _heuristicConfData.getParamMap();
- String heuristicName = _heuristicConfData.getHeuristicName();
-
- double[] confNumTasksThreshold = Utils.getParam(paramMap.get(NUM_TASKS_SEVERITY), numTasksLimits.length);
- if (confNumTasksThreshold != null) {
- numTasksLimits = confNumTasksThreshold;
- }
- logger.info(heuristicName + " will use " + NUM_TASKS_SEVERITY + " with the following threshold settings: "
- + Arrays.toString(numTasksLimits));
-
- double[] confSpillThreshold = Utils.getParam(paramMap.get(SPILL_SEVERITY), spillLimits.length);
- if (confSpillThreshold != null) {
- spillLimits = confSpillThreshold;
- }
- logger.info(heuristicName + " will use " + SPILL_SEVERITY + " with the following threshold settings: " + Arrays
- .toString(spillLimits));
- for (int i = 0; i < spillLimits.length; i++) {
- spillLimits[i] = spillLimits[i] * THRESHOLD_SPILL_FACTOR;
- }
- }
-
- public MapperSpillHeuristic(HeuristicConfigurationData heuristicConfData) {
- this._heuristicConfData = heuristicConfData;
- loadParameters();
- }
-
- @Override
- public HeuristicConfigurationData getHeuristicConfData() {
- return _heuristicConfData;
- }
-
- @Override
- public HeuristicResult apply(TezApplicationData data) {
- if(!data.getSucceeded()) {
- return null;
- }
- TezTaskData[] tasks = data.getMapTaskData();
-
- long totalSpills = 0;
- long totalOutputRecords = 0;
- double ratioSpills = 0.0;
-
- for (TezTaskData task : tasks) {
-
- if (task.isSampled()) {
- totalSpills += task.getCounters().get(TezCounterData.CounterName.SPILLED_RECORDS);
- totalOutputRecords += task.getCounters().get(TezCounterData.CounterName.OUTPUT_RECORDS);
- }
- }
-
- //If both totalSpills and totalOutputRecords are zero then set ratioSpills to zero.
- if (totalSpills == 0) {
- ratioSpills = 0;
- } else {
- ratioSpills = (double) totalSpills / (double) totalOutputRecords;
- }
-
- Severity severity = getSpillSeverity(ratioSpills);
-
- // Severity is reduced if number of tasks is small
- Severity taskSeverity = getNumTasksSeverity(tasks.length);
- severity = Severity.min(severity, taskSeverity);
-
- HeuristicResult result = new HeuristicResult(_heuristicConfData.getClassName(),
- _heuristicConfData.getHeuristicName(), severity, Utils.getHeuristicScore(severity, tasks.length));
-
- result.addResultDetail("Number of tasks", Integer.toString(tasks.length));
- result.addResultDetail("Avg spilled records per task",
- tasks.length == 0 ? "0" : Long.toString(totalSpills / tasks.length));
- result.addResultDetail("Avg output records per task",
- tasks.length == 0 ? "0" : Long.toString(totalOutputRecords / tasks.length));
- result.addResultDetail("Ratio of spilled records to output records", Double.toString(ratioSpills));
-
- return result;
-
- }
-
- private Severity getSpillSeverity(double ratioSpills) {
-
- long normalizedSpillRatio = 0;
- //Normalize the ratio to integer.
- normalizedSpillRatio = (long) (ratioSpills * THRESHOLD_SPILL_FACTOR);
-
- return Severity.getSeverityAscending(
- normalizedSpillRatio, spillLimits[0], spillLimits[1], spillLimits[2], spillLimits[3]);
- }
-
- private Severity getNumTasksSeverity(long numTasks) {
- return Severity.getSeverityAscending(
- numTasks, numTasksLimits[0], numTasksLimits[1], numTasksLimits[2], numTasksLimits[3]);
- }
-
-
-}
diff --git a/app/com/linkedin/drelephant/tez/heuristics/MapperTimeHeuristic.java b/app/com/linkedin/drelephant/tez/heuristics/MapperTimeHeuristic.java
deleted file mode 100644
index 838bd7a59..000000000
--- a/app/com/linkedin/drelephant/tez/heuristics/MapperTimeHeuristic.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright 2017 Electronic Arts Inc.
- *
- * 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.drelephant.tez.heuristics;
-
-
-import com.linkedin.drelephant.analysis.Heuristic;
-import com.linkedin.drelephant.analysis.HeuristicResult;
-import com.linkedin.drelephant.analysis.Severity;
-import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
-import com.linkedin.drelephant.tez.data.TezApplicationData;
-import com.linkedin.drelephant.tez.data.TezCounterData;
-import com.linkedin.drelephant.tez.data.TezTaskData;
-import com.linkedin.drelephant.util.Utils;
-import org.apache.commons.io.FileUtils;
-import org.apache.log4j.Logger;
-
-import com.linkedin.drelephant.math.Statistics;
-
-import java.util.*;
-
-/**
- * Analyzes mapper task runtimes
- */
-public class MapperTimeHeuristic implements Heuristic {
-
- private static final Logger logger = Logger.getLogger(MapperTimeHeuristic.class);
-
- // Severity parameters.
- private static final String SHORT_RUNTIME_SEVERITY = "short_runtime_severity_in_min";
- private static final String LONG_RUNTIME_SEVERITY = "long_runtime_severity_in_min";
- private static final String NUM_TASKS_SEVERITY = "num_tasks_severity";
-
- // Default value of parameters
- private double[] shortRuntimeLimits = {10, 4, 2, 1}; // Limits(ms) for tasks with shorter runtime
- private double[] longRuntimeLimits = {15, 30, 60, 120}; // Limits(ms) for tasks with longer runtime
- private double[] numTasksLimits = {50, 101, 500, 1000}; // Number of Map tasks.
-
- private List _counterNames = Arrays.asList(
- TezCounterData.CounterName.HDFS_BYTES_READ,
- TezCounterData.CounterName.S3A_BYTES_READ,
- TezCounterData.CounterName.S3N_BYTES_READ
- );
-
- private HeuristicConfigurationData _heuristicConfData;
-
- private void loadParameters() {
- Map paramMap = _heuristicConfData.getParamMap();
- String heuristicName = _heuristicConfData.getHeuristicName();
-
- double[] confShortThreshold = Utils.getParam(paramMap.get(SHORT_RUNTIME_SEVERITY), shortRuntimeLimits.length);
- if (confShortThreshold != null) {
- shortRuntimeLimits = confShortThreshold;
- }
- logger.info(heuristicName + " will use " + SHORT_RUNTIME_SEVERITY + " with the following threshold settings: "
- + Arrays.toString(shortRuntimeLimits));
- for (int i = 0; i < shortRuntimeLimits.length; i++) {
- shortRuntimeLimits[i] = shortRuntimeLimits[i] * Statistics.MINUTE_IN_MS;
- }
-
- double[] confLongThreshold = Utils.getParam(paramMap.get(LONG_RUNTIME_SEVERITY), longRuntimeLimits.length);
- if (confLongThreshold != null) {
- longRuntimeLimits = confLongThreshold;
- }
- logger.info(heuristicName + " will use " + LONG_RUNTIME_SEVERITY + " with the following threshold settings: "
- + Arrays.toString(longRuntimeLimits));
- for (int i = 0; i < longRuntimeLimits.length; i++) {
- longRuntimeLimits[i] = longRuntimeLimits[i] * Statistics.MINUTE_IN_MS;
- }
-
- double[] confNumTasksThreshold = Utils.getParam(paramMap.get(NUM_TASKS_SEVERITY), numTasksLimits.length);
- if (confNumTasksThreshold != null) {
- numTasksLimits = confNumTasksThreshold;
- }
- logger.info(heuristicName + " will use " + NUM_TASKS_SEVERITY + " with the following threshold settings: " + Arrays
- .toString(numTasksLimits));
-
-
- }
-
- public MapperTimeHeuristic(HeuristicConfigurationData heuristicConfData) {
- this._heuristicConfData = heuristicConfData;
- loadParameters();
- }
-
- public HeuristicConfigurationData getHeuristicConfData() {
- return _heuristicConfData;
- }
-
- public HeuristicResult apply(TezApplicationData data) {
- if(!data.getSucceeded()) {
- return null;
- }
- TezTaskData[] tasks = data.getMapTaskData();
-
- List inputSizes = new ArrayList();
- List runtimesMs = new ArrayList();
- long taskMinMs = Long.MAX_VALUE;
- long taskMaxMs = 0;
-
- for (TezTaskData task : tasks) {
-
- if (task.isSampled()) {
- long inputByte = 0;
- for (TezCounterData.CounterName counterName: _counterNames) {
- inputByte += task.getCounters().get(counterName);
- }
- inputSizes.add(inputByte);
- long taskTime = task.getTotalRunTimeMs();
- runtimesMs.add(taskTime);
- taskMinMs = Math.min(taskMinMs, taskTime);
- taskMaxMs = Math.max(taskMaxMs, taskTime);
- }
- }
-
- if(taskMinMs == Long.MAX_VALUE) {
- taskMinMs = 0;
- }
-
- long averageSize = Statistics.average(inputSizes);
- long averageTimeMs = Statistics.average(runtimesMs);
-
- Severity shortTaskSeverity = shortTaskSeverity(tasks.length, averageTimeMs);
- Severity longTaskSeverity = longTaskSeverity(tasks.length, averageTimeMs);
- Severity severity = Severity.max(shortTaskSeverity, longTaskSeverity);
-
- HeuristicResult result = new HeuristicResult(_heuristicConfData.getClassName(),
- _heuristicConfData.getHeuristicName(), severity, Utils.getHeuristicScore(severity, tasks.length));
-
- result.addResultDetail("Number of tasks", Integer.toString(tasks.length));
- result.addResultDetail("Average task input size", FileUtils.byteCountToDisplaySize(averageSize));
- result.addResultDetail("Average task runtime", Statistics.readableTimespan(averageTimeMs));
- result.addResultDetail("Max task runtime", Statistics.readableTimespan(taskMaxMs));
- result.addResultDetail("Min task runtime", Statistics.readableTimespan(taskMinMs));
-
- return result;
- }
-
- private Severity shortTaskSeverity(long numTasks, long averageTimeMs) {
- // We want to identify jobs with short task runtime
- Severity severity = getShortRuntimeSeverity(averageTimeMs);
- // Severity is reduced if number of tasks is small.
- Severity numTaskSeverity = getNumTasksSeverity(numTasks);
- return Severity.min(severity, numTaskSeverity);
- }
-
- private Severity longTaskSeverity(long numTasks, long averageTimeMs) {
- // We want to identify jobs with long task runtime. Severity is NOT reduced if num of tasks is large
- return getLongRuntimeSeverity(averageTimeMs);
- }
-
- private Severity getShortRuntimeSeverity(long runtimeMs) {
- return Severity.getSeverityDescending(
- runtimeMs, shortRuntimeLimits[0], shortRuntimeLimits[1], shortRuntimeLimits[2], shortRuntimeLimits[3]);
- }
-
- private Severity getLongRuntimeSeverity(long runtimeMs) {
- return Severity.getSeverityAscending(
- runtimeMs, longRuntimeLimits[0], longRuntimeLimits[1], longRuntimeLimits[2], longRuntimeLimits[3]);
- }
-
- private Severity getNumTasksSeverity(long numTasks) {
- return Severity.getSeverityAscending(
- numTasks, numTasksLimits[0], numTasksLimits[1], numTasksLimits[2], numTasksLimits[3]);
- }
-
-
-
-}
diff --git a/app/com/linkedin/drelephant/tez/heuristics/ReducerDataSkewHeuristic.java b/app/com/linkedin/drelephant/tez/heuristics/ReducerDataSkewHeuristic.java
deleted file mode 100644
index 3a01448d4..000000000
--- a/app/com/linkedin/drelephant/tez/heuristics/ReducerDataSkewHeuristic.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2017 Electronic Arts Inc.
- *
- * 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.drelephant.tez.heuristics;
-
-import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
-import com.linkedin.drelephant.tez.data.TezApplicationData;
-import com.linkedin.drelephant.tez.data.TezCounterData;
-import com.linkedin.drelephant.tez.data.TezTaskData;
-import org.apache.log4j.Logger;
-
-import java.util.Arrays;
-
-
-/**
- * This Heuristic analyses the skewness in the task input data
- */
-public class ReducerDataSkewHeuristic extends GenericDataSkewHeuristic {
- private static final Logger logger = Logger.getLogger(ReducerDataSkewHeuristic.class);
-
- public ReducerDataSkewHeuristic(HeuristicConfigurationData heuristicConfData) {
- super(Arrays.asList(TezCounterData.CounterName.SHUFFLE_BYTES), heuristicConfData);
- }
-
- @Override
- protected TezTaskData[] getTasks(TezApplicationData data) {
- return data.getReduceTaskData();
- }
-}
\ No newline at end of file
diff --git a/app/com/linkedin/drelephant/tez/heuristics/ReducerGCHeuristic.java b/app/com/linkedin/drelephant/tez/heuristics/ReducerGCHeuristic.java
deleted file mode 100644
index 3ea3bdac4..000000000
--- a/app/com/linkedin/drelephant/tez/heuristics/ReducerGCHeuristic.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2017 Electronic Arts Inc.
- *
- * 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.drelephant.tez.heuristics;
-
-import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
-import com.linkedin.drelephant.tez.data.TezApplicationData;
-import com.linkedin.drelephant.tez.data.TezTaskData;
-import org.apache.log4j.Logger;
-
-
-/**
- * Analyses garbage collection efficiency
- */
-public class ReducerGCHeuristic extends GenericGCHeuristic {
-
- private static final Logger logger = Logger.getLogger(ReducerGCHeuristic.class);
-
- public ReducerGCHeuristic(HeuristicConfigurationData heuristicConfData) {
- super(heuristicConfData);
- }
-
- @Override
- protected TezTaskData[] getTasks(TezApplicationData data) {
- return data.getReduceTaskData();
- }
-
-}
diff --git a/app/com/linkedin/drelephant/tez/heuristics/ReducerMemoryHeuristic.java b/app/com/linkedin/drelephant/tez/heuristics/ReducerMemoryHeuristic.java
deleted file mode 100644
index 41fc6fa1d..000000000
--- a/app/com/linkedin/drelephant/tez/heuristics/ReducerMemoryHeuristic.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2017 Electronic Arts Inc.
- *
- * 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.drelephant.tez.heuristics;
-
-
-import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
-import com.linkedin.drelephant.tez.data.TezApplicationData;
-import com.linkedin.drelephant.tez.data.TezTaskData;
-import org.apache.log4j.Logger;
-
-/**
- * Analyzes reducer memory allocation and requirements
- */
-
-public class ReducerMemoryHeuristic extends GenericMemoryHeuristic {
-
- private static final Logger logger = Logger.getLogger(ReducerMemoryHeuristic.class);
-
- public static final String REDUCER_MEMORY_CONF = "mapreduce.reduce.memory.mb";
-
- public ReducerMemoryHeuristic(HeuristicConfigurationData __heuristicConfData){
- super(REDUCER_MEMORY_CONF, __heuristicConfData);
- }
-
- @Override
- protected TezTaskData[] getTasks(TezApplicationData data) {
- return data.getReduceTaskData();
- }
-
-
-
-
-}
diff --git a/app/com/linkedin/drelephant/tez/heuristics/ReducerTimeHeuristic.java b/app/com/linkedin/drelephant/tez/heuristics/ReducerTimeHeuristic.java
deleted file mode 100644
index 5e0dbfe65..000000000
--- a/app/com/linkedin/drelephant/tez/heuristics/ReducerTimeHeuristic.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright 2017 Electronic Arts Inc.
- *
- * 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.drelephant.tez.heuristics;
-
-
-import com.linkedin.drelephant.analysis.Heuristic;
-import com.linkedin.drelephant.analysis.HeuristicResult;
-import com.linkedin.drelephant.analysis.Severity;
-import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
-import com.linkedin.drelephant.tez.data.TezApplicationData;
-import com.linkedin.drelephant.tez.data.TezTaskData;
-import com.linkedin.drelephant.util.Utils;
-import org.apache.log4j.Logger;
-
-import com.linkedin.drelephant.math.Statistics;
-
-import java.util.*;
-
-/**
- * Analyzes reducer task runtimes
- */
-
-public class ReducerTimeHeuristic implements Heuristic {
-
- private static final Logger logger = Logger.getLogger(ReducerTimeHeuristic.class);
-
- // Severity parameters.
- private static final String SHORT_RUNTIME_SEVERITY = "short_runtime_severity_in_min";
- private static final String LONG_RUNTIME_SEVERITY = "long_runtime_severity_in_min";
- private static final String NUM_TASKS_SEVERITY = "num_tasks_severity";
-
- // Default value of parameters
- private double[] shortRuntimeLimits = {10, 4, 2, 1}; // Limits(ms) for tasks with shorter runtime
- private double[] longRuntimeLimits = {15, 30, 60, 120}; // Limits(ms) for tasks with longer runtime
- private double[] numTasksLimits = {50, 101, 500, 1000}; // Number of Map tasks.
-
- private HeuristicConfigurationData _heuristicConfData;
-
- private void loadParameters() {
- Map paramMap = _heuristicConfData.getParamMap();
- String heuristicName = _heuristicConfData.getHeuristicName();
-
- double[] confShortThreshold = Utils.getParam(paramMap.get(SHORT_RUNTIME_SEVERITY), shortRuntimeLimits.length);
- if (confShortThreshold != null) {
- shortRuntimeLimits = confShortThreshold;
- }
- logger.info(heuristicName + " will use " + SHORT_RUNTIME_SEVERITY + " with the following threshold settings: "
- + Arrays.toString(shortRuntimeLimits));
- for (int i = 0; i < shortRuntimeLimits.length; i++) {
- shortRuntimeLimits[i] = shortRuntimeLimits[i] * Statistics.MINUTE_IN_MS;
- }
-
- double[] confLongThreshold = Utils.getParam(paramMap.get(LONG_RUNTIME_SEVERITY), longRuntimeLimits.length);
- if (confLongThreshold != null) {
- longRuntimeLimits = confLongThreshold;
- }
- logger.info(heuristicName + " will use " + LONG_RUNTIME_SEVERITY + " with the following threshold settings: "
- + Arrays.toString(longRuntimeLimits));
- for (int i = 0; i < longRuntimeLimits.length; i++) {
- longRuntimeLimits[i] = longRuntimeLimits[i] * Statistics.MINUTE_IN_MS;
- }
-
- double[] confNumTasksThreshold = Utils.getParam(paramMap.get(NUM_TASKS_SEVERITY), numTasksLimits.length);
- if (confNumTasksThreshold != null) {
- numTasksLimits = confNumTasksThreshold;
- }
- logger.info(heuristicName + " will use " + NUM_TASKS_SEVERITY + " with the following threshold settings: " + Arrays
- .toString(numTasksLimits));
-
-
- }
-
- public ReducerTimeHeuristic(HeuristicConfigurationData heuristicConfData) {
- this._heuristicConfData = heuristicConfData;
- loadParameters();
- }
-
- public HeuristicConfigurationData getHeuristicConfData() {
- return _heuristicConfData;
- }
-
- public HeuristicResult apply(TezApplicationData data) {
- if(!data.getSucceeded()) {
- return null;
- }
- TezTaskData[] tasks = data.getReduceTaskData();
-
- List runtimesMs = new ArrayList();
- long taskMinMs = Long.MAX_VALUE;
- long taskMaxMs = 0;
-
- for (TezTaskData task : tasks) {
-
- if (task.isSampled()) {
- long taskTime = task.getTotalRunTimeMs();
- runtimesMs.add(taskTime);
- taskMinMs = Math.min(taskMinMs, taskTime);
- taskMaxMs = Math.max(taskMaxMs, taskTime);
- }
- }
-
- if(taskMinMs == Long.MAX_VALUE) {
- taskMinMs = 0;
- }
-
-
- long averageTimeMs = Statistics.average(runtimesMs);
-
- Severity shortTaskSeverity = shortTaskSeverity(tasks.length, averageTimeMs);
- Severity longTaskSeverity = longTaskSeverity(tasks.length, averageTimeMs);
- Severity severity = Severity.max(shortTaskSeverity, longTaskSeverity);
-
- HeuristicResult result = new HeuristicResult(_heuristicConfData.getClassName(),
- _heuristicConfData.getHeuristicName(), severity, Utils.getHeuristicScore(severity, tasks.length));
-
- result.addResultDetail("Number of tasks", Integer.toString(tasks.length));
- result.addResultDetail("Average task runtime", tasks.length == 0 ? "0" : (Statistics.readableTimespan(averageTimeMs).equals("") ? "0 sec" : Statistics.readableTimespan(averageTimeMs)));
- result.addResultDetail("Max task runtime", tasks.length == 0 ? "0" : (Statistics.readableTimespan(taskMaxMs).equals("") ? "0 sec" : Statistics.readableTimespan(taskMaxMs)) );
- result.addResultDetail("Min task runtime", tasks.length == 0 ? "0" : (Statistics.readableTimespan(taskMinMs).equals("") ? "0 sec" :Statistics.readableTimespan(taskMinMs))) ;
-
- return result;
- }
-
- private Severity shortTaskSeverity(long numTasks, long averageTimeMs) {
- // We want to identify jobs with short task runtime
- Severity severity = getShortRuntimeSeverity(averageTimeMs);
- // Severity is reduced if number of tasks is small.
- Severity numTaskSeverity = getNumTasksSeverity(numTasks);
- return Severity.min(severity, numTaskSeverity);
- }
-
- private Severity longTaskSeverity(long numTasks, long averageTimeMs) {
- // We want to identify jobs with long task runtime. Severity is NOT reduced if num of tasks is large
- return getLongRuntimeSeverity(averageTimeMs);
- }
-
- private Severity getShortRuntimeSeverity(long runtimeMs) {
- return Severity.getSeverityDescending(
- runtimeMs, shortRuntimeLimits[0], shortRuntimeLimits[1], shortRuntimeLimits[2], shortRuntimeLimits[3]);
- }
-
- private Severity getLongRuntimeSeverity(long runtimeMs) {
- return Severity.getSeverityAscending(
- runtimeMs, longRuntimeLimits[0], longRuntimeLimits[1], longRuntimeLimits[2], longRuntimeLimits[3]);
- }
-
- private Severity getNumTasksSeverity(long numTasks) {
- return Severity.getSeverityAscending(
- numTasks, numTasksLimits[0], numTasksLimits[1], numTasksLimits[2], numTasksLimits[3]);
- }
-
-}
-
diff --git a/app/com/linkedin/drelephant/util/InfoExtractor.java b/app/com/linkedin/drelephant/util/InfoExtractor.java
index 6ff0f4d1b..9824b79b2 100644
--- a/app/com/linkedin/drelephant/util/InfoExtractor.java
+++ b/app/com/linkedin/drelephant/util/InfoExtractor.java
@@ -20,10 +20,6 @@
import com.linkedin.drelephant.clients.WorkflowClient;
import com.linkedin.drelephant.configurations.scheduler.SchedulerConfiguration;
import com.linkedin.drelephant.configurations.scheduler.SchedulerConfigurationData;
-
-import com.linkedin.drelephant.tez.data.TezApplicationData;
-import com.linkedin.drelephant.clients.WorkflowClient;
-
import com.linkedin.drelephant.mapreduce.data.MapReduceApplicationData;
import com.linkedin.drelephant.schedulers.Scheduler;
import com.linkedin.drelephant.spark.data.SparkApplicationData;
@@ -120,9 +116,6 @@ public static void loadInfo(AppResult result, HadoopApplicationData data) {
} else if ( data instanceof SparkApplicationData) {
properties = retrieveSparkProperties((SparkApplicationData) data);
}
- else if(data instanceof TezApplicationData){
- properties = retrieveTezProperties((TezApplicationData) data);
- }
Scheduler scheduler = getSchedulerInstance(data.getAppId(), properties);
if (scheduler == null) {
@@ -173,10 +166,6 @@ public static Properties retrieveMapreduceProperties(MapReduceApplicationData ap
return appData.getConf();
}
- public static Properties retrieveTezProperties(TezApplicationData appData) {
- return appData.getConf();
- }
-
/**
* Populates the given app result with the info from the given application data and scheduler.
*
diff --git a/app/com/linkedin/drelephant/util/ThreadContextMR2.java b/app/com/linkedin/drelephant/util/ThreadContextMR2.java
deleted file mode 100644
index a448a24fe..000000000
--- a/app/com/linkedin/drelephant/util/ThreadContextMR2.java
+++ /dev/null
@@ -1,92 +0,0 @@
-package com.linkedin.drelephant.util;
-
-import com.linkedin.drelephant.math.Statistics;
-import org.apache.hadoop.security.authentication.client.AuthenticatedURL;
-import org.apache.hadoop.security.authentication.client.AuthenticationException;
-import org.apache.log4j.Logger;
-import org.codehaus.jackson.JsonNode;
-import org.codehaus.jackson.map.ObjectMapper;
-
-import java.io.IOException;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.util.Random;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public final class ThreadContextMR2 {
- private static final Logger logger = Logger.getLogger(com.linkedin.drelephant.util.ThreadContextMR2.class);
-
- private static final AtomicInteger THREAD_ID = new AtomicInteger(1);
-
- private static final ThreadLocal _LOCAL_THREAD_ID = new ThreadLocal() {
- @Override
- public Integer initialValue() {
- return THREAD_ID.getAndIncrement();
- }
- };
-
- private static final ThreadLocal _LOCAL_LAST_UPDATED = new ThreadLocal();
- private static final ThreadLocal _LOCAL_UPDATE_INTERVAL = new ThreadLocal();
-
- private static final ThreadLocal _LOCAL_DIAGNOSTIC_PATTERN = new ThreadLocal() {
- @Override
- public Pattern initialValue() {
- // Example: "Task task_1443068695259_9143_m_000475 failed 1 times"
- return Pattern.compile(
- ".*[\\s\\u00A0]+(task_[0-9]+_[0-9]+_[m|r]_[0-9]+)[\\s\\u00A0]+.*");
- }
- };
-
- private static final ThreadLocal _LOCAL_AUTH_TOKEN =
- new ThreadLocal() {
- @Override
- public AuthenticatedURL.Token initialValue() {
- _LOCAL_LAST_UPDATED.set(System.currentTimeMillis());
- // Random an interval for each executor to avoid update token at the same time
- _LOCAL_UPDATE_INTERVAL.set(Statistics.MINUTE_IN_MS * 30 + new Random().nextLong()
- % (3 * Statistics.MINUTE_IN_MS));
- logger.info("Executor " + _LOCAL_THREAD_ID.get() + " update interval " + _LOCAL_UPDATE_INTERVAL.get() * 1.0
- / Statistics.MINUTE_IN_MS);
- return new AuthenticatedURL.Token();
- }
- };
-
- private static final ThreadLocal _LOCAL_AUTH_URL = new ThreadLocal() {
- @Override
- public AuthenticatedURL initialValue() {
- return new AuthenticatedURL();
- }
- };
-
- private static final ThreadLocal _LOCAL_MAPPER = new ThreadLocal() {
- @Override
- public ObjectMapper initialValue() {
- return new ObjectMapper();
- }
- };
-
- private ThreadContextMR2() {
- // Empty on purpose
- }
-
- public static Matcher getDiagnosticMatcher(String diagnosticInfo) {
- return _LOCAL_DIAGNOSTIC_PATTERN.get().matcher(diagnosticInfo);
- }
-
- public static JsonNode readJsonNode(URL url) throws IOException, AuthenticationException {
- HttpURLConnection conn = _LOCAL_AUTH_URL.get().openConnection(url, _LOCAL_AUTH_TOKEN.get());
- return _LOCAL_MAPPER.get().readTree(conn.getInputStream());
- }
-
- public static void updateAuthToken() {
- long curTime = System.currentTimeMillis();
- if (curTime - _LOCAL_LAST_UPDATED.get() > _LOCAL_UPDATE_INTERVAL.get()) {
- logger.info("Executor " + _LOCAL_THREAD_ID.get() + " updates its AuthenticatedToken.");
- _LOCAL_AUTH_TOKEN.set(new AuthenticatedURL.Token());
- _LOCAL_AUTH_URL.set(new AuthenticatedURL());
- _LOCAL_LAST_UPDATED.set(curTime);
- }
- }
-}
\ No newline at end of file
diff --git a/test/com/linkedin/drelephant/mapreduce/fetchers/MapReduceFetcherHadoop2Test.java b/test/com/linkedin/drelephant/mapreduce/fetchers/MapReduceFetcherHadoop2Test.java
index 8ce84bec1..531bb3724 100644
--- a/test/com/linkedin/drelephant/mapreduce/fetchers/MapReduceFetcherHadoop2Test.java
+++ b/test/com/linkedin/drelephant/mapreduce/fetchers/MapReduceFetcherHadoop2Test.java
@@ -18,8 +18,6 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-
-import com.linkedin.drelephant.util.ThreadContextMR2;
import org.junit.Assert;
import org.junit.Test;
diff --git a/test/com/linkedin/drelephant/tez/TezTaskLevelAggregatedMetricsTest.java b/test/com/linkedin/drelephant/tez/TezTaskLevelAggregatedMetricsTest.java
deleted file mode 100644
index 4a42a8617..000000000
--- a/test/com/linkedin/drelephant/tez/TezTaskLevelAggregatedMetricsTest.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2017 Electronic Arts Inc.
- *
- * 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.drelephant.tez;
-
-
-import com.linkedin.drelephant.tez.data.TezCounterData;
-import com.linkedin.drelephant.tez.data.TezTaskData;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class TezTaskLevelAggregatedMetricsTest {
-
- @Test
- public void testZeroTasks() {
- TezTaskData taskData[] = {};
- TezTaskLevelAggregatedMetrics taskMetrics = new TezTaskLevelAggregatedMetrics(taskData, 0, 0);
- Assert.assertEquals(taskMetrics.getDelay(), 0);
- Assert.assertEquals(taskMetrics.getResourceUsed(), 0);
- Assert.assertEquals(taskMetrics.getResourceWasted(), 0);
- }
-
- @Test
- public void testNullTaskArray() {
- TezTaskLevelAggregatedMetrics taskMetrics = new TezTaskLevelAggregatedMetrics(null, 0, 0);
- Assert.assertEquals(taskMetrics.getDelay(), 0);
- Assert.assertEquals(taskMetrics.getResourceUsed(), 0);
- Assert.assertEquals(taskMetrics.getResourceWasted(), 0);
- }
-
- @Test
- public void testTaskLevelData() {
- TezTaskData taskData[] = new TezTaskData[3];
- TezCounterData counterData = new TezCounterData();
- counterData.set(TezCounterData.CounterName.PHYSICAL_MEMORY_BYTES, 655577088L);
- counterData.set(TezCounterData.CounterName.VIRTUAL_MEMORY_BYTES, 3051589632L);
- long time[] = {0,0,0,1464218501117L, 1464218534148L};
- taskData[0] = new TezTaskData("task", "id");
- taskData[0].setTimeAndCounter(time,counterData);
- taskData[1] = new TezTaskData("task", "id");
- taskData[1].setTimeAndCounter(new long[5],counterData);
- // Non-sampled task, which does not contain time and counter data
- taskData[2] = new TezTaskData("task", "id");
- TezTaskLevelAggregatedMetrics taskMetrics = new TezTaskLevelAggregatedMetrics(taskData, 4096L, 1463218501117L);
- Assert.assertEquals(taskMetrics.getDelay(), 1000000000L);
- Assert.assertEquals(taskMetrics.getResourceUsed(), 135168L);
- Assert.assertEquals(taskMetrics.getResourceWasted(), 66627L);
- }
-}
\ No newline at end of file
diff --git a/test/com/linkedin/drelephant/tez/fetchers/TezFetcherTest.java b/test/com/linkedin/drelephant/tez/fetchers/TezFetcherTest.java
deleted file mode 100644
index bf38f10c3..000000000
--- a/test/com/linkedin/drelephant/tez/fetchers/TezFetcherTest.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2017 Electronic Arts Inc.
- *
- * 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.drelephant.tez.fetchers;
-
-import java.util.regex.Matcher;
-import org.junit.Assert;
-import org.junit.Test;
-import com.linkedin.drelephant.util.ThreadContextMR2;
-
-public class TezFetcherTest {
-
- @Test
- public void testDiagnosticMatcher() {
- Matcher matcher = ThreadContextMR2.getDiagnosticMatcher("Task task_1443068695259_9143_m_000475 failed 1 time");
- Assert.assertEquals(".*[\\s\\u00A0]+(task_[0-9]+_[0-9]+_[m|r]_[0-9]+)[\\s\\u00A0]+.*", matcher.pattern().toString());
- Assert.assertEquals(true, matcher.matches());
- Assert.assertEquals(1, matcher.groupCount());
- Assert.assertEquals("task_1443068695259_9143_m_000475", matcher.group(1));
- }
-
-}
\ No newline at end of file
diff --git a/test/com/linkedin/drelephant/tez/heuristics/MapperDataSkewHeuristicTest.java b/test/com/linkedin/drelephant/tez/heuristics/MapperDataSkewHeuristicTest.java
deleted file mode 100644
index 8792e70f4..000000000
--- a/test/com/linkedin/drelephant/tez/heuristics/MapperDataSkewHeuristicTest.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright 2017 Electronic Arts Inc.
- *
- * 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.drelephant.tez.heuristics;
-
-import com.linkedin.drelephant.analysis.*;
-import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-
-import com.linkedin.drelephant.tez.data.TezApplicationData;
-import com.linkedin.drelephant.tez.data.TezCounterData;
-import com.linkedin.drelephant.tez.data.TezTaskData;
-import junit.framework.TestCase;
-
-
-public class MapperDataSkewHeuristicTest extends TestCase {
-
- private static final long UNITSIZE = HDFSContext.HDFS_BLOCK_SIZE / 64; //1MB
- private static final long UNITSIZETIME = 1000000; //1000sec
-
- private static Map paramsMap = new HashMap();
- private static Heuristic _heuristic = new MapperDataSkewHeuristic(new HeuristicConfigurationData("test_heuristic",
- "test_class", "test_view", new ApplicationType("test_apptype"), paramsMap));
-
- public void testCritical() throws IOException {
- assertEquals(Severity.CRITICAL, analyzeJob(200, 200, 1 * UNITSIZE, 100 * UNITSIZE));
- }
-
- public void testSevere() throws IOException {
- assertEquals(Severity.SEVERE, analyzeJob(200, 200, 10 * UNITSIZE, 100 * UNITSIZE));
- }
-
- public void testModerate() throws IOException {
- assertEquals(Severity.MODERATE, analyzeJob(200, 200, 20 * UNITSIZE, 100 * UNITSIZE));
- }
-
- public void testLow() throws IOException {
- assertEquals(Severity.LOW, analyzeJob(200, 200, 30 * UNITSIZE, 100 * UNITSIZE));
- }
-
- public void testNone() throws IOException {
- assertEquals(Severity.NONE, analyzeJob(200, 200, 50 * UNITSIZE, 100 * UNITSIZE));
- }
-
- public void testSmallFiles() throws IOException {
- assertEquals(Severity.NONE, analyzeJob(200, 200, 1 * UNITSIZE, 5 * UNITSIZE));
- }
-
- public void testSmallTasks() throws IOException {
- assertEquals(Severity.NONE, analyzeJob(5, 5, 10 * UNITSIZE, 100 * UNITSIZE));
- }
-
- public void testCriticalTime() throws IOException {
- assertEquals(Severity.CRITICAL, analyzeJobTime(200, 200, 1 * UNITSIZETIME, 100 * UNITSIZETIME));
- }
-
- public void testSevereTime() throws IOException {
- assertEquals(Severity.SEVERE, analyzeJobTime(200, 200, 10 * UNITSIZETIME, 100 * UNITSIZETIME));
- }
-
- public void testModerateTime() throws IOException {
- assertEquals(Severity.MODERATE, analyzeJobTime(200, 200, 20 * UNITSIZETIME, 100 * UNITSIZETIME));
- }
-
- public void testLowTime() throws IOException {
- assertEquals(Severity.LOW, analyzeJobTime(200, 200, 30 * UNITSIZETIME, 100 * UNITSIZETIME));
- }
-
- public void testNoneTime() throws IOException {
- assertEquals(Severity.NONE, analyzeJobTime(200, 200, 50 * UNITSIZETIME, 100 * UNITSIZETIME));
- }
-
- public void testSmallTasksTime() throws IOException {
- assertEquals(Severity.NONE, analyzeJobTime(5, 5, 10 * UNITSIZETIME, 100 * UNITSIZETIME));
- }
-
- private Severity analyzeJob(int numSmallTasks, int numLargeTasks, long smallInputSize, long largeInputSize)
- throws IOException {
- TezCounterData jobCounter = new TezCounterData();
- TezTaskData[] mappers = new TezTaskData[numSmallTasks + numLargeTasks + 1];
-
- TezCounterData smallCounter = new TezCounterData();
- smallCounter.set(TezCounterData.CounterName.HDFS_BYTES_READ, smallInputSize);
-
- TezCounterData largeCounter = new TezCounterData();
- largeCounter.set(TezCounterData.CounterName.S3A_BYTES_READ, largeInputSize);
-
- int i = 0;
- for (; i < numSmallTasks; i++) {
- mappers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
- mappers[i].setTimeAndCounter(new long[5], smallCounter);
- }
- for (; i < numSmallTasks + numLargeTasks; i++) {
- mappers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
- mappers[i].setTimeAndCounter(new long[5], largeCounter);
- }
- // Non-sampled task, which does not contain time and counter data
- mappers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
-
- TezApplicationData data = new TezApplicationData().setCounters(jobCounter).setMapTaskData(mappers);
- HeuristicResult result = _heuristic.apply(data);
- return result.getSeverity();
-
- }
-
- private Severity analyzeJobTime(int numSmallTasks, int numLongTasks, long smallTimeTaken, long longTimeTaken)
- throws IOException {
- TezCounterData jobCounter = new TezCounterData();
- TezTaskData[] mappers = new TezTaskData[numSmallTasks + numLongTasks + 1];
-
- int i = 0;
- for (; i < numSmallTasks; i++) {
- mappers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
- mappers[i].setTotalTimeMs(smallTimeTaken, true);
- mappers[i].setCounter(jobCounter);
- }
- for (; i < numSmallTasks + numLongTasks; i++) {
- mappers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
- mappers[i].setTotalTimeMs(longTimeTaken, true);
- mappers[i].setCounter(jobCounter);
- }
- // Non-sampled task, which does not contain time data
- mappers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
-
- TezApplicationData data = new TezApplicationData().setCounters(jobCounter).setMapTaskData(mappers);
- HeuristicResult result = _heuristic.apply(data);
- return result.getSeverity();
-
- }
-}
\ No newline at end of file
diff --git a/test/com/linkedin/drelephant/tez/heuristics/MapperGCHeuristicTest.java b/test/com/linkedin/drelephant/tez/heuristics/MapperGCHeuristicTest.java
deleted file mode 100644
index 52cea2035..000000000
--- a/test/com/linkedin/drelephant/tez/heuristics/MapperGCHeuristicTest.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright 2017 Electronic Arts Inc.
- *
- * 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.drelephant.tez.heuristics;
-
-import com.linkedin.drelephant.analysis.ApplicationType;
-import com.linkedin.drelephant.analysis.HeuristicResult;
-import com.linkedin.drelephant.analysis.Severity;
-import com.linkedin.drelephant.tez.data.TezApplicationData;
-import com.linkedin.drelephant.tez.data.TezCounterData;
-import com.linkedin.drelephant.tez.data.TezTaskData;
-import com.linkedin.drelephant.analysis.Heuristic;
-import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
-import junit.framework.TestCase;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-
-public class MapperGCHeuristicTest extends TestCase {
-
- private static Map paramsMap = new HashMap();
-
- private static Heuristic _heuristic = new MapperGCHeuristic(new HeuristicConfigurationData("test_heuristic",
- "test_class", "test_view", new ApplicationType("test_apptype"), paramsMap));
-
- private static int NUMTASKS = 100;
-
- public void testGCCritical() throws IOException {
- assertEquals(Severity.CRITICAL, analyzeJob(1000000, 50000, 2000));
- }
-
- public void testGCSevere() throws IOException {
- assertEquals(Severity.SEVERE, analyzeJob(1000000, 50000, 1500));
- }
-
- public void testGCModerate() throws IOException {
- assertEquals(Severity.MODERATE, analyzeJob(1000000, 50000, 1000));
- }
-
- public void testGCNone() throws IOException {
- assertEquals(Severity.NONE, analyzeJob(1000000, 50000, 300));
- }
-
- public void testShortTasksNone() throws IOException {
- assertEquals(Severity.NONE, analyzeJob(100000, 50000, 2000));
- }
-
-
- private Severity analyzeJob(long runtimeMs, long cpuMs, long gcMs) throws IOException {
- TezCounterData jobCounter = new TezCounterData();
- TezTaskData[] mappers = new TezTaskData[NUMTASKS + 1];
-
- TezCounterData counter = new TezCounterData();
- counter.set(TezCounterData.CounterName.CPU_MILLISECONDS, cpuMs);
- counter.set(TezCounterData.CounterName.GC_TIME_MILLIS, gcMs);
-
- int i = 0;
- for (; i < NUMTASKS; i++) {
- mappers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
- mappers[i].setTimeAndCounter(new long[]{runtimeMs, 0 , 0, 0, 0}, counter);
- }
- // Non-sampled task, which does not contain time and counter data
- mappers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
-
- TezApplicationData data = new TezApplicationData().setCounters(jobCounter).setMapTaskData(mappers);
- HeuristicResult result = _heuristic.apply(data);
- return result.getSeverity();
- }
-
-}
\ No newline at end of file
diff --git a/test/com/linkedin/drelephant/tez/heuristics/MapperMemoryHeuristicTest.java b/test/com/linkedin/drelephant/tez/heuristics/MapperMemoryHeuristicTest.java
deleted file mode 100644
index 72e426743..000000000
--- a/test/com/linkedin/drelephant/tez/heuristics/MapperMemoryHeuristicTest.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright 2017 Electronic Arts Inc.
- *
- * 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.drelephant.tez.heuristics;
-
-import com.linkedin.drelephant.analysis.ApplicationType;
-import com.linkedin.drelephant.analysis.Heuristic;
-import com.linkedin.drelephant.analysis.HeuristicResult;
-import com.linkedin.drelephant.analysis.Severity;
-import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
-import com.linkedin.drelephant.tez.data.TezApplicationData;
-import com.linkedin.drelephant.tez.data.TezCounterData;
-import com.linkedin.drelephant.tez.data.TezTaskData;
-import junit.framework.TestCase;
-import org.apache.commons.io.FileUtils;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Properties;
-
-public class MapperMemoryHeuristicTest extends TestCase{
-
- private static Map paramsMap = new HashMap();
- private static Heuristic _heuristic = new MapperMemoryHeuristic(new HeuristicConfigurationData("test_heuristic",
- "test_class", "test_view", new ApplicationType("test_apptype"), paramsMap));
-
- private int NUMTASKS = 100;
-
- public void testLargeContainerSizeCritical() throws IOException {
- assertEquals(Severity.CRITICAL, analyzeJob(2048, 8192));
- }
-
- public void testLargeContainerSizeSevere() throws IOException {
- assertEquals(Severity.SEVERE, analyzeJob(3072, 8192));
- }
-
- public void testLargeContainerSizeModerate() throws IOException {
- assertEquals(Severity.MODERATE, analyzeJob(4096, 8192));
- }
-
- public void testLargeContainerSizeNone() throws IOException {
- assertEquals(Severity.NONE, analyzeJob(6144, 8192));
- }
-
- // If the task use default container size, it should not be flagged
- // Not using Default Container param, will calculate severity irrespective of default container size Chaning NONE -> CRITICAL
- public void testDefaultContainerNone() throws IOException {
- assertEquals(Severity.CRITICAL, analyzeJob(256, 2048));
- }
-
- // Not using Default Container param, will calculate severity irrespective of default container size Chaning NONE -> MODERATE
- public void testDefaultContainerNoneMore() throws IOException {
- assertEquals(Severity.MODERATE, analyzeJob(1024, 2048));
- }
-
- private Severity analyzeJob(long taskAvgMemMB, long containerMemMB) throws IOException {
- TezCounterData jobCounter = new TezCounterData();
- TezTaskData[] mappers = new TezTaskData[NUMTASKS + 1];
-
- TezCounterData counter = new TezCounterData();
- counter.set(TezCounterData.CounterName.PHYSICAL_MEMORY_BYTES, taskAvgMemMB* FileUtils.ONE_MB);
-
- Properties p = new Properties();
- p.setProperty(MapperMemoryHeuristic.MAPPER_MEMORY_CONF, Long.toString(containerMemMB));
-
- int i = 0;
- for (; i < NUMTASKS; i++) {
- mappers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
- mappers[i].setTime(new long[5]);
- mappers[i].setCounter(counter);
- }
- // Non-sampled task, which does not contain time and counter data
- mappers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
-
- TezApplicationData data = new TezApplicationData().setCounters(jobCounter).setMapTaskData(mappers);
- data.setConf(p);
- HeuristicResult result = _heuristic.apply(data);
- return result.getSeverity();
- }
-}
\ No newline at end of file
diff --git a/test/com/linkedin/drelephant/tez/heuristics/MapperSpeedHeuristicTest.java b/test/com/linkedin/drelephant/tez/heuristics/MapperSpeedHeuristicTest.java
deleted file mode 100644
index 2d21f856a..000000000
--- a/test/com/linkedin/drelephant/tez/heuristics/MapperSpeedHeuristicTest.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright 2017 Electronic Arts Inc.
- *
- * 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.drelephant.tez.heuristics;
-
-import com.linkedin.drelephant.analysis.ApplicationType;
-import com.linkedin.drelephant.analysis.Heuristic;
-import com.linkedin.drelephant.analysis.HeuristicResult;
-import com.linkedin.drelephant.analysis.Severity;
-import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
-import com.linkedin.drelephant.math.Statistics;
-import com.linkedin.drelephant.tez.data.TezApplicationData;
-import com.linkedin.drelephant.tez.data.TezCounterData;
-import com.linkedin.drelephant.tez.data.TezTaskData;
-import junit.framework.TestCase;
-import org.apache.commons.io.FileUtils;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-
-public class MapperSpeedHeuristicTest extends TestCase {
-
- private static Map paramsMap = new HashMap();
- private static Heuristic _heuristic = new MapperSpeedHeuristic(new HeuristicConfigurationData("test_heuristic",
- "test_class", "test_view", new ApplicationType("test_apptype"), paramsMap));
-
- private static final long MB_IN_BYTES = FileUtils.ONE_MB;
- private static final long MINUTE_IN_MS = Statistics.MINUTE_IN_MS;
- private static final int NUMTASKS = 100;
-
- public void testCritical() throws IOException {
- long runtime = 120 * MINUTE_IN_MS;
- long speed_factor = (runtime * MB_IN_BYTES) / 1000;
- assertEquals(Severity.CRITICAL, analyzeJob(runtime, 1 * speed_factor));
- }
-
- public void testSevere() throws IOException {
- long runtime = 120 * MINUTE_IN_MS;
- long speed_factor = (runtime * MB_IN_BYTES) / 1000;
- assertEquals(Severity.SEVERE, analyzeJob(runtime, 4 * speed_factor));
- }
-
- public void testModerate() throws IOException {
- long runtime = 120 * MINUTE_IN_MS;
- long speed_factor = (runtime * MB_IN_BYTES) / 1000;
- assertEquals(Severity.MODERATE, analyzeJob(runtime, 13 * speed_factor));
- }
-
- public void testLow() throws IOException {
- long runtime = 120 * MINUTE_IN_MS;
- long speed_factor = (runtime * MB_IN_BYTES) / 1000;
- assertEquals(Severity.LOW, analyzeJob(runtime, 50 * speed_factor));
- }
-
- public void testNone() throws IOException {
- long runtime = 120 * MINUTE_IN_MS;
- long speed_factor = (runtime * MB_IN_BYTES) / 1000;
- assertEquals(Severity.NONE, analyzeJob(runtime, 51 * speed_factor));
- }
-
- public void testShortTask() throws IOException {
- long runtime = 2 * MINUTE_IN_MS;
- long speed_factor = (runtime * MB_IN_BYTES) / 1000;
- assertEquals(Severity.NONE, analyzeJob(runtime, 1 * speed_factor));
- }
-
- private Severity analyzeJob(long runtimeMs, long readBytes) throws IOException {
- TezCounterData jobCounter = new TezCounterData();
- TezTaskData[] mappers = new TezTaskData[NUMTASKS + 1];
-
- TezCounterData counter = new TezCounterData();
- counter.set(TezCounterData.CounterName.HDFS_BYTES_READ, readBytes / 2);
- counter.set(TezCounterData.CounterName.S3A_BYTES_READ, readBytes / 2);
-
- int i = 0;
- for (; i < NUMTASKS; i++) {
- mappers[i] = new TezTaskData(counter, new long[] { runtimeMs, 0, 0 ,0, 0});
- }
- // Non-sampled task, which does not contain time and counter data
- mappers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
-
- TezApplicationData data = new TezApplicationData().setCounters(jobCounter).setMapTaskData(mappers);
- HeuristicResult result = _heuristic.apply(data);
- return result.getSeverity();
- }
-}
\ No newline at end of file
diff --git a/test/com/linkedin/drelephant/tez/heuristics/MapperSpillHeuristicTest.java b/test/com/linkedin/drelephant/tez/heuristics/MapperSpillHeuristicTest.java
deleted file mode 100644
index 62831a727..000000000
--- a/test/com/linkedin/drelephant/tez/heuristics/MapperSpillHeuristicTest.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright 2017 Electronic Arts Inc.
- *
- * 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.drelephant.tez.heuristics;
-
-import com.linkedin.drelephant.analysis.ApplicationType;
-import com.linkedin.drelephant.analysis.Heuristic;
-import com.linkedin.drelephant.analysis.HeuristicResult;
-import com.linkedin.drelephant.analysis.Severity;
-import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
-import com.linkedin.drelephant.tez.data.TezApplicationData;
-import com.linkedin.drelephant.tez.data.TezCounterData;
-import com.linkedin.drelephant.tez.data.TezTaskData;
-import junit.framework.TestCase;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-
-
-public class MapperSpillHeuristicTest extends TestCase{
-
- private static Map paramsMap = new HashMap();
- private static Heuristic _heuristic = new MapperSpillHeuristic(new HeuristicConfigurationData("test_heuristic",
- "test_class", "test_view", new ApplicationType("test_apptype"), paramsMap));
-
- public void testCritical() throws IOException {
- // Spill ratio 3.0, 1000 tasks
- assertEquals(Severity.CRITICAL, analyzeJob(3000, 1000, 1000));
- }
-
- public void testSevere() throws IOException {
- // Spill ratio 2.5, 1000 tasks
- assertEquals(Severity.SEVERE, analyzeJob(2500, 1000, 1000));
- }
-
- public void testModerate() throws IOException {
- // Spill ratio 2.3, 1000 tasks
- assertEquals(Severity.MODERATE, analyzeJob(2300, 1000, 1000));
- }
-
- public void testLow() throws IOException {
- // Spill ratio 2.1, 1000 tasks
- assertEquals(Severity.LOW, analyzeJob(2100, 1000, 1000));
- }
-
- public void testNone() throws IOException {
- // Spill ratio 1.0, 1000 tasks
- assertEquals(Severity.NONE, analyzeJob(1000, 1000, 1000));
- }
-
- public void testSmallNumTasks() throws IOException {
- // Spill ratio 3.0, should be critical, but number of task is small(10), final result is NONE
- assertEquals(Severity.NONE, analyzeJob(3000, 1000, 10));
- }
-
- private Severity analyzeJob(long spilledRecords, long mapRecords, int numTasks) throws IOException {
- TezCounterData jobCounter = new TezCounterData();
- TezTaskData[] mappers = new TezTaskData[numTasks + 1];
-
- TezCounterData counter = new TezCounterData();
- counter.set(TezCounterData.CounterName.SPILLED_RECORDS, spilledRecords);
- counter.set(TezCounterData.CounterName.OUTPUT_RECORDS, mapRecords);
-
- int i = 0;
- for (; i < numTasks; i++) {
- mappers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
- mappers[i].setTimeAndCounter(new long[5], counter);
- }
- // Non-sampled task, which does not contain time and counter data
- mappers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
-
- TezApplicationData data = new TezApplicationData().setCounters(jobCounter).setMapTaskData(mappers);
- HeuristicResult result = _heuristic.apply(data);
- return result.getSeverity();
- }
-}
\ No newline at end of file
diff --git a/test/com/linkedin/drelephant/tez/heuristics/MapperTimeHeuristicTest.java b/test/com/linkedin/drelephant/tez/heuristics/MapperTimeHeuristicTest.java
deleted file mode 100644
index 39c6ec891..000000000
--- a/test/com/linkedin/drelephant/tez/heuristics/MapperTimeHeuristicTest.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright 2017 Electronic Arts Inc.
- *
- * 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.drelephant.tez.heuristics;
-
-import com.linkedin.drelephant.analysis.ApplicationType;
-import com.linkedin.drelephant.analysis.Heuristic;
-import com.linkedin.drelephant.analysis.HeuristicResult;
-import com.linkedin.drelephant.analysis.Severity;
-import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
-import com.linkedin.drelephant.math.Statistics;
-import com.linkedin.drelephant.tez.data.TezApplicationData;
-import com.linkedin.drelephant.tez.data.TezCounterData;
-import com.linkedin.drelephant.tez.data.TezTaskData;
-import junit.framework.TestCase;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-
-
-public class MapperTimeHeuristicTest extends TestCase {
-
- private static final long DUMMY_INPUT_SIZE = 0;
-
- private static Map paramsMap = new HashMap();
- private static Heuristic _heuristic = new MapperTimeHeuristic(new HeuristicConfigurationData("test_heuristic",
- "test_class", "test_view", new ApplicationType("test_apptype"), paramsMap));
-
- // Test batch 1: Large runtime. Heuristic is not affected by various number of tasks */
-
- public void testLongRuntimeTasksCritical() throws IOException {
- // Should decrease split size and increase number of tasks
- assertEquals(Severity.CRITICAL, analyzeJob(10, 120 * Statistics.MINUTE_IN_MS));
- }
-
- public void testLongRuntimeTasksCriticalMore() throws IOException {
- // Should decrease split size and increase number of tasks
- assertEquals(Severity.CRITICAL, analyzeJob(1000, 120 * Statistics.MINUTE_IN_MS));
- }
-
- public void testLongRuntimeTasksSevere() throws IOException {
- // Should decrease split size and increase number of tasks
- assertEquals(Severity.SEVERE, analyzeJob(10, 60 * Statistics.MINUTE_IN_MS));
- }
-
- public void testLongRuntimeTasksSevereMore() throws IOException {
- // Should decrease split size and increase number of tasks
- assertEquals(Severity.SEVERE, analyzeJob(1000, 60 * Statistics.MINUTE_IN_MS));
- }
-
- // Test batch 2: Short runtime and various number of tasks
-
- public void testShortRuntimeTasksCritical() throws IOException {
- // Should increase split size and decrease number of tasks
- assertEquals(Severity.CRITICAL, analyzeJob(1000, 1 * Statistics.MINUTE_IN_MS));
- }
-
- public void testShortRuntimeTasksSevere() throws IOException {
- // Should increase split size and decrease number of tasks
- assertEquals(Severity.SEVERE, analyzeJob(500, 1 * Statistics.MINUTE_IN_MS));
- }
-
- public void testShortRuntimeTasksModerate() throws IOException {
- assertEquals(Severity.MODERATE, analyzeJob(101, 1 * Statistics.MINUTE_IN_MS));
- }
-
- public void testShortRuntimeTasksLow() throws IOException {
- assertEquals(Severity.LOW, analyzeJob(50, 1 * Statistics.MINUTE_IN_MS));
- }
-
- public void testShortRuntimeTasksNone() throws IOException {
- // Small file with small number of tasks and short runtime. This should be the common case.
- assertEquals(Severity.NONE, analyzeJob(5, 1 * Statistics.MINUTE_IN_MS));
- }
-
- private Severity analyzeJob(int numTasks, long runtime) throws IOException {
- TezCounterData jobCounter = new TezCounterData();
- TezTaskData[] mappers = new TezTaskData[numTasks + 1];
-
- TezCounterData taskCounter = new TezCounterData();
- taskCounter.set(TezCounterData.CounterName.S3A_BYTES_READ, DUMMY_INPUT_SIZE / 4);
-
-
- int i = 0;
- for (; i < numTasks; i++) {
- mappers[i] = new TezTaskData(jobCounter,new long[] { runtime, 0, 0, 0, 0 });
- }
- // Non-sampled task, which does not contain time and counter data
- mappers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
-
- TezApplicationData data = new TezApplicationData().setCounters(jobCounter).setMapTaskData(mappers);
- HeuristicResult result = _heuristic.apply(data);
- return result.getSeverity();
- }
-
-
-}
\ No newline at end of file
diff --git a/test/com/linkedin/drelephant/tez/heuristics/ReducerDataSkewHeuristicTest.java b/test/com/linkedin/drelephant/tez/heuristics/ReducerDataSkewHeuristicTest.java
deleted file mode 100644
index ff95e862a..000000000
--- a/test/com/linkedin/drelephant/tez/heuristics/ReducerDataSkewHeuristicTest.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright 2017 Electronic Arts Inc.
- *
- * 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.drelephant.tez.heuristics;
-
-import com.linkedin.drelephant.analysis.*;
-import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
-import com.linkedin.drelephant.tez.data.TezApplicationData;
-import com.linkedin.drelephant.tez.data.TezCounterData;
-import com.linkedin.drelephant.tez.data.TezTaskData;
-import junit.framework.TestCase;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-
-public class ReducerDataSkewHeuristicTest extends TestCase{
-
- private static final long UNITSIZE = HDFSContext.HDFS_BLOCK_SIZE / 64; //1mb
- private static final long UNITSIZETIME = 1000000; //1000sec
-
- private static Map paramsMap = new HashMap();
- private static Heuristic _heuristic = new ReducerDataSkewHeuristic(new HeuristicConfigurationData("test_heuristic",
- "test_class", "test_view", new ApplicationType("test_apptype"), paramsMap));
-
- public void testCritical() throws IOException {
- assertEquals(Severity.CRITICAL, analyzeJob(200, 200, 1 * UNITSIZE, 100 * UNITSIZE));
- }
-
- public void testSevere() throws IOException {
- assertEquals(Severity.SEVERE, analyzeJob(200, 200, 10 * UNITSIZE, 100 * UNITSIZE));
- }
-
- public void testModerate() throws IOException {
- assertEquals(Severity.MODERATE, analyzeJob(200, 200, 20 * UNITSIZE, 100 * UNITSIZE));
- }
-
- public void testLow() throws IOException {
- assertEquals(Severity.LOW, analyzeJob(200, 200, 30 * UNITSIZE, 100 * UNITSIZE));
- }
-
- public void testNone() throws IOException {
- assertEquals(Severity.NONE, analyzeJob(200, 200, 50 * UNITSIZE, 100 * UNITSIZE));
- }
-
- public void testSmallFiles() throws IOException {
- assertEquals(Severity.NONE, analyzeJob(200, 200, 1 * UNITSIZE, 5 * UNITSIZE));
- }
-
- public void testSmallTasks() throws IOException {
- assertEquals(Severity.NONE, analyzeJob(5, 5, 10 * UNITSIZE, 100 * UNITSIZE));
- }
-
- public void testCriticalTime() throws IOException {
- assertEquals(Severity.CRITICAL, analyzeJobTime(200, 200, 1 * UNITSIZETIME, 100 * UNITSIZETIME));
- }
-
- public void testSevereTime() throws IOException {
- assertEquals(Severity.SEVERE, analyzeJobTime(200, 200, 10 * UNITSIZETIME, 100 * UNITSIZETIME));
- }
-
- public void testModerateTime() throws IOException {
- assertEquals(Severity.MODERATE, analyzeJobTime(200, 200, 20 * UNITSIZETIME, 100 * UNITSIZETIME));
- }
-
- public void testLowTime() throws IOException {
- assertEquals(Severity.LOW, analyzeJobTime(200, 200, 30 * UNITSIZETIME, 100 * UNITSIZETIME));
- }
-
- public void testNoneTime() throws IOException {
- assertEquals(Severity.NONE, analyzeJobTime(200, 200, 50 * UNITSIZETIME, 100 * UNITSIZETIME));
- }
-
- public void testSmallTasksTime() throws IOException {
- assertEquals(Severity.NONE, analyzeJobTime(5, 5, 10 * UNITSIZETIME, 100 * UNITSIZETIME));
- }
-
- private Severity analyzeJob(int numSmallTasks, int numLargeTasks, long smallInputSize, long largeInputSize)
- throws IOException {
- TezCounterData jobCounter = new TezCounterData();
- TezTaskData[] reducers = new TezTaskData[numSmallTasks + numLargeTasks + 1];
-
- TezCounterData smallCounter = new TezCounterData();
- smallCounter.set(TezCounterData.CounterName.SHUFFLE_BYTES, smallInputSize);
-
- TezCounterData largeCounter = new TezCounterData();
- largeCounter.set(TezCounterData.CounterName.SHUFFLE_BYTES, largeInputSize);
-
- int i = 0;
- for (; i < numSmallTasks; i++) {
- reducers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
- reducers[i].setTimeAndCounter(new long[5], smallCounter);
- }
- for (; i < numSmallTasks + numLargeTasks; i++) {
- reducers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
- reducers[i].setTimeAndCounter(new long[5], largeCounter);
- }
- // Non-sampled task, which does not contain time and counter data
- reducers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
-
- TezApplicationData data = new TezApplicationData().setCounters(jobCounter).setReduceTaskData(reducers);
- HeuristicResult result = _heuristic.apply(data);
- return result.getSeverity();
- }
-
- private Severity analyzeJobTime(int numSmallTasks, int numLongTasks, long smallTimeTaken, long longTimeTaken)
- throws IOException {
- TezCounterData jobCounter = new TezCounterData();
- TezTaskData[] reducers = new TezTaskData[numSmallTasks + numLongTasks + 1];
-
- int i = 0;
- for (; i < numSmallTasks; i++) {
- reducers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
- reducers[i].setTotalTimeMs(smallTimeTaken, true);
- reducers[i].setCounter(jobCounter);
- }
- for (; i < numSmallTasks + numLongTasks; i++) {
- reducers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
- reducers[i].setTotalTimeMs(longTimeTaken, true);
- reducers[i].setCounter(jobCounter);
- }
- // Non-sampled task, which does not contain time data
- reducers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
- TezApplicationData data = new TezApplicationData().setCounters(jobCounter).setReduceTaskData(reducers);
- HeuristicResult result = _heuristic.apply(data);
- return result.getSeverity();
-
- }
-}
\ No newline at end of file
diff --git a/test/com/linkedin/drelephant/tez/heuristics/ReducerGCHeuristicTest.java b/test/com/linkedin/drelephant/tez/heuristics/ReducerGCHeuristicTest.java
deleted file mode 100644
index 4a86fbefb..000000000
--- a/test/com/linkedin/drelephant/tez/heuristics/ReducerGCHeuristicTest.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright 2017 Electronic Arts Inc.
- *
- * 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.drelephant.tez.heuristics;
-
-import com.linkedin.drelephant.analysis.ApplicationType;
-import com.linkedin.drelephant.analysis.Heuristic;
-import com.linkedin.drelephant.analysis.HeuristicResult;
-import com.linkedin.drelephant.analysis.Severity;
-import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
-import com.linkedin.drelephant.tez.data.TezApplicationData;
-import com.linkedin.drelephant.tez.data.TezCounterData;
-import com.linkedin.drelephant.tez.data.TezTaskData;
-import junit.framework.TestCase;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-
-public class ReducerGCHeuristicTest extends TestCase{
-
- private static Map paramsMap = new HashMap();
-
- private static Heuristic _heuristic = new ReducerGCHeuristic(new HeuristicConfigurationData("test_heuristic",
- "test_class", "test_view", new ApplicationType("test_apptype"), paramsMap));
-
- private static int NUMTASKS = 100;
-
- public void testGCCritical() throws IOException {
- assertEquals(Severity.CRITICAL, analyzeJob(1000000, 50000, 2000));
- }
-
- public void testGCSevere() throws IOException {
- assertEquals(Severity.SEVERE, analyzeJob(1000000, 50000, 1500));
- }
-
- public void testGCModerate() throws IOException {
- assertEquals(Severity.MODERATE, analyzeJob(1000000, 50000, 1000));
- }
-
- public void testGCNone() throws IOException {
- assertEquals(Severity.NONE, analyzeJob(1000000, 50000, 300));
- }
-
- public void testShortTasksNone() throws IOException {
- assertEquals(Severity.NONE, analyzeJob(100000, 50000, 2000));
- }
-
-
- private Severity analyzeJob(long runtimeMs, long cpuMs, long gcMs) throws IOException {
- TezCounterData jobCounter = new TezCounterData();
- TezTaskData[] reducers = new TezTaskData[NUMTASKS + 1];
-
- TezCounterData counter = new TezCounterData();
- counter.set(TezCounterData.CounterName.CPU_MILLISECONDS, cpuMs);
- counter.set(TezCounterData.CounterName.GC_TIME_MILLIS, gcMs);
-
- int i = 0;
- for (; i < NUMTASKS; i++) {
- reducers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
- reducers[i].setTimeAndCounter(new long[] { runtimeMs, 0, 0, 0, 0 }, counter);
- }
- // Non-sampled task, which does not contain time and counter data
- reducers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
-
- TezApplicationData data = new TezApplicationData().setCounters(jobCounter).setReduceTaskData(reducers);
- HeuristicResult result = _heuristic.apply(data);
- return result.getSeverity();
- }
-}
\ No newline at end of file
diff --git a/test/com/linkedin/drelephant/tez/heuristics/ReducerMemoryHeuristicTest.java b/test/com/linkedin/drelephant/tez/heuristics/ReducerMemoryHeuristicTest.java
deleted file mode 100644
index 66c3e193c..000000000
--- a/test/com/linkedin/drelephant/tez/heuristics/ReducerMemoryHeuristicTest.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright 2017 Electronic Arts Inc.
- *
- * 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.drelephant.tez.heuristics;
-
-import com.linkedin.drelephant.analysis.ApplicationType;
-import com.linkedin.drelephant.analysis.Heuristic;
-import com.linkedin.drelephant.analysis.HeuristicResult;
-import com.linkedin.drelephant.analysis.Severity;
-import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
-import com.linkedin.drelephant.tez.data.TezApplicationData;
-import com.linkedin.drelephant.tez.data.TezCounterData;
-import com.linkedin.drelephant.tez.data.TezTaskData;
-import junit.framework.TestCase;
-import org.apache.commons.io.FileUtils;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Properties;
-
-public class ReducerMemoryHeuristicTest extends TestCase {
-
- private static Map paramsMap = new HashMap();
- private static Heuristic _heuristic = new ReducerMemoryHeuristic(new HeuristicConfigurationData("test_heuristic",
- "test_class", "test_view", new ApplicationType("test_apptype"), paramsMap));
-
- private int NUMTASKS = 100;
-
- public void testLargeContainerSizeCritical() throws IOException {
- assertEquals(Severity.CRITICAL, analyzeJob(2048, 8192));
- }
-
- public void testLargeContainerSizeSevere() throws IOException {
- assertEquals(Severity.SEVERE, analyzeJob(3072, 8192));
- }
-
- public void testLargeContainerSizeModerate() throws IOException {
- assertEquals(Severity.MODERATE, analyzeJob(4096, 8192));
- }
-
- public void testLargeContainerSizeNone() throws IOException {
- assertEquals(Severity.NONE, analyzeJob(6144, 8192));
- }
-
- // If the task use default container size, it should not be flagged
- // Not using Default Container param, will calculate severity irrespective of default container size Chaning NONE -> CRITICAL
- public void testDefaultContainerNone() throws IOException {
- assertEquals(Severity.CRITICAL, analyzeJob(256, 2048));
- }
-
- // Not using Default Container param, will calculate severity irrespective of default container size Chaning NONE -> MODERATE
- public void testDefaultContainerNoneMore() throws IOException {
- assertEquals(Severity.MODERATE, analyzeJob(1024, 2048));
- }
-
- private Severity analyzeJob(long taskAvgMemMB, long containerMemMB) throws IOException {
- TezCounterData jobCounter = new TezCounterData();
- TezTaskData[] reducers = new TezTaskData[NUMTASKS + 1];
-
- TezCounterData counter = new TezCounterData();
- counter.set(TezCounterData.CounterName.PHYSICAL_MEMORY_BYTES, taskAvgMemMB* FileUtils.ONE_MB);
-
- Properties p = new Properties();
- p.setProperty(com.linkedin.drelephant.mapreduce.heuristics.ReducerMemoryHeuristic.REDUCER_MEMORY_CONF, Long.toString(containerMemMB));
-
- int i = 0;
- for (; i < NUMTASKS; i++) {
- reducers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
- reducers[i].setTimeAndCounter(new long[5], counter);
- }
- // Non-sampled task, which does not contain time and counter data
- reducers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
-
- TezApplicationData data = new TezApplicationData().setCounters(jobCounter).setReduceTaskData(reducers);
- data.setConf(p);
- HeuristicResult result = _heuristic.apply(data);
- return result.getSeverity();
- }
-}
\ No newline at end of file
diff --git a/test/com/linkedin/drelephant/tez/heuristics/ReducerTimeHeuristicTest.java b/test/com/linkedin/drelephant/tez/heuristics/ReducerTimeHeuristicTest.java
deleted file mode 100644
index ab0abbe38..000000000
--- a/test/com/linkedin/drelephant/tez/heuristics/ReducerTimeHeuristicTest.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright 2017 Electronic Arts Inc.
- *
- * 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.drelephant.tez.heuristics;
-
-import com.linkedin.drelephant.analysis.ApplicationType;
-import com.linkedin.drelephant.analysis.Heuristic;
-import com.linkedin.drelephant.analysis.HeuristicResult;
-import com.linkedin.drelephant.analysis.Severity;
-import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
-import com.linkedin.drelephant.math.Statistics;
-import com.linkedin.drelephant.tez.data.TezApplicationData;
-import com.linkedin.drelephant.tez.data.TezCounterData;
-import com.linkedin.drelephant.tez.data.TezTaskData;
-import junit.framework.TestCase;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-
-
-public class ReducerTimeHeuristicTest extends TestCase {
-
- private static Map paramsMap = new HashMap();
- private static Heuristic _heuristic = new ReducerTimeHeuristic(new HeuristicConfigurationData("test_heuristic",
- "test_class", "test_view", new ApplicationType("test_apptype"), paramsMap));
-
- private static final long MINUTE_IN_MS = Statistics.MINUTE_IN_MS;;
-
- public void testShortRunetimeCritical() throws IOException {
- assertEquals(Severity.CRITICAL, analyzeJob(1 * MINUTE_IN_MS, 1000));
- }
-
- public void testShortRunetimeSevere() throws IOException {
- assertEquals(Severity.SEVERE, analyzeJob(1 * MINUTE_IN_MS, 500));
- }
-
- public void testShortRunetimeModerate() throws IOException {
- assertEquals(Severity.MODERATE, analyzeJob(1 * MINUTE_IN_MS, 101));
- }
-
- public void testShortRunetimeLow() throws IOException {
- assertEquals(Severity.LOW, analyzeJob(1 * MINUTE_IN_MS, 50));
- }
-
- public void testShortRunetimeNone() throws IOException {
- assertEquals(Severity.NONE, analyzeJob(1 * MINUTE_IN_MS, 2));
- }
-
- public void testLongRunetimeCritical() throws IOException {
- assertEquals(Severity.CRITICAL, analyzeJob(120 * MINUTE_IN_MS, 10));
- }
-
- // Long runtime severity is not affected by number of tasks
- public void testLongRunetimeCriticalMore() throws IOException {
- assertEquals(Severity.CRITICAL, analyzeJob(120 * MINUTE_IN_MS, 1000));
- }
-
- public void testLongRunetimeSevere() throws IOException {
- assertEquals(Severity.SEVERE, analyzeJob(60 * MINUTE_IN_MS, 10));
- }
-
- public void testLongRunetimeSevereMore() throws IOException {
- assertEquals(Severity.SEVERE, analyzeJob(60 * MINUTE_IN_MS, 1000));
- }
-
- private Severity analyzeJob(long runtimeMs, int numTasks) throws IOException {
- TezCounterData dummyCounter = new TezCounterData();
- TezTaskData[] reducers = new TezTaskData[numTasks + 1];
-
- int i = 0;
- for (; i < numTasks; i++) {
- reducers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
- reducers[i].setTime(new long[] { runtimeMs, 0, 0, 0, 0 });
- reducers[i].setCounter(dummyCounter);
- }
- // Non-sampled task, which does not contain time and counter data
- reducers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
-
- TezApplicationData data = new TezApplicationData().setCounters(dummyCounter).setReduceTaskData(reducers);
- HeuristicResult result = _heuristic.apply(data);
- return result.getSeverity();
- }
-}
\ No newline at end of file
From d5476c1e005b30383a64dc6b3dda9d7d34c94ffd Mon Sep 17 00:00:00 2001
From: Chinmay Sumant
Date: Thu, 14 Jun 2018 09:55:08 +0530
Subject: [PATCH 10/15] Rerevert "Dr. Elephant Tez Support working patch
(#313)" including attribution.
This reverts commit e3fd598d048838006f2da26fe64538bd39d746ed.
Co-authored-by: Abhishek Das
---
app-conf/AggregatorConf.xml | 4 +
app-conf/FetcherConf.xml | 7 +
app-conf/HeuristicConf.xml | 117 ++++++
app-conf/JobTypeConf.xml | 6 +
.../fetchers/MapReduceFetcherHadoop2.java | 84 +---
.../drelephant/tez/TezMetricsAggregator.java | 109 +++++
.../tez/TezTaskLevelAggregatedMetrics.java | 154 +++++++
.../tez/data/TezApplicationData.java | 137 ++++++
.../drelephant/tez/data/TezCounterData.java | 195 +++++++++
.../drelephant/tez/data/TezTaskData.java | 137 ++++++
.../drelephant/tez/fetchers/TezFetcher.java | 391 ++++++++++++++++++
.../heuristics/GenericDataSkewHeuristic.java | 215 ++++++++++
.../tez/heuristics/GenericGCHeuristic.java | 142 +++++++
.../heuristics/GenericMemoryHeuristic.java | 185 +++++++++
.../heuristics/MapperDataSkewHeuristic.java | 47 +++
.../tez/heuristics/MapperGCHeuristic.java | 51 +++
.../tez/heuristics/MapperMemoryHeuristic.java | 45 ++
.../tez/heuristics/MapperSpeedHeuristic.java | 165 ++++++++
.../tez/heuristics/MapperSpillHeuristic.java | 142 +++++++
.../tez/heuristics/MapperTimeHeuristic.java | 182 ++++++++
.../heuristics/ReducerDataSkewHeuristic.java | 42 ++
.../tez/heuristics/ReducerGCHeuristic.java | 41 ++
.../heuristics/ReducerMemoryHeuristic.java | 47 +++
.../tez/heuristics/ReducerTimeHeuristic.java | 167 ++++++++
.../drelephant/util/InfoExtractor.java | 11 +
.../drelephant/util/ThreadContextMR2.java | 92 +++++
.../fetchers/MapReduceFetcherHadoop2Test.java | 2 +
.../TezTaskLevelAggregatedMetricsTest.java | 62 +++
.../tez/fetchers/TezFetcherTest.java | 35 ++
.../MapperDataSkewHeuristicTest.java | 146 +++++++
.../tez/heuristics/MapperGCHeuristicTest.java | 84 ++++
.../heuristics/MapperMemoryHeuristicTest.java | 94 +++++
.../heuristics/MapperSpeedHeuristicTest.java | 100 +++++
.../heuristics/MapperSpillHeuristicTest.java | 90 ++++
.../heuristics/MapperTimeHeuristicTest.java | 111 +++++
.../ReducerDataSkewHeuristicTest.java | 142 +++++++
.../heuristics/ReducerGCHeuristicTest.java | 83 ++++
.../ReducerMemoryHeuristicTest.java | 93 +++++
.../heuristics/ReducerTimeHeuristicTest.java | 97 +++++
39 files changed, 3971 insertions(+), 83 deletions(-)
create mode 100644 app/com/linkedin/drelephant/tez/TezMetricsAggregator.java
create mode 100644 app/com/linkedin/drelephant/tez/TezTaskLevelAggregatedMetrics.java
create mode 100644 app/com/linkedin/drelephant/tez/data/TezApplicationData.java
create mode 100644 app/com/linkedin/drelephant/tez/data/TezCounterData.java
create mode 100644 app/com/linkedin/drelephant/tez/data/TezTaskData.java
create mode 100644 app/com/linkedin/drelephant/tez/fetchers/TezFetcher.java
create mode 100644 app/com/linkedin/drelephant/tez/heuristics/GenericDataSkewHeuristic.java
create mode 100644 app/com/linkedin/drelephant/tez/heuristics/GenericGCHeuristic.java
create mode 100644 app/com/linkedin/drelephant/tez/heuristics/GenericMemoryHeuristic.java
create mode 100644 app/com/linkedin/drelephant/tez/heuristics/MapperDataSkewHeuristic.java
create mode 100644 app/com/linkedin/drelephant/tez/heuristics/MapperGCHeuristic.java
create mode 100644 app/com/linkedin/drelephant/tez/heuristics/MapperMemoryHeuristic.java
create mode 100644 app/com/linkedin/drelephant/tez/heuristics/MapperSpeedHeuristic.java
create mode 100644 app/com/linkedin/drelephant/tez/heuristics/MapperSpillHeuristic.java
create mode 100644 app/com/linkedin/drelephant/tez/heuristics/MapperTimeHeuristic.java
create mode 100644 app/com/linkedin/drelephant/tez/heuristics/ReducerDataSkewHeuristic.java
create mode 100644 app/com/linkedin/drelephant/tez/heuristics/ReducerGCHeuristic.java
create mode 100644 app/com/linkedin/drelephant/tez/heuristics/ReducerMemoryHeuristic.java
create mode 100644 app/com/linkedin/drelephant/tez/heuristics/ReducerTimeHeuristic.java
create mode 100644 app/com/linkedin/drelephant/util/ThreadContextMR2.java
create mode 100644 test/com/linkedin/drelephant/tez/TezTaskLevelAggregatedMetricsTest.java
create mode 100644 test/com/linkedin/drelephant/tez/fetchers/TezFetcherTest.java
create mode 100644 test/com/linkedin/drelephant/tez/heuristics/MapperDataSkewHeuristicTest.java
create mode 100644 test/com/linkedin/drelephant/tez/heuristics/MapperGCHeuristicTest.java
create mode 100644 test/com/linkedin/drelephant/tez/heuristics/MapperMemoryHeuristicTest.java
create mode 100644 test/com/linkedin/drelephant/tez/heuristics/MapperSpeedHeuristicTest.java
create mode 100644 test/com/linkedin/drelephant/tez/heuristics/MapperSpillHeuristicTest.java
create mode 100644 test/com/linkedin/drelephant/tez/heuristics/MapperTimeHeuristicTest.java
create mode 100644 test/com/linkedin/drelephant/tez/heuristics/ReducerDataSkewHeuristicTest.java
create mode 100644 test/com/linkedin/drelephant/tez/heuristics/ReducerGCHeuristicTest.java
create mode 100644 test/com/linkedin/drelephant/tez/heuristics/ReducerMemoryHeuristicTest.java
create mode 100644 test/com/linkedin/drelephant/tez/heuristics/ReducerTimeHeuristicTest.java
diff --git a/app-conf/AggregatorConf.xml b/app-conf/AggregatorConf.xml
index 23586d587..1536b2d96 100644
--- a/app-conf/AggregatorConf.xml
+++ b/app-conf/AggregatorConf.xml
@@ -33,6 +33,10 @@
mapreduce
com.linkedin.drelephant.mapreduce.MapReduceMetricsAggregator
+
+ tez
+ com.linkedin.drelephant.tez.TezMetricsAggregator
+
spark
com.linkedin.drelephant.spark.SparkMetricsAggregator
diff --git a/app-conf/FetcherConf.xml b/app-conf/FetcherConf.xml
index 1f813cf15..89dafc887 100644
--- a/app-conf/FetcherConf.xml
+++ b/app-conf/FetcherConf.xml
@@ -29,6 +29,13 @@
-->
+
+
+ tez
+ com.linkedin.drelephant.tez.fetchers.TezFetcher
+
+
+
+
+ tez
+ Mapper Data Skew
+ com.linkedin.drelephant.tez.heuristics.MapperDataSkewHeuristic
+ views.html.help.mapreduce.helpMapperSpill
+
+
+
+
+ tez
+ Mapper GC
+ com.linkedin.drelephant.tez.heuristics.MapperGCHeuristic
+ views.html.help.mapreduce.helpMapperSpill
+
+
+
+
+ tez
+ Mapper Time
+ com.linkedin.drelephant.tez.heuristics.MapperTimeHeuristic
+ views.html.help.mapreduce.helpMapperSpill
+
+
+
+
+ tez
+ Mapper Speed
+ com.linkedin.drelephant.tez.heuristics.MapperSpeedHeuristic
+ views.html.help.mapreduce.helpMapperSpill
+
+
+
+
+ tez
+ Mapper Memory
+ com.linkedin.drelephant.tez.heuristics.MapperMemoryHeuristic
+ views.html.help.mapreduce.helpMapperSpill
+
+
+
+
+ tez
+ Mapper Spill
+ com.linkedin.drelephant.tez.heuristics.MapperSpillHeuristic
+ views.html.help.mapreduce.helpMapperSpill
+
+
+
+
+ tez
+ Reducer Data Skew
+ com.linkedin.drelephant.tez.heuristics.ReducerDataSkewHeuristic
+ views.html.help.mapreduce.helpMapperSpill
+
+
+
+
+ tez
+ Reducer GC
+ com.linkedin.drelephant.tez.heuristics.ReducerGCHeuristic
+ views.html.help.mapreduce.helpMapperSpill
+
+
+
+
+ tez
+ Reducer Time
+ com.linkedin.drelephant.tez.heuristics.ReducerTimeHeuristic
+ views.html.help.mapreduce.helpMapperSpill
+
+
+
+
+ tez
+ Reducer Memory
+ com.linkedin.drelephant.tez.heuristics.ReducerMemoryHeuristic
+ views.html.help.mapreduce.helpMapperSpill
+
+
+
diff --git a/app-conf/JobTypeConf.xml b/app-conf/JobTypeConf.xml
index 8a4cae3eb..9dc4aa5ba 100644
--- a/app-conf/JobTypeConf.xml
+++ b/app-conf/JobTypeConf.xml
@@ -42,6 +42,12 @@
mapreduce
pig.script
+
+ Tez
+ tez
+ hive.mapred.mode
+
+
Hive
mapreduce
diff --git a/app/com/linkedin/drelephant/mapreduce/fetchers/MapReduceFetcherHadoop2.java b/app/com/linkedin/drelephant/mapreduce/fetchers/MapReduceFetcherHadoop2.java
index 4165971aa..5a8a64425 100644
--- a/app/com/linkedin/drelephant/mapreduce/fetchers/MapReduceFetcherHadoop2.java
+++ b/app/com/linkedin/drelephant/mapreduce/fetchers/MapReduceFetcherHadoop2.java
@@ -20,30 +20,23 @@
import com.linkedin.drelephant.mapreduce.data.MapReduceApplicationData;
import com.linkedin.drelephant.mapreduce.data.MapReduceCounterData;
import com.linkedin.drelephant.mapreduce.data.MapReduceTaskData;
-import com.linkedin.drelephant.math.Statistics;
import com.linkedin.drelephant.configurations.fetcher.FetcherConfigurationData;
+import com.linkedin.drelephant.util.ThreadContextMR2;
import com.linkedin.drelephant.util.Utils;
import java.io.IOException;
-import java.lang.Integer;
-import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
-import java.util.Random;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
-import java.util.regex.Pattern;
import org.apache.hadoop.conf.Configuration;
-import org.apache.hadoop.security.authentication.client.AuthenticatedURL;
import org.apache.hadoop.security.authentication.client.AuthenticationException;
import org.apache.log4j.Logger;
import org.codehaus.jackson.JsonNode;
-import org.codehaus.jackson.map.ObjectMapper;
/**
@@ -387,78 +380,3 @@ private JsonNode getTaskFirstFailedAttempt(URL taskAllAttemptsUrl) throws IOExce
}
}
}
-
-final class ThreadContextMR2 {
- private static final Logger logger = Logger.getLogger(ThreadContextMR2.class);
- private static final AtomicInteger THREAD_ID = new AtomicInteger(1);
-
- private static final ThreadLocal _LOCAL_THREAD_ID = new ThreadLocal() {
- @Override
- public Integer initialValue() {
- return THREAD_ID.getAndIncrement();
- }
- };
-
- private static final ThreadLocal _LOCAL_LAST_UPDATED = new ThreadLocal();
- private static final ThreadLocal _LOCAL_UPDATE_INTERVAL = new ThreadLocal();
-
- private static final ThreadLocal _LOCAL_DIAGNOSTIC_PATTERN = new ThreadLocal() {
- @Override
- public Pattern initialValue() {
- // Example: "Task task_1443068695259_9143_m_000475 failed 1 times"
- return Pattern.compile(
- ".*[\\s\\u00A0]+(task_[0-9]+_[0-9]+_[m|r]_[0-9]+)[\\s\\u00A0]+.*");
- }
- };
-
- private static final ThreadLocal _LOCAL_AUTH_TOKEN =
- new ThreadLocal() {
- @Override
- public AuthenticatedURL.Token initialValue() {
- _LOCAL_LAST_UPDATED.set(System.currentTimeMillis());
- // Random an interval for each executor to avoid update token at the same time
- _LOCAL_UPDATE_INTERVAL.set(Statistics.MINUTE_IN_MS * 30 + new Random().nextLong()
- % (3 * Statistics.MINUTE_IN_MS));
- logger.info("Executor " + _LOCAL_THREAD_ID.get() + " update interval " + _LOCAL_UPDATE_INTERVAL.get() * 1.0
- / Statistics.MINUTE_IN_MS);
- return new AuthenticatedURL.Token();
- }
- };
-
- private static final ThreadLocal _LOCAL_AUTH_URL = new ThreadLocal() {
- @Override
- public AuthenticatedURL initialValue() {
- return new AuthenticatedURL();
- }
- };
-
- private static final ThreadLocal _LOCAL_MAPPER = new ThreadLocal() {
- @Override
- public ObjectMapper initialValue() {
- return new ObjectMapper();
- }
- };
-
- private ThreadContextMR2() {
- // Empty on purpose
- }
-
- public static Matcher getDiagnosticMatcher(String diagnosticInfo) {
- return _LOCAL_DIAGNOSTIC_PATTERN.get().matcher(diagnosticInfo);
- }
-
- public static JsonNode readJsonNode(URL url) throws IOException, AuthenticationException {
- HttpURLConnection conn = _LOCAL_AUTH_URL.get().openConnection(url, _LOCAL_AUTH_TOKEN.get());
- return _LOCAL_MAPPER.get().readTree(conn.getInputStream());
- }
-
- public static void updateAuthToken() {
- long curTime = System.currentTimeMillis();
- if (curTime - _LOCAL_LAST_UPDATED.get() > _LOCAL_UPDATE_INTERVAL.get()) {
- logger.info("Executor " + _LOCAL_THREAD_ID.get() + " updates its AuthenticatedToken.");
- _LOCAL_AUTH_TOKEN.set(new AuthenticatedURL.Token());
- _LOCAL_AUTH_URL.set(new AuthenticatedURL());
- _LOCAL_LAST_UPDATED.set(curTime);
- }
- }
-}
diff --git a/app/com/linkedin/drelephant/tez/TezMetricsAggregator.java b/app/com/linkedin/drelephant/tez/TezMetricsAggregator.java
new file mode 100644
index 000000000..c04edd7fd
--- /dev/null
+++ b/app/com/linkedin/drelephant/tez/TezMetricsAggregator.java
@@ -0,0 +1,109 @@
+/*
+ *
+ * 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.drelephant.tez;
+
+import com.linkedin.drelephant.analysis.*;
+import com.linkedin.drelephant.configurations.aggregator.AggregatorConfigurationData;
+import com.linkedin.drelephant.tez.data.TezApplicationData;
+import org.apache.commons.io.FileUtils;
+import org.apache.log4j.Logger;
+
+/**
+ * Aggregates task level metrics to application
+ */
+
+public class TezMetricsAggregator implements HadoopMetricsAggregator {
+
+ private static final Logger logger = Logger.getLogger(TezMetricsAggregator.class);
+
+ private static final String TEZ_CONTAINER_CONFIG = "hive.tez.container.size";
+ private static final String MAP_CONTAINER_CONFIG = "mapreduce.map.memory.mb";
+ private static final String REDUCER_CONTAINER_CONFIG = "mapreduce.reduce.memory.mb";
+ private static final String REDUCER_SLOW_START_CONFIG = "mapreduce.job.reduce.slowstart.completedmaps";
+ private static final long CONTAINER_MEMORY_DEFAULT_BYTES = 2048L * FileUtils.ONE_MB;
+
+ private HadoopAggregatedData _hadoopAggregatedData = null;
+ private TezTaskLevelAggregatedMetrics _mapTasks;
+ private TezTaskLevelAggregatedMetrics _reduceTasks;
+
+ private AggregatorConfigurationData _aggregatorConfigurationData;
+
+ public TezMetricsAggregator(AggregatorConfigurationData _aggregatorConfigurationData) {
+ this._aggregatorConfigurationData = _aggregatorConfigurationData;
+ _hadoopAggregatedData = new HadoopAggregatedData();
+ }
+
+ @Override
+ public void aggregate(HadoopApplicationData hadoopData) {
+
+ TezApplicationData data = (TezApplicationData) hadoopData;
+
+ long mapTaskContainerSize = getMapContainerSize(data);
+ long reduceTaskContainerSize = getReducerContainerSize(data);
+
+ int reduceTaskSlowStartPercentage =
+ (int) (Double.parseDouble(data.getConf().getProperty(REDUCER_SLOW_START_CONFIG)) * 100);
+
+
+ //overwrite reduceTaskSlowStartPercentage to 100%. TODO: make use of the slow start percent
+ reduceTaskSlowStartPercentage = 100;
+
+ _mapTasks = new TezTaskLevelAggregatedMetrics(data.getMapTaskData(), mapTaskContainerSize, data.getStartTime());
+
+ long reduceIdealStartTime = _mapTasks.getNthPercentileFinishTime(reduceTaskSlowStartPercentage);
+
+ // Mappers list is empty
+ if(reduceIdealStartTime == -1) {
+ // ideal start time for reducer is infinite since it cannot start
+ reduceIdealStartTime = Long.MAX_VALUE;
+ }
+
+ _reduceTasks = new TezTaskLevelAggregatedMetrics(data.getReduceTaskData(), reduceTaskContainerSize, reduceIdealStartTime);
+
+ _hadoopAggregatedData.setResourceUsed(_mapTasks.getResourceUsed() + _reduceTasks.getResourceUsed());
+ _hadoopAggregatedData.setTotalDelay(_mapTasks.getDelay() + _reduceTasks.getDelay());
+ _hadoopAggregatedData.setResourceWasted(_mapTasks.getResourceWasted() + _reduceTasks.getResourceWasted());
+ }
+
+ @Override
+ public HadoopAggregatedData getResult() {
+ return _hadoopAggregatedData;
+ }
+
+ private long getMapContainerSize(HadoopApplicationData data) {
+ try {
+ long mapContainerSize = Long.parseLong(data.getConf().getProperty(TEZ_CONTAINER_CONFIG));
+ if (mapContainerSize > 0)
+ return mapContainerSize;
+ else
+ return Long.parseLong(data.getConf().getProperty(MAP_CONTAINER_CONFIG));
+ } catch ( NumberFormatException ex) {
+ return CONTAINER_MEMORY_DEFAULT_BYTES;
+ }
+ }
+
+ private long getReducerContainerSize(HadoopApplicationData data) {
+ try {
+ long reducerContainerSize = Long.parseLong(data.getConf().getProperty(TEZ_CONTAINER_CONFIG));
+ if (reducerContainerSize > 0)
+ return reducerContainerSize;
+ else
+ return Long.parseLong(data.getConf().getProperty(REDUCER_CONTAINER_CONFIG));
+ } catch ( NumberFormatException ex) {
+ return CONTAINER_MEMORY_DEFAULT_BYTES;
+ }
+ }
+}
diff --git a/app/com/linkedin/drelephant/tez/TezTaskLevelAggregatedMetrics.java b/app/com/linkedin/drelephant/tez/TezTaskLevelAggregatedMetrics.java
new file mode 100644
index 000000000..0f5eaa5f6
--- /dev/null
+++ b/app/com/linkedin/drelephant/tez/TezTaskLevelAggregatedMetrics.java
@@ -0,0 +1,154 @@
+/*
+ *
+ * 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.drelephant.tez;
+
+import com.linkedin.drelephant.tez.data.TezCounterData;
+import com.linkedin.drelephant.tez.data.TezTaskData;
+import com.linkedin.drelephant.math.Statistics;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.io.FileUtils;
+import org.apache.log4j.Logger;
+
+/**
+ * Aggregation functionality for task level metrics
+ */
+
+public class TezTaskLevelAggregatedMetrics {
+
+ private static final Logger logger = Logger.getLogger(TezTaskLevelAggregatedMetrics.class);
+
+ private long _delay = 0;
+ private long _resourceWasted = 0;
+ private long _resourceUsed = 0;
+
+ private List finishTimes = new ArrayList();
+ private List durations = new ArrayList();
+
+ private static final double MEMORY_BUFFER = 1.5;
+ private static final double CLUSTER_MEMORY_FACTOR = 2.1;
+
+ /**
+ * Returns the nth percentile finish job
+ * @param percentile The percentile of finish job to return
+ * @return The nth percentile finish job
+ */
+ public long getNthPercentileFinishTime(int percentile)
+ {
+ if(finishTimes == null || finishTimes.size() == 0 ) {
+ return -1;
+ }
+ return Statistics.percentile(finishTimes, percentile);
+ }
+
+ /**
+ * Constructor for TaskLevelAggregatedMetrics
+ * @param taskData Array containing the task data for mappers and/or reducers
+ * @param containerSize The container size of the tasks
+ * @param idealStartTime The ideal start time for the task. For mappers it is the submit time, for
+ * reducers, it is the time when the number of completed maps become more than
+ * the slow start time.
+ */
+ public TezTaskLevelAggregatedMetrics(TezTaskData[] taskData, long containerSize, long idealStartTime) {
+ compute(taskData, containerSize, idealStartTime);
+ }
+
+ /**
+ * Returns the overall delay for the tasks.
+ * @return The delay of the tasks.
+ */
+ public long getDelay() {
+ return _delay;
+ }
+
+ /**
+ * Retruns the resources wasted by all the tasks in MB Seconds
+ * @return The wasted resources of all the tasks in MB Seconds
+ */
+ public long getResourceWasted() {
+ return _resourceWasted;
+ }
+
+ /**
+ * Returns the resource used by all the tasks in MB Seconds
+ * @return The total resources used by all tasks in MB Seconds
+ */
+ public long getResourceUsed() {
+ return _resourceUsed;
+ }
+
+ /**
+ * Computes the aggregated metrics -> peakMemory, delay, total task duration, wasted resources and memory usage.
+ * @param taskDatas
+ * @param containerSize
+ * @param idealStartTime
+ */
+ private void compute(TezTaskData[] taskDatas, long containerSize, long idealStartTime) {
+
+ long peakMemoryNeed = 0;
+ long taskFinishTimeMax = 0;
+ long taskDurationMax = 0;
+
+ // if there are zero tasks, then nothing to compute.
+ if(taskDatas == null || taskDatas.length == 0) {
+ return;
+ }
+
+ for (TezTaskData taskData: taskDatas) {
+ if (!taskData.isSampled()) {
+ continue;
+ }
+ long taskMemory = taskData.getCounters().get(TezCounterData.CounterName.PHYSICAL_MEMORY_BYTES)/ FileUtils.ONE_MB; // MB
+ long taskVM = taskData.getCounters().get(TezCounterData.CounterName.VIRTUAL_MEMORY_BYTES)/ FileUtils.ONE_MB; // MB
+ long taskDuration = taskData.getFinishTime() - taskData.getStartTime(); // Milliseconds
+ long taskCost = (containerSize) * (taskDuration / Statistics.SECOND_IN_MS); // MB Seconds
+
+ durations.add(taskDuration);
+ finishTimes.add(taskData.getFinishTime());
+
+ //peak Memory usage
+ long memoryRequiredForVM = (long) (taskVM/CLUSTER_MEMORY_FACTOR);
+ long biggerMemoryRequirement = memoryRequiredForVM > taskMemory ? memoryRequiredForVM : taskMemory;
+ peakMemoryNeed = biggerMemoryRequirement > peakMemoryNeed ? biggerMemoryRequirement : peakMemoryNeed;
+
+ if(taskFinishTimeMax < taskData.getFinishTime()) {
+ taskFinishTimeMax = taskData.getFinishTime();
+ }
+
+ if(taskDurationMax < taskDuration) {
+ taskDurationMax = taskDuration;
+ }
+ _resourceUsed += taskCost;
+ }
+
+ // Compute the delay in starting the task.
+ _delay = taskFinishTimeMax - (idealStartTime + taskDurationMax);
+
+ // invalid delay
+ if(_delay < 0) {
+ _delay = 0;
+ }
+
+ // wastedResources
+ long wastedMemory = containerSize - (long) (peakMemoryNeed * MEMORY_BUFFER);
+ if(wastedMemory > 0) {
+ for (long duration : durations) {
+ _resourceWasted += (wastedMemory) * (duration / Statistics.SECOND_IN_MS); // MB Seconds
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/com/linkedin/drelephant/tez/data/TezApplicationData.java b/app/com/linkedin/drelephant/tez/data/TezApplicationData.java
new file mode 100644
index 000000000..f38f527bc
--- /dev/null
+++ b/app/com/linkedin/drelephant/tez/data/TezApplicationData.java
@@ -0,0 +1,137 @@
+/*
+ *
+ * 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.drelephant.tez.data;
+
+import com.linkedin.drelephant.analysis.ApplicationType;
+import com.linkedin.drelephant.analysis.HadoopApplicationData;
+
+import java.util.Properties;
+
+/**
+ * Tez Application level data structure which hold all task data
+ */
+public class TezApplicationData implements HadoopApplicationData {
+
+ private static final ApplicationType APPLICATION_TYPE = new ApplicationType("TEZ");
+
+ private String _appId = "";
+ private Properties _conf;
+ private boolean _succeeded = true;
+ private TezTaskData[] _reduceTasks;
+ private TezTaskData[] _mapTasks;
+ private TezCounterData _counterHolder;
+
+ private long _submitTime = 0;
+ private long _startTime = 0;
+ private long _finishTime = 0;
+
+ public boolean getSucceeded() {
+ return _succeeded;
+ }
+
+ @Override
+ public String getAppId() {
+ return _appId;
+ }
+
+ @Override
+ public Properties getConf() {
+ return _conf;
+ }
+
+ @Override
+ public ApplicationType getApplicationType() {
+ return APPLICATION_TYPE;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return _succeeded && getMapTaskData().length == 0 && getReduceTaskData().length == 0;
+ }
+
+ public TezTaskData[] getReduceTaskData() {
+ return _reduceTasks;
+ }
+
+ public TezTaskData[] getMapTaskData() {
+ return _mapTasks;
+ }
+
+ public long getSubmitTime() {
+ return _submitTime;
+ }
+
+ public long getStartTime() {
+ return _startTime;
+ }
+
+ public long getFinishTime() {
+ return _finishTime;
+ }
+
+ public TezCounterData getCounters() {
+ return _counterHolder;
+ }
+
+ public TezApplicationData setCounters(TezCounterData counterHolder) {
+ this._counterHolder = counterHolder;
+ return this;
+ }
+
+ public TezApplicationData setAppId(String appId) {
+ this._appId = appId;
+ return this;
+ }
+
+ public TezApplicationData setConf(Properties conf) {
+ this._conf = conf;
+ return this;
+ }
+
+ public TezApplicationData setSucceeded(boolean succeeded) {
+ this._succeeded = succeeded;
+ return this;
+ }
+
+ public TezApplicationData setReduceTaskData(TezTaskData[] reduceTasks) {
+ this._reduceTasks = reduceTasks;
+ return this;
+ }
+
+ public TezApplicationData setMapTaskData(TezTaskData[] mapTasks) {
+ this._mapTasks = mapTasks;
+ return this;
+ }
+
+ public TezApplicationData setSubmitTime(long submitTime) {
+ this._submitTime = submitTime;
+ return this;
+ }
+
+ public TezApplicationData setStartTime(long startTime) {
+ this._startTime = startTime;
+ return this;
+ }
+
+ public TezApplicationData setFinishTime(long finishTime) {
+ this._finishTime = finishTime;
+ return this;
+ }
+
+ public String toString(){
+ return APPLICATION_TYPE.toString() + " " + _appId;
+ }
+}
\ No newline at end of file
diff --git a/app/com/linkedin/drelephant/tez/data/TezCounterData.java b/app/com/linkedin/drelephant/tez/data/TezCounterData.java
new file mode 100644
index 000000000..9aac00117
--- /dev/null
+++ b/app/com/linkedin/drelephant/tez/data/TezCounterData.java
@@ -0,0 +1,195 @@
+/*
+ *
+ * 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.drelephant.tez.data;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Tez Counter Data defining data structure.
+ */
+public class TezCounterData {
+ // Map to group counters into DAG, Task and application levels.
+ private final Map> _pubCounters;
+
+ public String toString() {
+ return _pubCounters.toString();
+ }
+
+ public TezCounterData() {
+ _pubCounters = new HashMap>(8);
+ }
+
+ public long get(CounterName counterName) {
+ for(Map counterGrp : _pubCounters.values()) {
+ if(counterGrp.containsKey(counterName._name)) {
+ return counterGrp.get(counterName._name);
+ }
+ }
+ return 0;
+ }
+
+ public void set(CounterName counterName, long value) {
+ set(counterName.getGroupName(), counterName.getName(), value);
+ }
+
+ public void set(String groupName, String counterName, long value) {
+ Map counterMap = _pubCounters.get(groupName);
+ if (counterMap == null) {
+ counterMap = new HashMap(4);
+ _pubCounters.put(groupName, counterMap);
+ }
+ counterMap.put(counterName, value);
+ }
+
+ public Set getGroupNames() {
+ Set groupNames = _pubCounters.keySet();
+ return Collections.unmodifiableSet(groupNames);
+ }
+
+ public Map getAllCountersInGroup(String groupName) {
+ Map counterMap = _pubCounters.get(groupName);
+ if (counterMap == null) {
+ counterMap = new HashMap(1);
+ }
+ return counterMap;
+ }
+
+ public static enum GroupName {
+ FileSystemCounters("org.apache.tez.common.counters.FileSystemCounter"),
+ TezTask("org.apache.tez.common.counters.TaskCounter"),
+ TezDag("org.apache.tez.common.counters.DAGCounter");
+
+
+ String _name;
+ GroupName(String name) {
+ _name = name;
+ }
+ }
+
+ public static enum CounterName {
+
+ NUM_SUCCEEDED_TASKS(GroupName.TezDag, "NUM_SUCCEEDED_TASKS", "NUM_SUCCEEDED_TASKS"),
+ TOTAL_LAUNCHED_TASKS(GroupName.TezDag, "TOTAL_LAUNCHED_TASKS", "TOTAL_LAUNCHED_TASKS"),
+ RACK_LOCAL_TASKS(GroupName.TezDag, "RACK_LOCAL_TASKS", "RACK_LOCAL_TASKS"),
+ AM_CPU_MILLISECONDS(GroupName.TezDag, "AM_CPU_MILLISECONDS", "AM_CPU_MILLISECONDS"),
+ AM_GC_TIME_MILLIS(GroupName.TezDag, "AM_GC_TIME_MILLIS", "AM_GC_TIME_MILLIS"),
+
+ FILE_BYTES_READ(GroupName.FileSystemCounters, "FILE_BYTES_READ", "FILE_BYTES_READ"),
+ FILE_BYTES_WRITTEN(GroupName.FileSystemCounters, "FILE_BYTES_WRITTEN", "FILE_BYTES_WRITTEN"),
+ FILE_READ_OPS(GroupName.FileSystemCounters, "FILE_READ_OPS", "FILE_READ_OPS"),
+ FILE_LARGE_READ_OPS(GroupName.FileSystemCounters, "FILE_LARGE_READ_OPS", "FILE_LARGE_READ_OPS"),
+ FILE_WRITE_OPS(GroupName.FileSystemCounters, "FILE_WRITE_OPS", "FILE_WRITE_OPS"),
+ HDFS_BYTES_READ(GroupName.FileSystemCounters, "HDFS_BYTES_READ", "HDFS_BYTES_READ"),
+ HDFS_BYTES_WRITTEN(GroupName.FileSystemCounters, "HDFS_BYTES_WRITTEN", "HDFS_BYTES_WRITTEN"),
+ HDFS_READ_OPS(GroupName.FileSystemCounters, "HDFS_READ_OPS", "HDFS_READ_OPS"),
+ HDFS_LARGE_READ_OPS(GroupName.FileSystemCounters, "HDFS_LARGE_READ_OPS", "HDFS_LARGE_READ_OPS"),
+ HDFS_WRITE_OPS(GroupName.FileSystemCounters, "HDFS_WRITE_OPS", "HDFS_WRITE_OPS"),
+ S3A_BYTES_READ(GroupName.FileSystemCounters, "S3A_BYTES_READ", "S3A_BYTES_READ"),
+ S3A_BYTES_WRITTEN(GroupName.FileSystemCounters, "S3A_BYTES_WRITTEN", "S3A_BYTES_WRITTEN"),
+ S3A_READ_OPS(GroupName.FileSystemCounters, "S3A_READ_OPS", "S3A_READ_OPS"),
+ S3A_LARGE_READ_OPS(GroupName.FileSystemCounters, "S3A_LARGE_READ_OPS", "S3A_LARGE_READ_OPS"),
+ S3A_WRITE_OPS(GroupName.FileSystemCounters, "S3A_WRITE_OPS", "S3_WRITE_OPS"),
+ S3N_BYTES_READ(GroupName.FileSystemCounters, "S3N_BYTES_READ", "S3N_BYTES_READ"),
+ S3N_BYTES_WRITTEN(GroupName.FileSystemCounters, "S3N_BYTES_WRITTEN", "S3N_BYTES_WRITTEN"),
+ S3N_READ_OPS(GroupName.FileSystemCounters, "S3N_READ_OPS", "S3N_READ_OPS"),
+ S3N_LARGE_READ_OPS(GroupName.FileSystemCounters, "S3N_LARGE_READ_OPS", "S3N_LARGE_READ_OPS"),
+ S3N_WRITE_OPS(GroupName.FileSystemCounters, "S3N_WRITE_OPS", "S3N_WRITE_OPS"),
+
+ REDUCE_INPUT_GROUPS(GroupName.TezTask, "REDUCE_INPUT_GROUPS", "REDUCE_INPUT_GROUPS"),
+ REDUCE_INPUT_RECORDS(GroupName.TezTask, "REDUCE_INPUT_RECORDS", "REDUCE_INPUT_RECORDS"),
+ COMBINE_INPUT_RECORDS(GroupName.TezTask, "COMBINE_INPUT_RECORDS", "COMBINE_INPUT_RECORDS"),
+ SPILLED_RECORDS(GroupName.TezTask, "SPILLED_RECORDS", "SPILLED_RECORDS"),
+ NUM_SHUFFLED_INPUTS(GroupName.TezTask, "NUM_SHUFFLED_INPUTS", "NUM_SHUFFLED_INPUTS"),
+ NUM_SKIPPED_INPUTS(GroupName.TezTask, "NUM_SKIPPED_INPUTS", "NUM_SKIPPED_INPUTS"),
+ NUM_FAILED_SHUFFLE_INPUTS(GroupName.TezTask, "NUM_FAILED_SHUFFLE_INPUTS", "NUM_FAILED_SHUFFLE_INPUTS"),
+ MERGED_MAP_OUTPUTS(GroupName.TezTask, "MERGED_MAP_OUTPUTS", "MERGED_MAP_OUTPUTS"),
+ GC_TIME_MILLIS(GroupName.TezTask, "GC_TIME_MILLIS", "GC_TIME_MILLIS"),
+ COMMITTED_HEAP_BYTES(GroupName.TezTask, "COMMITTED_HEAP_BYTES", "COMMITTED_HEAP_BYTES"),
+ INPUT_RECORDS_PROCESSED(GroupName.TezTask, "INPUT_RECORDS_PROCESSED", "INPUT_RECORDS_PROCESSED"),
+ OUTPUT_RECORDS(GroupName.TezTask, "OUTPUT_RECORDS", "OUTPUT_RECORDS"),
+ OUTPUT_BYTES(GroupName.TezTask, "OUTPUT_BYTES", "OUTPUT_BYTES"),
+ OUTPUT_BYTES_WITH_OVERHEAD(GroupName.TezTask, "OUTPUT_BYTES_WITH_OVERHEAD", "OUTPUT_BYTES_WITH_OVERHEAD"),
+ OUTPUT_BYTES_PHYSICAL(GroupName.TezTask, "OUTPUT_BYTES_PHYSICAL", "OUTPUT_BYTES_PHYSICAL"),
+ ADDITIONAL_SPILLS_BYTES_WRITTEN(GroupName.TezTask, "ADDITIONAL_SPILLS_BYTES_WRITTEN", "ADDITIONAL_SPILLS_BYTES_WRITTEN"),
+ ADDITIONAL_SPILLS_BYTES_READ(GroupName.TezTask, "ADDITIONAL_SPILLS_BYTES_READ", "ADDITIONAL_SPILLS_BYTES_READ"),
+ ADDITIONAL_SPILL_COUNT(GroupName.TezTask, "ADDITIONAL_SPILL_COUNT", "ADDITIONAL_SPILL_COUNT"),
+ SHUFFLE_BYTES(GroupName.TezTask, "SHUFFLE_BYTES", "SHUFFLE_BYTES"),
+ SHUFFLE_BYTES_DECOMPRESSED(GroupName.TezTask, "SHUFFLE_BYTES_DECOMPRESSED", "SHUFFLE_BYTES_DECOMPRESSED"),
+ SHUFFLE_BYTES_TO_MEM(GroupName.TezTask, "SHUFFLE_BYTES_TO_MEM", "SHUFFLE_BYTES_TO_MEM"),
+ SHUFFLE_BYTES_TO_DISK(GroupName.TezTask, "SHUFFLE_BYTES_TO_DISK", "SHUFFLE_BYTES_TO_DISK"),
+ SHUFFLE_BYTES_DISK_DIRECT(GroupName.TezTask, "SHUFFLE_BYTES_DISK_DIRECT", "SHUFFLE_BYTES_DISK_DIRECT"),
+ NUM_MEM_TO_DISK_MERGES(GroupName.TezTask, "NUM_MEM_TO_DISK_MERGES", "NUM_MEM_TO_DISK_MERGES"),
+ CPU_MILLISECONDS(GroupName.TezTask,"CPU_MILLISECONDS","CPU_MILLISECONDS"),
+ PHYSICAL_MEMORY_BYTES(GroupName.TezTask,"PHYSICAL_MEMORY_BYTES","PHYSICAL_MEMORY_BYTES"),
+ VIRTUAL_MEMORY_BYTES(GroupName.TezTask,"VIRTUAL_MEMORY_BYTES","VIRTUAL_MEMORY_BYTES"),
+ NUM_DISK_TO_DISK_MERGES(GroupName.TezTask, "NUM_DISK_TO_DISK_MERGES", "NUM_DISK_TO_DISK_MERGES"),
+ SHUFFLE_PHASE_TIME(GroupName.TezTask, "SHUFFLE_PHASE_TIME", "SHUFFLE_PHASE_TIME"),
+ MERGE_PHASE_TIME(GroupName.TezTask, "MERGE_PHASE_TIME", "MERGE_PHASE_TIME"),
+ FIRST_EVENT_RECEIVED(GroupName.TezTask, "FIRST_EVENT_RECEIVED", "FIRST_EVENT_RECEIVED"),
+ LAST_EVENT_RECEIVED(GroupName.TezTask, "LAST_EVENT_RECEIVED", "LAST_EVENT_RECEIVED");
+
+
+ GroupName _group;
+ String _name;
+ String _displayName;
+
+ CounterName(GroupName group, String name, String displayName) {
+ this._group = group;
+ this._name = name;
+ this._displayName = displayName;
+ }
+
+ static Map _counterDisplayNameMap;
+ static Map _counterNameMap;
+ static {
+ _counterDisplayNameMap = new HashMap();
+ _counterNameMap = new HashMap();
+ for (CounterName cn : CounterName.values()) {
+ _counterDisplayNameMap.put(cn._displayName, cn);
+ _counterNameMap.put(cn._name, cn);
+ }
+ }
+
+ public static CounterName getCounterFromName(String name) {
+ if (_counterNameMap.containsKey(name)) {
+ return _counterNameMap.get(name);
+ }
+ return null;
+ }
+
+ public static CounterName getCounterFromDisplayName(String displayName) {
+ if (_counterDisplayNameMap.containsKey(displayName)) {
+ return _counterDisplayNameMap.get(displayName);
+ }
+ return null;
+ }
+
+ public String getName() {
+ return _name;
+ }
+
+ public String getDisplayName() {
+ return _displayName;
+ }
+
+ public String getGroupName() {
+ return _group.name();
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/com/linkedin/drelephant/tez/data/TezTaskData.java b/app/com/linkedin/drelephant/tez/data/TezTaskData.java
new file mode 100644
index 000000000..eb6aa832c
--- /dev/null
+++ b/app/com/linkedin/drelephant/tez/data/TezTaskData.java
@@ -0,0 +1,137 @@
+/*
+ *
+ * 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.drelephant.tez.data;
+
+/**
+ * Tez Task Level metadata holding data structure
+ */
+
+public class TezTaskData {
+ private TezCounterData _counterHolder;
+ private String _taskId;
+ // The successful attempt id
+ private String _attemptId;
+
+ private long _totalTimeMs = 0;
+ private long _shuffleTimeMs = 0;
+ private long _sortTimeMs = 0;
+ private long _startTime = 0;
+ private long _finishTime = 0;
+ private boolean _isSampled = false;
+
+ //Constructor used only in Test Cases , if provided to partially assign needed time values while ignoring the others
+ public TezTaskData(TezCounterData counterHolder, long[] time) {
+ if(time == null || time.length<3){
+ time = new long[5];
+ }
+ this._counterHolder = counterHolder;
+ this._totalTimeMs = time[0];
+ this._shuffleTimeMs = time[1];
+ this._sortTimeMs = time[2];
+ if (time.length > 3)
+ this._startTime = time[3];
+ if (time.length > 4)
+ this._finishTime = time[4];
+ this._isSampled = true;
+ }
+
+ public TezTaskData(TezCounterData counterHolder) {
+ this._counterHolder = counterHolder;
+ }
+
+ public TezTaskData(String taskId, String taskAttemptId) {
+ this._taskId = taskId;
+ this._attemptId = taskAttemptId;
+ }
+
+ public void setCounter(TezCounterData counterHolder) {
+ this._counterHolder = counterHolder;
+ this._isSampled = true;
+ }
+
+ public void setTime(long[] time) {
+ //No Validation needed here as time array will always be of fixed length 5 from upstream methods.
+ this._totalTimeMs = time[0];
+ this._shuffleTimeMs = time[1];
+ this._sortTimeMs = time[2];
+ this._startTime = time[3];
+ this._finishTime = time[4];
+ this._isSampled = true;
+ }
+
+ //Used only in Test Cases
+ public void setTimeAndCounter(long[] time, TezCounterData counterHolder){
+ if(time == null || time.length<3){
+ time = new long[5];
+ }
+ this._totalTimeMs = time[0];
+ this._shuffleTimeMs = time[1];
+ this._sortTimeMs = time[2];
+ if (time.length > 3)
+ this._startTime = time[3];
+ if (time.length > 4)
+ this._finishTime = time[4];
+ this._isSampled = true;
+ this._counterHolder = counterHolder;
+
+
+ }
+
+ public TezCounterData getCounters() {
+ return _counterHolder;
+ }
+
+ public long getTotalRunTimeMs() {
+ return _totalTimeMs;
+ }
+
+ public long getCodeExecutionTimeMs() {
+ return _totalTimeMs - _shuffleTimeMs - _sortTimeMs;
+ }
+
+ public long getShuffleTimeMs() {
+ return _shuffleTimeMs;
+ }
+
+ public long getSortTimeMs() {
+ return _sortTimeMs;
+ }
+
+ public boolean isSampled() {
+ return _isSampled;
+ }
+
+ public String getTaskId() {
+ return _taskId;
+ }
+
+ public String getAttemptId() {
+ return _attemptId;
+ }
+
+ public long getStartTime() {
+ return _startTime;
+ }
+
+ public long getFinishTime() {
+ return _finishTime;
+ }
+
+ public void setTotalTimeMs(long totalTimeMs, boolean isSampled) {
+ this._totalTimeMs = totalTimeMs;
+ this._isSampled = isSampled;
+ }
+}
diff --git a/app/com/linkedin/drelephant/tez/fetchers/TezFetcher.java b/app/com/linkedin/drelephant/tez/fetchers/TezFetcher.java
new file mode 100644
index 000000000..294fe20e0
--- /dev/null
+++ b/app/com/linkedin/drelephant/tez/fetchers/TezFetcher.java
@@ -0,0 +1,391 @@
+/*
+ * 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.drelephant.tez.fetchers;
+
+import com.linkedin.drelephant.analysis.AnalyticJob;
+import com.linkedin.drelephant.analysis.ElephantFetcher;
+import com.linkedin.drelephant.configurations.fetcher.FetcherConfigurationData;
+import com.linkedin.drelephant.tez.data.TezApplicationData;
+import com.linkedin.drelephant.tez.data.TezCounterData;
+import com.linkedin.drelephant.tez.data.TezTaskData;
+import com.linkedin.drelephant.util.ThreadContextMR2;
+import org.apache.log4j.Logger;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.security.authentication.client.AuthenticationException;
+import org.codehaus.jackson.JsonNode;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.*;
+
+/**
+ * Task level data mining for Tez Tasks from timeline server API
+ */
+
+public class TezFetcher implements ElephantFetcher {
+
+ private static final Logger logger = Logger.getLogger(TezFetcher.class);
+
+ private static final String TIMELINE_SERVER_URL = "yarn.timeline-service.webapp.address";
+
+ private URLFactory _urlFactory;
+ private JSONFactory _jsonFactory;
+ private String _timelineWebAddr;
+
+ private FetcherConfigurationData _fetcherConfigurationData;
+
+ public TezFetcher(FetcherConfigurationData fetcherConfData) throws IOException {
+ this._fetcherConfigurationData = fetcherConfData;
+ final String applicationHistoryAddr = new Configuration().get(TIMELINE_SERVER_URL);
+
+ //Connection validity checked using method verifyURL(_timelineWebAddr) inside URLFactory constructor;
+ _urlFactory = new URLFactory(applicationHistoryAddr);
+ logger.info("Connection success.");
+
+ _jsonFactory = new JSONFactory();
+ _timelineWebAddr = "http://" + _timelineWebAddr + "/ws/v1/timeline/";
+
+ }
+
+ public TezApplicationData fetchData(AnalyticJob analyticJob) throws IOException, AuthenticationException {
+
+ int maxSize = 0;
+ String appId = analyticJob.getAppId();
+ TezApplicationData jobData = new TezApplicationData();
+ jobData.setAppId(appId);
+ Properties jobConf = _jsonFactory.getProperties(_urlFactory.getApplicationURL(appId));
+ jobData.setConf(jobConf);
+ URL dagIdsUrl = _urlFactory.getDagURLByTezApplicationId(appId);
+
+ List dagIdsByApplicationId = _jsonFactory.getDagIdsByApplicationId(dagIdsUrl);
+
+ List mapperListAggregate = new ArrayList();
+ List reducerListAggregate = new ArrayList();
+
+ //Iterate over dagIds and choose the dagId with the highest no. of tasks/highest impact as settings changes can be made only at DAG level.
+ for(String dagId : dagIdsByApplicationId){
+ try {
+ //set job task independent properties
+
+ URL dagUrl = _urlFactory.getDagURL(dagId);
+ String state = _jsonFactory.getState(dagUrl);
+
+ jobData.setStartTime(_jsonFactory.getDagStartTime(dagUrl));
+ jobData.setFinishTime(_jsonFactory.getDagEndTime(dagUrl));
+
+ if (state.equals("SUCCEEDED")) {
+ jobData.setSucceeded(true);
+
+ List mapperList = new ArrayList();
+ List reducerList = new ArrayList();
+
+ // Fetch task data
+ URL vertexListUrl = _urlFactory.getVertexListURL(dagId);
+ _jsonFactory.getTaskDataAll(vertexListUrl, dagId, mapperList, reducerList);
+
+ if(mapperList.size() + reducerList.size() > maxSize){
+ mapperListAggregate = mapperList;
+ reducerListAggregate = reducerList;
+ maxSize = mapperList.size() + reducerList.size();
+ }
+ }
+ if (state.equals("FAILED")) {
+ jobData.setSucceeded(false);
+ }
+ }
+ finally {
+ ThreadContextMR2.updateAuthToken();
+ }
+ }
+
+ TezTaskData[] mapperData = mapperListAggregate.toArray(new TezTaskData[mapperListAggregate.size()]);
+ TezTaskData[] reducerData = reducerListAggregate.toArray(new TezTaskData[reducerListAggregate.size()]);
+
+ TezCounterData dagCounter = _jsonFactory.getDagCounter(_urlFactory.getDagURL(_jsonFactory.getDagIdsByApplicationId(dagIdsUrl).get(0)));
+
+ jobData.setCounters(dagCounter).setMapTaskData(mapperData).setReduceTaskData(reducerData);
+
+ return jobData;
+ }
+
+ private URL getTaskListByVertexURL(String dagId, String vertexId) throws MalformedURLException {
+ return _urlFactory.getTaskListByVertexURL(dagId, vertexId);
+ }
+
+ private URL getTaskURL(String taskId) throws MalformedURLException {
+ return _urlFactory.getTasksURL(taskId);
+ }
+
+ private URL getTaskAttemptURL(String dagId, String taskId, String attemptId) throws MalformedURLException {
+ return _urlFactory.getTaskAttemptURL(dagId, taskId, attemptId);
+ }
+
+ private class URLFactory {
+
+ private String _timelineWebAddr;
+
+ private URLFactory(String hserverAddr) throws IOException {
+ _timelineWebAddr = "http://" + hserverAddr + "/ws/v1/timeline";
+ verifyURL(_timelineWebAddr);
+ }
+
+ private void verifyURL(String url) throws IOException {
+ final URLConnection connection = new URL(url).openConnection();
+ // Check service availability
+ connection.connect();
+ return;
+ }
+
+ private URL getDagURLByTezApplicationId(String applicationId) throws MalformedURLException {
+ return new URL(_timelineWebAddr + "/TEZ_DAG_ID?primaryFilter=applicationId:" + applicationId);
+ }
+
+ private URL getApplicationURL(String applicationId) throws MalformedURLException {
+ return new URL(_timelineWebAddr + "/TEZ_APPLICATION/tez_" + applicationId);
+ }
+
+ private URL getDagURL(String dagId) throws MalformedURLException {
+ return new URL(_timelineWebAddr + "/TEZ_DAG_ID/" + dagId);
+ }
+
+ private URL getVertexListURL(String dagId) throws MalformedURLException {
+ return new URL(_timelineWebAddr + "/TEZ_VERTEX_ID?primaryFilter=TEZ_DAG_ID:" + dagId);
+ }
+
+ private URL getTaskListByVertexURL(String dagId, String vertexId) throws MalformedURLException {
+ return new URL(_timelineWebAddr + "/TEZ_TASK_ID?primaryFilter=TEZ_DAG_ID:" + dagId +
+ "&secondaryFilter=TEZ_VERTEX_ID:" + vertexId + "&limit=500000");
+ }
+
+ private URL getTasksURL(String taskId) throws MalformedURLException {
+ return new URL(_timelineWebAddr + "/TEZ_TASK_ID/" + taskId);
+ }
+
+ private URL getTaskAllAttemptsURL(String dagId, String taskId) throws MalformedURLException {
+ return new URL(_timelineWebAddr + "/TEZ_TASK_ATTEMPT_ID?primaryFilter=TEZ_DAG_ID:" + dagId +
+ "&secondaryFilter=TEZ_TASK_ID:" + taskId);
+ }
+
+ private URL getTaskAttemptURL(String dagId, String taskId, String attemptId) throws MalformedURLException {
+ return new URL(_timelineWebAddr + "/TEZ_TASK_ATTEMPT_ID/" + attemptId);
+ }
+
+ }
+
+ /**
+ * JSONFactory class provides functionality to parse mined job data from timeline server.
+ */
+
+ private class JSONFactory {
+
+ private String getState(URL url) throws IOException, AuthenticationException {
+ JsonNode rootNode = ThreadContextMR2.readJsonNode(url);
+ return rootNode.path("otherinfo").path("status").getTextValue();
+ }
+
+ private Properties getProperties(URL url) throws IOException, AuthenticationException {
+ Properties jobConf = new Properties();
+ JsonNode rootNode = ThreadContextMR2.readJsonNode(url);
+ JsonNode configs = rootNode.path("otherinfo").path("config");
+ Iterator keys = configs.getFieldNames();
+ String key = "";
+ String value = "";
+ while (keys.hasNext()) {
+ key = keys.next();
+ value = configs.get(key).getTextValue();
+ jobConf.put(key, value);
+ }
+ return jobConf;
+ }
+
+ private List getDagIdsByApplicationId(URL dagIdsUrl) throws IOException, AuthenticationException {
+ List dagIds = new ArrayList();
+ JsonNode nodes = ThreadContextMR2.readJsonNode(dagIdsUrl).get("entities");
+
+ for (JsonNode node : nodes) {
+ String dagId = node.get("entity").getTextValue();
+ dagIds.add(dagId);
+ }
+
+ return dagIds;
+ }
+
+ private TezCounterData getDagCounter(URL url) throws IOException, AuthenticationException {
+ TezCounterData holder = new TezCounterData();
+ JsonNode rootNode = ThreadContextMR2.readJsonNode(url);
+ JsonNode groups = rootNode.path("otherinfo").path("counters").path("counterGroups");
+
+ for (JsonNode group : groups) {
+ for (JsonNode counter : group.path("counters")) {
+ String name = counter.get("counterName").getTextValue();
+ String groupName = group.get("counterGroupName").getTextValue();
+ Long value = counter.get("counterValue").getLongValue();
+ holder.set(groupName, name, value);
+ }
+ }
+
+ return holder;
+ }
+
+ private long getDagStartTime(URL url) throws IOException, AuthenticationException {
+ JsonNode rootNode = ThreadContextMR2.readJsonNode(url);
+ long startTime = rootNode.path("otherinfo").get("startTime").getLongValue();
+ return startTime;
+ }
+
+ private long getDagEndTime(URL url) throws IOException, AuthenticationException {
+ JsonNode rootNode = ThreadContextMR2.readJsonNode(url);
+ long endTime = rootNode.path("otherinfo").get("endTime").getLongValue();
+ return endTime;
+ }
+
+ private void getTaskDataAll(URL vertexListUrl, String dagId, List mapperList,
+ List reducerList) throws IOException, AuthenticationException {
+
+ JsonNode rootVertexNode = ThreadContextMR2.readJsonNode(vertexListUrl);
+ JsonNode vertices = rootVertexNode.path("entities");
+ boolean isMapVertex = false;
+
+ for (JsonNode vertex : vertices) {
+ String vertexId = vertex.get("entity").getTextValue();
+ String vertexClass = vertex.path("otherinfo").path("processorClassName").getTextValue();
+
+ if (vertexClass.equals("org.apache.hadoop.hive.ql.exec.tez.MapTezProcessor"))
+ isMapVertex = true;
+ else if (vertexClass.equals("org.apache.hadoop.hive.ql.exec.tez.ReduceTezProcessor"))
+ isMapVertex = false;
+
+ URL tasksByVertexURL = getTaskListByVertexURL(dagId, vertexId);
+ if(isMapVertex)
+ getTaskDataByVertexId(tasksByVertexURL, dagId, vertexId, mapperList, true);
+ else
+ getTaskDataByVertexId(tasksByVertexURL, dagId, vertexId, reducerList, false);
+ }
+ }
+
+ private void getTaskDataByVertexId(URL url, String dagId, String vertexId, List taskList,
+ boolean isMapVertex) throws IOException, AuthenticationException {
+
+ JsonNode rootNode = ThreadContextMR2.readJsonNode(url);
+ JsonNode tasks = rootNode.path("entities");
+ for (JsonNode task : tasks) {
+ String state = task.path("otherinfo").path("status").getTextValue();
+ String taskId = task.get("entity").getValueAsText();
+ String attemptId = task.path("otherinfo").path("successfulAttemptId").getTextValue();
+ if (state.equals("SUCCEEDED")) {
+ attemptId = task.path("otherinfo").path("successfulAttemptId").getTextValue();
+ }
+ else{
+ JsonNode firstAttempt = getTaskFirstFailedAttempt(_urlFactory.getTaskAllAttemptsURL(dagId,taskId));
+ if(firstAttempt != null){
+ attemptId = firstAttempt.get("entity").getTextValue();
+ }
+ }
+
+ taskList.add(new TezTaskData(taskId, attemptId));
+ }
+
+ getTaskData(dagId, taskList, isMapVertex);
+
+ }
+
+ private JsonNode getTaskFirstFailedAttempt(URL taskAllAttemptsUrl) throws IOException, AuthenticationException {
+ JsonNode rootNode = ThreadContextMR2.readJsonNode(taskAllAttemptsUrl);
+ long firstAttemptFinishTime = Long.MAX_VALUE;
+ JsonNode firstAttempt = null;
+ JsonNode taskAttempts = rootNode.path("entities");
+ for (JsonNode taskAttempt : taskAttempts) {
+ String state = taskAttempt.path("otherinfo").path("counters").path("status").getTextValue();
+ if (state.equals("SUCCEEDED")) {
+ continue;
+ }
+ long finishTime = taskAttempt.path("otherinfo").path("counters").path("endTime").getLongValue();
+ if( finishTime < firstAttemptFinishTime) {
+ firstAttempt = taskAttempt;
+ firstAttemptFinishTime = finishTime;
+ }
+ }
+ return firstAttempt;
+ }
+
+
+
+ private void getTaskData(String dagId, List taskList, boolean isMapTask)
+ throws IOException, AuthenticationException {
+
+ for(int i=0; i {
+ private static final Logger logger = Logger.getLogger(GenericDataSkewHeuristic.class);
+
+ // Severity Parameters
+ private static final String NUM_TASKS_SEVERITY = "num_tasks_severity";
+ private static final String DEVIATION_SEVERITY = "deviation_severity";
+ private static final String FILES_SEVERITY = "files_severity";
+
+ // Default value of parameters
+ private double[] numTasksLimits = {10, 50, 100, 200}; // Number of map or reduce tasks
+ private double[] deviationLimits = {2, 4, 8, 16}; // Deviation in i/p bytes btw 2 groups
+ private double[] filesLimits = {1d / 8, 1d / 4, 1d / 2, 1d}; // Fraction of HDFS Block Size
+
+ private HeuristicConfigurationData _heuristicConfData;
+ private List _counterNames;
+
+ private void loadParameters() {
+ Map paramMap = _heuristicConfData.getParamMap();
+ String heuristicName = _heuristicConfData.getHeuristicName();
+
+ double[] confNumTasksThreshold = Utils.getParam(paramMap.get(NUM_TASKS_SEVERITY), numTasksLimits.length);
+ if (confNumTasksThreshold != null) {
+ numTasksLimits = confNumTasksThreshold;
+ }
+ logger.info(heuristicName + " will use " + NUM_TASKS_SEVERITY + " with the following threshold settings: "
+ + Arrays.toString(numTasksLimits));
+
+ double[] confDeviationThreshold = Utils.getParam(paramMap.get(DEVIATION_SEVERITY), deviationLimits.length);
+ if (confDeviationThreshold != null) {
+ deviationLimits = confDeviationThreshold;
+ }
+ logger.info(heuristicName + " will use " + DEVIATION_SEVERITY + " with the following threshold settings: "
+ + Arrays.toString(deviationLimits));
+
+ double[] confFilesThreshold = Utils.getParam(paramMap.get(FILES_SEVERITY), filesLimits.length);
+ if (confFilesThreshold != null) {
+ filesLimits = confFilesThreshold;
+ }
+ logger.info(
+ heuristicName + " will use " + FILES_SEVERITY + " with the following threshold settings: " + Arrays.toString(
+ filesLimits));
+ for (int i = 0; i < filesLimits.length; i++) {
+ filesLimits[i] = filesLimits[i] * HDFSContext.HDFS_BLOCK_SIZE;
+ }
+ }
+
+ public GenericDataSkewHeuristic(List counterNames, HeuristicConfigurationData heuristicConfData) {
+ this._counterNames = counterNames;
+ this._heuristicConfData = heuristicConfData;
+ loadParameters();
+ }
+
+ protected abstract TezTaskData[] getTasks(TezApplicationData data);
+
+ public HeuristicConfigurationData getHeuristicConfData() {
+ return _heuristicConfData;
+ }
+
+ public HeuristicResult apply(TezApplicationData data) {
+
+ if (!data.getSucceeded()) {
+ return null;
+ }
+
+ TezTaskData[] tasks = getTasks(data);
+
+ //Gathering data for checking time skew
+ List timeTaken = new ArrayList();
+
+ for(int i = 0; i < tasks.length; i++) {
+ if (tasks[i].isSampled()) {
+ timeTaken.add(tasks[i].getTotalRunTimeMs());
+ }
+ }
+
+ long[][] groupsTime = Statistics.findTwoGroups(Longs.toArray(timeTaken));
+
+ long timeAvg1 = Statistics.average(groupsTime[0]);
+ long timeAvg2 = Statistics.average(groupsTime[1]);
+
+ //seconds are used for calculating deviation as they provide a better idea than millisecond.
+ long timeAvgSec1 = TimeUnit.MILLISECONDS.toSeconds(timeAvg1);
+ long timeAvgSec2 = TimeUnit.MILLISECONDS.toSeconds(timeAvg2);
+
+ long minTime = Math.min(timeAvgSec1, timeAvgSec2);
+ long diffTime = Math.abs(timeAvgSec1 - timeAvgSec2);
+
+ //using the same deviation limits for time skew as for data skew. It can be changed in the fututre.
+ Severity severityTime = getDeviationSeverity(minTime, diffTime);
+
+ //This reduces severity if number of tasks is insignificant
+ severityTime = Severity.min(severityTime,
+ Severity.getSeverityAscending(groupsTime[0].length, numTasksLimits[0], numTasksLimits[1], numTasksLimits[2],
+ numTasksLimits[3]));
+
+ //Gather data
+ List inputSizes = new ArrayList();
+
+ for (int i = 0; i < tasks.length; i++) {
+ if (tasks[i].isSampled()) {
+
+ long inputByte = 0;
+ for (TezCounterData.CounterName counterName : _counterNames) {
+ inputByte += tasks[i].getCounters().get(counterName);
+ }
+
+ inputSizes.add(inputByte);
+ }
+ }
+
+ long[][] groups = Statistics.findTwoGroups(Longs.toArray(inputSizes));
+
+ long avg1 = Statistics.average(groups[0]);
+ long avg2 = Statistics.average(groups[1]);
+
+ long min = Math.min(avg1, avg2);
+ long diff = Math.abs(avg2 - avg1);
+
+ Severity severityData = getDeviationSeverity(min, diff);
+
+ //This reduces severity if the largest file sizes are insignificant
+ severityData = Severity.min(severityData, getFilesSeverity(avg2));
+
+ //This reduces severity if number of tasks is insignificant
+ severityData = Severity.min(severityData,
+ Severity.getSeverityAscending(groups[0].length, numTasksLimits[0], numTasksLimits[1], numTasksLimits[2],
+ numTasksLimits[3]));
+
+ Severity severity = Severity.max(severityData, severityTime);
+
+ HeuristicResult result =
+ new HeuristicResult(_heuristicConfData.getClassName(), _heuristicConfData.getHeuristicName(), severity,
+ Utils.getHeuristicScore(severityData, tasks.length));
+
+ result.addResultDetail("Data skew (Number of tasks)", Integer.toString(tasks.length));
+ result.addResultDetail("Data skew (Group A)",
+ groups[0].length + " tasks @ " + FileUtils.byteCountToDisplaySize(avg1) + " avg");
+ result.addResultDetail("Data skew (Group B)",
+ groups[1].length + " tasks @ " + FileUtils.byteCountToDisplaySize(avg2) + " avg");
+
+ result.addResultDetail("Time skew (Number of tasks)", Integer.toString(tasks.length));
+ result.addResultDetail("Time skew (Group A)",
+ groupsTime[0].length + " tasks @ " + convertTimeMs(timeAvg1) + " avg");
+ result.addResultDetail("Time skew (Group B)",
+ groupsTime[1].length + " tasks @ " + convertTimeMs(timeAvg2) + " avg");
+
+ return result;
+ }
+
+ private String convertTimeMs(long timeMs) {
+ if (timeMs < 1000) {
+ return Long.toString(timeMs) + " msec";
+ }
+ return DurationFormatUtils.formatDuration(timeMs, "HH:mm:ss") + " HH:MM:SS";
+ }
+
+ private Severity getDeviationSeverity(long averageMin, long averageDiff) {
+ if (averageMin <= 0) {
+ averageMin = 1;
+ }
+ long value = averageDiff / averageMin;
+ return Severity.getSeverityAscending(value, deviationLimits[0], deviationLimits[1], deviationLimits[2],
+ deviationLimits[3]);
+ }
+
+ private Severity getFilesSeverity(long value) {
+ return Severity.getSeverityAscending(value, filesLimits[0], filesLimits[1], filesLimits[2], filesLimits[3]);
+ }
+}
\ No newline at end of file
diff --git a/app/com/linkedin/drelephant/tez/heuristics/GenericGCHeuristic.java b/app/com/linkedin/drelephant/tez/heuristics/GenericGCHeuristic.java
new file mode 100644
index 000000000..d5726ef71
--- /dev/null
+++ b/app/com/linkedin/drelephant/tez/heuristics/GenericGCHeuristic.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2017 Electronic Arts Inc.
+ *
+ * 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.drelephant.tez.heuristics;
+
+import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
+import com.linkedin.drelephant.tez.data.TezApplicationData;
+import com.linkedin.drelephant.tez.data.TezCounterData;
+import com.linkedin.drelephant.tez.data.TezTaskData;
+import com.linkedin.drelephant.util.Utils;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import com.linkedin.drelephant.analysis.Heuristic;
+import com.linkedin.drelephant.analysis.HeuristicResult;
+import com.linkedin.drelephant.analysis.Severity;
+import com.linkedin.drelephant.math.Statistics;
+import java.util.Map;
+import org.apache.log4j.Logger;
+
+
+/**
+ * Analyses garbage collection efficiency
+ */
+public abstract class GenericGCHeuristic implements Heuristic {
+ private static final Logger logger = Logger.getLogger(GenericGCHeuristic.class);
+
+ // Severity Parameters
+ private static final String GC_RATIO_SEVERITY = "gc_ratio_severity";
+ private static final String RUNTIME_SEVERITY = "runtime_severity_in_min";
+
+ // Default value of parameters
+ private double[] gcRatioLimits = {0.01d, 0.02d, 0.03d, 0.04d}; // Garbage Collection Time / CPU Time
+ private double[] runtimeLimits = {5, 10, 12, 15}; // Task Runtime in milli sec
+
+ private HeuristicConfigurationData _heuristicConfData;
+
+ private void loadParameters() {
+ Map paramMap = _heuristicConfData.getParamMap();
+ String heuristicName = _heuristicConfData.getHeuristicName();
+
+ double[] confGcRatioThreshold = Utils.getParam(paramMap.get(GC_RATIO_SEVERITY), gcRatioLimits.length);
+ if (confGcRatioThreshold != null) {
+ gcRatioLimits = confGcRatioThreshold;
+ }
+ logger.info(heuristicName + " will use " + GC_RATIO_SEVERITY + " with the following threshold settings: "
+ + Arrays.toString(gcRatioLimits));
+
+ double[] confRuntimeThreshold = Utils.getParam(paramMap.get(RUNTIME_SEVERITY), runtimeLimits.length);
+ if (confRuntimeThreshold != null) {
+ runtimeLimits = confRuntimeThreshold;
+ }
+ logger.info(heuristicName + " will use " + RUNTIME_SEVERITY + " with the following threshold settings: "
+ + Arrays.toString(runtimeLimits));
+ for (int i = 0; i < runtimeLimits.length; i++) {
+ runtimeLimits[i] = runtimeLimits[i] * Statistics.MINUTE_IN_MS;
+ }
+ }
+
+ public GenericGCHeuristic(HeuristicConfigurationData heuristicConfData) {
+ this._heuristicConfData = heuristicConfData;
+ loadParameters();
+ }
+
+ protected abstract TezTaskData[] getTasks(TezApplicationData data);
+
+ public HeuristicConfigurationData getHeuristicConfData() {
+ return _heuristicConfData;
+ }
+
+ public HeuristicResult apply(TezApplicationData data) {
+
+ if(!data.getSucceeded()) {
+ return null;
+ }
+
+ TezTaskData[] tasks = getTasks(data) ;
+ List gcMs = new ArrayList();
+ List cpuMs = new ArrayList();
+ List runtimesMs = new ArrayList();
+
+ for (TezTaskData task : tasks) {
+ if (task.isSampled()) {
+ runtimesMs.add(task.getTotalRunTimeMs());
+ gcMs.add(task.getCounters().get(TezCounterData.CounterName.GC_TIME_MILLIS));
+ cpuMs.add(task.getCounters().get(TezCounterData.CounterName.CPU_MILLISECONDS));
+ }
+ }
+
+ long avgRuntimeMs = Statistics.average(runtimesMs);
+ long avgCpuMs = Statistics.average(cpuMs);
+ long avgGcMs = Statistics.average(gcMs);
+ double ratio = avgCpuMs != 0 ? avgGcMs*(1.0)/avgCpuMs: 0;
+
+ Severity severity;
+ if (tasks.length == 0) {
+ severity = Severity.NONE;
+ } else {
+ severity = getGcRatioSeverity(avgRuntimeMs, avgCpuMs, avgGcMs);
+ }
+
+ HeuristicResult result = new HeuristicResult(_heuristicConfData.getClassName(),
+ _heuristicConfData.getHeuristicName(), severity, Utils.getHeuristicScore(severity, tasks.length));
+
+ result.addResultDetail("Number of tasks", Integer.toString(tasks.length));
+ result.addResultDetail("Avg task runtime (ms)", Long.toString(avgRuntimeMs));
+ result.addResultDetail("Avg task CPU time (ms)", Long.toString(avgCpuMs));
+ result.addResultDetail("Avg task GC time (ms)", Long.toString(avgGcMs));
+ result.addResultDetail("Task GC/CPU ratio", Double.toString(ratio));
+ return result;
+ }
+
+ private Severity getGcRatioSeverity(long runtimeMs, long cpuMs, long gcMs) {
+ double gcRatio = ((double)gcMs)/cpuMs;
+ Severity ratioSeverity = Severity.getSeverityAscending(
+ gcRatio, gcRatioLimits[0], gcRatioLimits[1], gcRatioLimits[2], gcRatioLimits[3]);
+
+ // Severity is reduced if task runtime is insignificant
+ Severity runtimeSeverity = getRuntimeSeverity(runtimeMs);
+
+ return Severity.min(ratioSeverity, runtimeSeverity);
+ }
+
+ private Severity getRuntimeSeverity(long runtimeMs) {
+ return Severity.getSeverityAscending(
+ runtimeMs, runtimeLimits[0], runtimeLimits[1], runtimeLimits[2], runtimeLimits[3]);
+ }
+
+}
\ No newline at end of file
diff --git a/app/com/linkedin/drelephant/tez/heuristics/GenericMemoryHeuristic.java b/app/com/linkedin/drelephant/tez/heuristics/GenericMemoryHeuristic.java
new file mode 100644
index 000000000..3a575092b
--- /dev/null
+++ b/app/com/linkedin/drelephant/tez/heuristics/GenericMemoryHeuristic.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2017 Electronic Arts Inc.
+ *
+ * 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.drelephant.tez.heuristics;
+
+import com.google.common.base.Strings;
+import com.linkedin.drelephant.analysis.Heuristic;
+import com.linkedin.drelephant.analysis.HeuristicResult;
+import com.linkedin.drelephant.analysis.Severity;
+import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
+import com.linkedin.drelephant.math.Statistics;
+import com.linkedin.drelephant.tez.data.TezApplicationData;
+import com.linkedin.drelephant.tez.data.TezCounterData;
+import com.linkedin.drelephant.tez.data.TezTaskData;
+import com.linkedin.drelephant.util.Utils;
+import org.apache.log4j.Logger;
+
+import org.apache.commons.io.FileUtils;
+
+import java.util.*;
+
+/**
+ * Analyzes mapper memory allocation and requirements
+ */
+public abstract class GenericMemoryHeuristic implements Heuristic {
+
+ private static final Logger logger = Logger.getLogger(GenericMemoryHeuristic.class);
+
+ //Severity Parameters
+ private static final String MEM_RATIO_SEVERITY = "memory_ratio_severity";
+ private static final String DEFAULT_MAPPER_CONTAINER_SIZE = "2048";
+ private static final String CONTAINER_MEM_DEFAULT_MB = "container_memory_default_mb";
+ private String _containerMemConf;
+
+ //Default Value of parameters
+
+ private double [] memoryRatioLimits = {0.6d, 0.5d, 0.4d, 0.3d}; //Ratio of successful tasks
+
+ private HeuristicConfigurationData _heuristicConfData;
+
+ private String getContainerMemDefaultMBytes() {
+ Map paramMap = _heuristicConfData.getParamMap();
+ if (paramMap.containsKey(CONTAINER_MEM_DEFAULT_MB)) {
+ String strValue = paramMap.get(CONTAINER_MEM_DEFAULT_MB);
+ try {
+ return strValue;
+ }
+ catch (NumberFormatException e) {
+ logger.warn(CONTAINER_MEM_DEFAULT_MB + ": expected number [" + strValue + "]");
+ }
+ }
+ return DEFAULT_MAPPER_CONTAINER_SIZE;
+ }
+
+ private void loadParameters() {
+ Map paramMap = _heuristicConfData.getParamMap();
+ String heuristicName = _heuristicConfData.getHeuristicName();
+
+ double[] confSuccessRatioLimits = Utils.getParam(paramMap.get(MEM_RATIO_SEVERITY), memoryRatioLimits.length);
+ if (confSuccessRatioLimits != null) {
+ memoryRatioLimits = confSuccessRatioLimits;
+ }
+ logger.info(heuristicName + " will use " + MEM_RATIO_SEVERITY + " with the following threshold settings: "
+ + Arrays.toString(memoryRatioLimits));
+
+
+ }
+
+ public GenericMemoryHeuristic(String containerMemConf, HeuristicConfigurationData heuristicConfData) {
+ this._containerMemConf = containerMemConf;
+ this._heuristicConfData = heuristicConfData;
+ loadParameters();
+ }
+
+ public HeuristicConfigurationData getHeuristicConfData() {
+ return _heuristicConfData;
+ }
+
+ protected abstract TezTaskData[] getTasks(TezApplicationData data);
+
+ public HeuristicResult apply(TezApplicationData data) {
+ if(!data.getSucceeded()) {
+ return null;
+ }
+ TezTaskData[] tasks = getTasks(data);
+
+
+ List totalPhysicalMemory = new LinkedList();
+ List totalVirtualMemory = new LinkedList();
+ List runTime = new LinkedList();
+
+ for (TezTaskData task : tasks) {
+
+ if (task.isSampled()) {
+ totalPhysicalMemory.add(task.getCounters().get(TezCounterData.CounterName.PHYSICAL_MEMORY_BYTES));
+ totalVirtualMemory.add(task.getCounters().get(TezCounterData.CounterName.VIRTUAL_MEMORY_BYTES));
+ runTime.add(task.getTotalRunTimeMs());
+ }
+
+
+ }
+
+ long averagePMem = Statistics.average(totalPhysicalMemory);
+ long averageVMem = Statistics.average(totalVirtualMemory);
+ long maxPMem;
+ long minPMem;
+ try{
+ maxPMem = Collections.max(totalPhysicalMemory);
+ minPMem = Collections.min(totalPhysicalMemory);
+
+ }
+ catch(Exception exception){
+ maxPMem = 0;
+ minPMem = 0;
+ }
+ long averageRunTime = Statistics.average(runTime);
+
+ String containerSizeStr;
+
+ if(!Strings.isNullOrEmpty(data.getConf().getProperty(_containerMemConf))){
+ containerSizeStr = data.getConf().getProperty(_containerMemConf);
+ }
+ else {
+ containerSizeStr = getContainerMemDefaultMBytes();
+ }
+
+ long containerSize = Long.valueOf(containerSizeStr) * FileUtils.ONE_MB;
+
+ double averageMemMb = (double)((averagePMem) /FileUtils.ONE_MB) ;
+
+ double ratio = averageMemMb / ((double)(containerSize / FileUtils.ONE_MB));
+
+ Severity severity ;
+
+ if(tasks.length == 0){
+ severity = Severity.NONE;
+ }
+ else{
+ severity = getMemoryRatioSeverity(ratio);
+ }
+
+ HeuristicResult result = new HeuristicResult(_heuristicConfData.getClassName(),
+ _heuristicConfData.getHeuristicName(), severity, Utils.getHeuristicScore(severity, tasks.length));
+
+ result.addResultDetail("Number of tasks", Integer.toString(tasks.length));
+ result.addResultDetail("Maximum Physical Memory (MB)",
+ tasks.length == 0 ? "0" : Long.toString(maxPMem/FileUtils.ONE_MB));
+ result.addResultDetail("Minimum Physical memory (MB)",
+ tasks.length == 0 ? "0" : Long.toString(minPMem/FileUtils.ONE_MB));
+ result.addResultDetail("Average Physical Memory (MB)",
+ tasks.length == 0 ? "0" : Long.toString(averagePMem/FileUtils.ONE_MB));
+ result.addResultDetail("Average Virtual Memory (MB)",
+ tasks.length == 0 ? "0" : Long.toString(averageVMem/FileUtils.ONE_MB));
+ result.addResultDetail("Average Task RunTime",
+ tasks.length == 0 ? "0" : Statistics.readableTimespan(averageRunTime));
+ result.addResultDetail("Requested Container Memory (MB)",
+ (tasks.length == 0 || containerSize == 0 || containerSize == -1) ? "0" : String.valueOf(containerSize / FileUtils.ONE_MB));
+
+
+ return result;
+
+ }
+
+ private Severity getMemoryRatioSeverity(double ratio) {
+ return Severity.getSeverityDescending(
+ ratio, memoryRatioLimits[0], memoryRatioLimits[1], memoryRatioLimits[2], memoryRatioLimits[3]);
+ }
+
+
+
+
+}
\ No newline at end of file
diff --git a/app/com/linkedin/drelephant/tez/heuristics/MapperDataSkewHeuristic.java b/app/com/linkedin/drelephant/tez/heuristics/MapperDataSkewHeuristic.java
new file mode 100644
index 000000000..b17ebe3b2
--- /dev/null
+++ b/app/com/linkedin/drelephant/tez/heuristics/MapperDataSkewHeuristic.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2017 Electronic Arts Inc.
+ *
+ * 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.drelephant.tez.heuristics;
+
+
+import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
+import com.linkedin.drelephant.tez.data.TezApplicationData;
+import com.linkedin.drelephant.tez.data.TezCounterData;
+import com.linkedin.drelephant.tez.data.TezTaskData;
+
+import java.util.Arrays;
+import org.apache.log4j.Logger;
+
+
+/**
+ * This Heuristic analyses the skewness in the task input data
+ */
+public class MapperDataSkewHeuristic extends GenericDataSkewHeuristic {
+ private static final Logger logger = Logger.getLogger(MapperDataSkewHeuristic.class);
+
+ public MapperDataSkewHeuristic(HeuristicConfigurationData heuristicConfData) {
+ super(Arrays.asList(
+ TezCounterData.CounterName.HDFS_BYTES_READ,
+ TezCounterData.CounterName.S3A_BYTES_READ,
+ TezCounterData.CounterName.S3N_BYTES_READ
+ ), heuristicConfData);
+ }
+
+ @Override
+ protected TezTaskData[] getTasks(TezApplicationData data) {
+ return data.getMapTaskData();
+ }
+}
\ No newline at end of file
diff --git a/app/com/linkedin/drelephant/tez/heuristics/MapperGCHeuristic.java b/app/com/linkedin/drelephant/tez/heuristics/MapperGCHeuristic.java
new file mode 100644
index 000000000..3a82ae5f9
--- /dev/null
+++ b/app/com/linkedin/drelephant/tez/heuristics/MapperGCHeuristic.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2017 Electronic Arts Inc.
+ *
+ * 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.drelephant.tez.heuristics;
+
+
+import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
+import com.linkedin.drelephant.tez.data.TezApplicationData;
+import com.linkedin.drelephant.tez.data.TezCounterData;
+import com.linkedin.drelephant.tez.data.TezTaskData;
+import com.linkedin.drelephant.util.Utils;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import com.linkedin.drelephant.analysis.Heuristic;
+import com.linkedin.drelephant.analysis.HeuristicResult;
+import com.linkedin.drelephant.analysis.Severity;
+import com.linkedin.drelephant.math.Statistics;
+import java.util.Map;
+import org.apache.log4j.Logger;
+
+
+/**
+ * Analyses garbage collection efficiency
+ */
+public class MapperGCHeuristic extends GenericGCHeuristic{
+ private static final Logger logger = Logger.getLogger(MapperGCHeuristic.class);
+
+ public MapperGCHeuristic(HeuristicConfigurationData heuristicConfData) {
+ super(heuristicConfData);
+ }
+
+ @Override
+ protected TezTaskData[] getTasks(TezApplicationData data) {
+ return data.getMapTaskData();
+ }
+
+}
\ No newline at end of file
diff --git a/app/com/linkedin/drelephant/tez/heuristics/MapperMemoryHeuristic.java b/app/com/linkedin/drelephant/tez/heuristics/MapperMemoryHeuristic.java
new file mode 100644
index 000000000..96d5bb12e
--- /dev/null
+++ b/app/com/linkedin/drelephant/tez/heuristics/MapperMemoryHeuristic.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2017 Electronic Arts Inc.
+ *
+ * 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.drelephant.tez.heuristics;
+
+import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
+
+import com.linkedin.drelephant.tez.data.TezApplicationData;
+
+import com.linkedin.drelephant.tez.data.TezTaskData;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Analyzes mapper memory allocation and requirements
+ */
+public class MapperMemoryHeuristic extends GenericMemoryHeuristic {
+
+ private static final Logger logger = Logger.getLogger(MapperMemoryHeuristic.class);
+
+ public static final String MAPPER_MEMORY_CONF = "mapreduce.map.memory.mb";
+
+ public MapperMemoryHeuristic(HeuristicConfigurationData __heuristicConfData){
+ super(MAPPER_MEMORY_CONF, __heuristicConfData);
+ }
+
+ @Override
+ protected TezTaskData[] getTasks(TezApplicationData data) {
+ return data.getMapTaskData();
+ }
+
+}
\ No newline at end of file
diff --git a/app/com/linkedin/drelephant/tez/heuristics/MapperSpeedHeuristic.java b/app/com/linkedin/drelephant/tez/heuristics/MapperSpeedHeuristic.java
new file mode 100644
index 000000000..1694e32ea
--- /dev/null
+++ b/app/com/linkedin/drelephant/tez/heuristics/MapperSpeedHeuristic.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2017 Electronic Arts Inc.
+ *
+ * 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.drelephant.tez.heuristics;
+
+
+import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
+import com.linkedin.drelephant.tez.data.TezApplicationData;
+import com.linkedin.drelephant.tez.data.TezCounterData;
+import com.linkedin.drelephant.tez.data.TezTaskData;
+import com.linkedin.drelephant.util.Utils;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import com.linkedin.drelephant.analysis.HDFSContext;
+import com.linkedin.drelephant.analysis.Heuristic;
+import com.linkedin.drelephant.analysis.HeuristicResult;
+import com.linkedin.drelephant.analysis.Severity;
+import com.linkedin.drelephant.math.Statistics;
+
+import java.util.Map;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.log4j.Logger;
+
+/**
+ * Analyzes mapper task speed and efficiency
+ */
+public class MapperSpeedHeuristic implements Heuristic {
+ private static final Logger logger = Logger.getLogger(MapperSpeedHeuristic.class);
+
+ // Severity parameters.
+ private static final String DISK_SPEED_SEVERITY = "disk_speed_severity";
+ private static final String RUNTIME_SEVERITY = "runtime_severity_in_min";
+
+ // Default value of parameters
+ private double[] diskSpeedLimits = {1d/2, 1d/4, 1d/8, 1d/32}; // Fraction of HDFS block size
+ private double[] runtimeLimits = {5, 10, 15, 30}; // The Map task runtime in milli sec
+
+ private List _counterNames = Arrays.asList(
+ TezCounterData.CounterName.HDFS_BYTES_READ,
+ TezCounterData.CounterName.S3A_BYTES_READ,
+ TezCounterData.CounterName.S3N_BYTES_READ
+ );
+
+ private HeuristicConfigurationData _heuristicConfData;
+
+ private void loadParameters() {
+ Map paramMap = _heuristicConfData.getParamMap();
+ String heuristicName = _heuristicConfData.getHeuristicName();
+
+ double[] confDiskSpeedThreshold = Utils.getParam(paramMap.get(DISK_SPEED_SEVERITY), diskSpeedLimits.length);
+ if (confDiskSpeedThreshold != null) {
+ diskSpeedLimits = confDiskSpeedThreshold;
+ }
+ logger.info(heuristicName + " will use " + DISK_SPEED_SEVERITY + " with the following threshold settings: "
+ + Arrays.toString(diskSpeedLimits));
+ for (int i = 0; i < diskSpeedLimits.length; i++) {
+ diskSpeedLimits[i] = diskSpeedLimits[i] * HDFSContext.DISK_READ_SPEED;
+ }
+
+ double[] confRuntimeThreshold = Utils.getParam(paramMap.get(RUNTIME_SEVERITY), runtimeLimits.length);
+ if (confRuntimeThreshold != null) {
+ runtimeLimits = confRuntimeThreshold;
+ }
+ logger.info(heuristicName + " will use " + RUNTIME_SEVERITY + " with the following threshold settings: " + Arrays
+ .toString(runtimeLimits));
+ for (int i = 0; i < runtimeLimits.length; i++) {
+ runtimeLimits[i] = runtimeLimits[i] * Statistics.MINUTE_IN_MS;
+ }
+ }
+
+ public MapperSpeedHeuristic(HeuristicConfigurationData heuristicConfData) {
+ this._heuristicConfData = heuristicConfData;
+ loadParameters();
+ }
+
+ public HeuristicConfigurationData getHeuristicConfData() {
+ return _heuristicConfData;
+ }
+
+ public HeuristicResult apply(TezApplicationData data) {
+
+ if(!data.getSucceeded()) {
+ return null;
+ }
+
+ TezTaskData[] tasks = data.getMapTaskData();
+
+ List inputSizes = new ArrayList();
+ List speeds = new ArrayList();
+ List runtimesMs = new ArrayList();
+
+ for (TezTaskData task : tasks) {
+
+ if (task.isSampled()) {
+
+ long inputBytes = 0;
+
+ for (TezCounterData.CounterName counterName: _counterNames) {
+ inputBytes += task.getCounters().get(counterName);
+ }
+
+ long runtimeMs = task.getTotalRunTimeMs();
+ inputSizes.add(inputBytes);
+ runtimesMs.add(runtimeMs);
+ //Speed is records per second
+ speeds.add((1000 * inputBytes) / (runtimeMs));
+ }
+ }
+
+ long medianSpeed;
+ long medianSize;
+ long medianRuntimeMs;
+
+ if (tasks.length != 0) {
+ medianSpeed = Statistics.median(speeds);
+ medianSize = Statistics.median(inputSizes);
+ medianRuntimeMs = Statistics.median(runtimesMs);
+ } else {
+ medianSpeed = 0;
+ medianSize = 0;
+ medianRuntimeMs = 0;
+ }
+
+ Severity severity = getDiskSpeedSeverity(medianSpeed);
+
+ //This reduces severity if task runtime is insignificant
+ severity = Severity.min(severity, getRuntimeSeverity(medianRuntimeMs));
+
+ HeuristicResult result = new HeuristicResult(_heuristicConfData.getClassName(),
+ _heuristicConfData.getHeuristicName(), severity, Utils.getHeuristicScore(severity, tasks.length));
+
+ result.addResultDetail("Number of tasks", Integer.toString(tasks.length));
+ result.addResultDetail("Median task input ", FileUtils.byteCountToDisplaySize(medianSize));
+ result.addResultDetail("Median task runtime", Statistics.readableTimespan(medianRuntimeMs));
+ result.addResultDetail("Median task speed", FileUtils.byteCountToDisplaySize(medianSpeed) + "/s");
+
+ return result;
+ }
+
+ private Severity getDiskSpeedSeverity(long speed) {
+ return Severity.getSeverityDescending(
+ speed, diskSpeedLimits[0], diskSpeedLimits[1], diskSpeedLimits[2], diskSpeedLimits[3]);
+ }
+
+ private Severity getRuntimeSeverity(long runtimeMs) {
+ return Severity.getSeverityAscending(
+ runtimeMs, runtimeLimits[0], runtimeLimits[1], runtimeLimits[2], runtimeLimits[3]);
+ }
+}
\ No newline at end of file
diff --git a/app/com/linkedin/drelephant/tez/heuristics/MapperSpillHeuristic.java b/app/com/linkedin/drelephant/tez/heuristics/MapperSpillHeuristic.java
new file mode 100644
index 000000000..f7ea997e5
--- /dev/null
+++ b/app/com/linkedin/drelephant/tez/heuristics/MapperSpillHeuristic.java
@@ -0,0 +1,142 @@
+/*
+ * 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.drelephant.tez.heuristics;
+
+import com.linkedin.drelephant.analysis.Heuristic;
+import com.linkedin.drelephant.analysis.HeuristicResult;
+import com.linkedin.drelephant.analysis.Severity;
+import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
+import com.linkedin.drelephant.tez.data.TezCounterData;
+import com.linkedin.drelephant.tez.data.TezApplicationData;
+import com.linkedin.drelephant.tez.data.TezTaskData;
+import com.linkedin.drelephant.util.Utils;
+import org.apache.log4j.Logger;
+
+
+import java.util.Arrays;
+import java.util.Map;
+
+/**
+ * Analyzes mapper task data spill rates
+ */
+public class MapperSpillHeuristic implements Heuristic {
+ private static final Logger logger = Logger.getLogger(MapperSpillHeuristic.class);
+ private static final long THRESHOLD_SPILL_FACTOR = 10000;
+
+ // Severity parameters.
+ private static final String SPILL_SEVERITY = "spill_severity";
+ private static final String NUM_TASKS_SEVERITY = "num_tasks_severity";
+
+ // Default value of parameters
+ private double[] numTasksLimits = {50, 100, 500, 1000}; // Number of Map tasks.
+ private double[] spillLimits = {2.01d, 2.2d, 2.5d, 3.0d}; // Records spilled/total output records
+
+ private HeuristicConfigurationData _heuristicConfData;
+
+ private void loadParameters() {
+ Map paramMap = _heuristicConfData.getParamMap();
+ String heuristicName = _heuristicConfData.getHeuristicName();
+
+ double[] confNumTasksThreshold = Utils.getParam(paramMap.get(NUM_TASKS_SEVERITY), numTasksLimits.length);
+ if (confNumTasksThreshold != null) {
+ numTasksLimits = confNumTasksThreshold;
+ }
+ logger.info(heuristicName + " will use " + NUM_TASKS_SEVERITY + " with the following threshold settings: "
+ + Arrays.toString(numTasksLimits));
+
+ double[] confSpillThreshold = Utils.getParam(paramMap.get(SPILL_SEVERITY), spillLimits.length);
+ if (confSpillThreshold != null) {
+ spillLimits = confSpillThreshold;
+ }
+ logger.info(heuristicName + " will use " + SPILL_SEVERITY + " with the following threshold settings: " + Arrays
+ .toString(spillLimits));
+ for (int i = 0; i < spillLimits.length; i++) {
+ spillLimits[i] = spillLimits[i] * THRESHOLD_SPILL_FACTOR;
+ }
+ }
+
+ public MapperSpillHeuristic(HeuristicConfigurationData heuristicConfData) {
+ this._heuristicConfData = heuristicConfData;
+ loadParameters();
+ }
+
+ @Override
+ public HeuristicConfigurationData getHeuristicConfData() {
+ return _heuristicConfData;
+ }
+
+ @Override
+ public HeuristicResult apply(TezApplicationData data) {
+ if(!data.getSucceeded()) {
+ return null;
+ }
+ TezTaskData[] tasks = data.getMapTaskData();
+
+ long totalSpills = 0;
+ long totalOutputRecords = 0;
+ double ratioSpills = 0.0;
+
+ for (TezTaskData task : tasks) {
+
+ if (task.isSampled()) {
+ totalSpills += task.getCounters().get(TezCounterData.CounterName.SPILLED_RECORDS);
+ totalOutputRecords += task.getCounters().get(TezCounterData.CounterName.OUTPUT_RECORDS);
+ }
+ }
+
+ //If both totalSpills and totalOutputRecords are zero then set ratioSpills to zero.
+ if (totalSpills == 0) {
+ ratioSpills = 0;
+ } else {
+ ratioSpills = (double) totalSpills / (double) totalOutputRecords;
+ }
+
+ Severity severity = getSpillSeverity(ratioSpills);
+
+ // Severity is reduced if number of tasks is small
+ Severity taskSeverity = getNumTasksSeverity(tasks.length);
+ severity = Severity.min(severity, taskSeverity);
+
+ HeuristicResult result = new HeuristicResult(_heuristicConfData.getClassName(),
+ _heuristicConfData.getHeuristicName(), severity, Utils.getHeuristicScore(severity, tasks.length));
+
+ result.addResultDetail("Number of tasks", Integer.toString(tasks.length));
+ result.addResultDetail("Avg spilled records per task",
+ tasks.length == 0 ? "0" : Long.toString(totalSpills / tasks.length));
+ result.addResultDetail("Avg output records per task",
+ tasks.length == 0 ? "0" : Long.toString(totalOutputRecords / tasks.length));
+ result.addResultDetail("Ratio of spilled records to output records", Double.toString(ratioSpills));
+
+ return result;
+
+ }
+
+ private Severity getSpillSeverity(double ratioSpills) {
+
+ long normalizedSpillRatio = 0;
+ //Normalize the ratio to integer.
+ normalizedSpillRatio = (long) (ratioSpills * THRESHOLD_SPILL_FACTOR);
+
+ return Severity.getSeverityAscending(
+ normalizedSpillRatio, spillLimits[0], spillLimits[1], spillLimits[2], spillLimits[3]);
+ }
+
+ private Severity getNumTasksSeverity(long numTasks) {
+ return Severity.getSeverityAscending(
+ numTasks, numTasksLimits[0], numTasksLimits[1], numTasksLimits[2], numTasksLimits[3]);
+ }
+
+
+}
diff --git a/app/com/linkedin/drelephant/tez/heuristics/MapperTimeHeuristic.java b/app/com/linkedin/drelephant/tez/heuristics/MapperTimeHeuristic.java
new file mode 100644
index 000000000..838bd7a59
--- /dev/null
+++ b/app/com/linkedin/drelephant/tez/heuristics/MapperTimeHeuristic.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2017 Electronic Arts Inc.
+ *
+ * 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.drelephant.tez.heuristics;
+
+
+import com.linkedin.drelephant.analysis.Heuristic;
+import com.linkedin.drelephant.analysis.HeuristicResult;
+import com.linkedin.drelephant.analysis.Severity;
+import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
+import com.linkedin.drelephant.tez.data.TezApplicationData;
+import com.linkedin.drelephant.tez.data.TezCounterData;
+import com.linkedin.drelephant.tez.data.TezTaskData;
+import com.linkedin.drelephant.util.Utils;
+import org.apache.commons.io.FileUtils;
+import org.apache.log4j.Logger;
+
+import com.linkedin.drelephant.math.Statistics;
+
+import java.util.*;
+
+/**
+ * Analyzes mapper task runtimes
+ */
+public class MapperTimeHeuristic implements Heuristic {
+
+ private static final Logger logger = Logger.getLogger(MapperTimeHeuristic.class);
+
+ // Severity parameters.
+ private static final String SHORT_RUNTIME_SEVERITY = "short_runtime_severity_in_min";
+ private static final String LONG_RUNTIME_SEVERITY = "long_runtime_severity_in_min";
+ private static final String NUM_TASKS_SEVERITY = "num_tasks_severity";
+
+ // Default value of parameters
+ private double[] shortRuntimeLimits = {10, 4, 2, 1}; // Limits(ms) for tasks with shorter runtime
+ private double[] longRuntimeLimits = {15, 30, 60, 120}; // Limits(ms) for tasks with longer runtime
+ private double[] numTasksLimits = {50, 101, 500, 1000}; // Number of Map tasks.
+
+ private List _counterNames = Arrays.asList(
+ TezCounterData.CounterName.HDFS_BYTES_READ,
+ TezCounterData.CounterName.S3A_BYTES_READ,
+ TezCounterData.CounterName.S3N_BYTES_READ
+ );
+
+ private HeuristicConfigurationData _heuristicConfData;
+
+ private void loadParameters() {
+ Map paramMap = _heuristicConfData.getParamMap();
+ String heuristicName = _heuristicConfData.getHeuristicName();
+
+ double[] confShortThreshold = Utils.getParam(paramMap.get(SHORT_RUNTIME_SEVERITY), shortRuntimeLimits.length);
+ if (confShortThreshold != null) {
+ shortRuntimeLimits = confShortThreshold;
+ }
+ logger.info(heuristicName + " will use " + SHORT_RUNTIME_SEVERITY + " with the following threshold settings: "
+ + Arrays.toString(shortRuntimeLimits));
+ for (int i = 0; i < shortRuntimeLimits.length; i++) {
+ shortRuntimeLimits[i] = shortRuntimeLimits[i] * Statistics.MINUTE_IN_MS;
+ }
+
+ double[] confLongThreshold = Utils.getParam(paramMap.get(LONG_RUNTIME_SEVERITY), longRuntimeLimits.length);
+ if (confLongThreshold != null) {
+ longRuntimeLimits = confLongThreshold;
+ }
+ logger.info(heuristicName + " will use " + LONG_RUNTIME_SEVERITY + " with the following threshold settings: "
+ + Arrays.toString(longRuntimeLimits));
+ for (int i = 0; i < longRuntimeLimits.length; i++) {
+ longRuntimeLimits[i] = longRuntimeLimits[i] * Statistics.MINUTE_IN_MS;
+ }
+
+ double[] confNumTasksThreshold = Utils.getParam(paramMap.get(NUM_TASKS_SEVERITY), numTasksLimits.length);
+ if (confNumTasksThreshold != null) {
+ numTasksLimits = confNumTasksThreshold;
+ }
+ logger.info(heuristicName + " will use " + NUM_TASKS_SEVERITY + " with the following threshold settings: " + Arrays
+ .toString(numTasksLimits));
+
+
+ }
+
+ public MapperTimeHeuristic(HeuristicConfigurationData heuristicConfData) {
+ this._heuristicConfData = heuristicConfData;
+ loadParameters();
+ }
+
+ public HeuristicConfigurationData getHeuristicConfData() {
+ return _heuristicConfData;
+ }
+
+ public HeuristicResult apply(TezApplicationData data) {
+ if(!data.getSucceeded()) {
+ return null;
+ }
+ TezTaskData[] tasks = data.getMapTaskData();
+
+ List inputSizes = new ArrayList();
+ List runtimesMs = new ArrayList();
+ long taskMinMs = Long.MAX_VALUE;
+ long taskMaxMs = 0;
+
+ for (TezTaskData task : tasks) {
+
+ if (task.isSampled()) {
+ long inputByte = 0;
+ for (TezCounterData.CounterName counterName: _counterNames) {
+ inputByte += task.getCounters().get(counterName);
+ }
+ inputSizes.add(inputByte);
+ long taskTime = task.getTotalRunTimeMs();
+ runtimesMs.add(taskTime);
+ taskMinMs = Math.min(taskMinMs, taskTime);
+ taskMaxMs = Math.max(taskMaxMs, taskTime);
+ }
+ }
+
+ if(taskMinMs == Long.MAX_VALUE) {
+ taskMinMs = 0;
+ }
+
+ long averageSize = Statistics.average(inputSizes);
+ long averageTimeMs = Statistics.average(runtimesMs);
+
+ Severity shortTaskSeverity = shortTaskSeverity(tasks.length, averageTimeMs);
+ Severity longTaskSeverity = longTaskSeverity(tasks.length, averageTimeMs);
+ Severity severity = Severity.max(shortTaskSeverity, longTaskSeverity);
+
+ HeuristicResult result = new HeuristicResult(_heuristicConfData.getClassName(),
+ _heuristicConfData.getHeuristicName(), severity, Utils.getHeuristicScore(severity, tasks.length));
+
+ result.addResultDetail("Number of tasks", Integer.toString(tasks.length));
+ result.addResultDetail("Average task input size", FileUtils.byteCountToDisplaySize(averageSize));
+ result.addResultDetail("Average task runtime", Statistics.readableTimespan(averageTimeMs));
+ result.addResultDetail("Max task runtime", Statistics.readableTimespan(taskMaxMs));
+ result.addResultDetail("Min task runtime", Statistics.readableTimespan(taskMinMs));
+
+ return result;
+ }
+
+ private Severity shortTaskSeverity(long numTasks, long averageTimeMs) {
+ // We want to identify jobs with short task runtime
+ Severity severity = getShortRuntimeSeverity(averageTimeMs);
+ // Severity is reduced if number of tasks is small.
+ Severity numTaskSeverity = getNumTasksSeverity(numTasks);
+ return Severity.min(severity, numTaskSeverity);
+ }
+
+ private Severity longTaskSeverity(long numTasks, long averageTimeMs) {
+ // We want to identify jobs with long task runtime. Severity is NOT reduced if num of tasks is large
+ return getLongRuntimeSeverity(averageTimeMs);
+ }
+
+ private Severity getShortRuntimeSeverity(long runtimeMs) {
+ return Severity.getSeverityDescending(
+ runtimeMs, shortRuntimeLimits[0], shortRuntimeLimits[1], shortRuntimeLimits[2], shortRuntimeLimits[3]);
+ }
+
+ private Severity getLongRuntimeSeverity(long runtimeMs) {
+ return Severity.getSeverityAscending(
+ runtimeMs, longRuntimeLimits[0], longRuntimeLimits[1], longRuntimeLimits[2], longRuntimeLimits[3]);
+ }
+
+ private Severity getNumTasksSeverity(long numTasks) {
+ return Severity.getSeverityAscending(
+ numTasks, numTasksLimits[0], numTasksLimits[1], numTasksLimits[2], numTasksLimits[3]);
+ }
+
+
+
+}
diff --git a/app/com/linkedin/drelephant/tez/heuristics/ReducerDataSkewHeuristic.java b/app/com/linkedin/drelephant/tez/heuristics/ReducerDataSkewHeuristic.java
new file mode 100644
index 000000000..3a01448d4
--- /dev/null
+++ b/app/com/linkedin/drelephant/tez/heuristics/ReducerDataSkewHeuristic.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017 Electronic Arts Inc.
+ *
+ * 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.drelephant.tez.heuristics;
+
+import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
+import com.linkedin.drelephant.tez.data.TezApplicationData;
+import com.linkedin.drelephant.tez.data.TezCounterData;
+import com.linkedin.drelephant.tez.data.TezTaskData;
+import org.apache.log4j.Logger;
+
+import java.util.Arrays;
+
+
+/**
+ * This Heuristic analyses the skewness in the task input data
+ */
+public class ReducerDataSkewHeuristic extends GenericDataSkewHeuristic {
+ private static final Logger logger = Logger.getLogger(ReducerDataSkewHeuristic.class);
+
+ public ReducerDataSkewHeuristic(HeuristicConfigurationData heuristicConfData) {
+ super(Arrays.asList(TezCounterData.CounterName.SHUFFLE_BYTES), heuristicConfData);
+ }
+
+ @Override
+ protected TezTaskData[] getTasks(TezApplicationData data) {
+ return data.getReduceTaskData();
+ }
+}
\ No newline at end of file
diff --git a/app/com/linkedin/drelephant/tez/heuristics/ReducerGCHeuristic.java b/app/com/linkedin/drelephant/tez/heuristics/ReducerGCHeuristic.java
new file mode 100644
index 000000000..3ea3bdac4
--- /dev/null
+++ b/app/com/linkedin/drelephant/tez/heuristics/ReducerGCHeuristic.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2017 Electronic Arts Inc.
+ *
+ * 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.drelephant.tez.heuristics;
+
+import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
+import com.linkedin.drelephant.tez.data.TezApplicationData;
+import com.linkedin.drelephant.tez.data.TezTaskData;
+import org.apache.log4j.Logger;
+
+
+/**
+ * Analyses garbage collection efficiency
+ */
+public class ReducerGCHeuristic extends GenericGCHeuristic {
+
+ private static final Logger logger = Logger.getLogger(ReducerGCHeuristic.class);
+
+ public ReducerGCHeuristic(HeuristicConfigurationData heuristicConfData) {
+ super(heuristicConfData);
+ }
+
+ @Override
+ protected TezTaskData[] getTasks(TezApplicationData data) {
+ return data.getReduceTaskData();
+ }
+
+}
diff --git a/app/com/linkedin/drelephant/tez/heuristics/ReducerMemoryHeuristic.java b/app/com/linkedin/drelephant/tez/heuristics/ReducerMemoryHeuristic.java
new file mode 100644
index 000000000..41fc6fa1d
--- /dev/null
+++ b/app/com/linkedin/drelephant/tez/heuristics/ReducerMemoryHeuristic.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2017 Electronic Arts Inc.
+ *
+ * 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.drelephant.tez.heuristics;
+
+
+import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
+import com.linkedin.drelephant.tez.data.TezApplicationData;
+import com.linkedin.drelephant.tez.data.TezTaskData;
+import org.apache.log4j.Logger;
+
+/**
+ * Analyzes reducer memory allocation and requirements
+ */
+
+public class ReducerMemoryHeuristic extends GenericMemoryHeuristic {
+
+ private static final Logger logger = Logger.getLogger(ReducerMemoryHeuristic.class);
+
+ public static final String REDUCER_MEMORY_CONF = "mapreduce.reduce.memory.mb";
+
+ public ReducerMemoryHeuristic(HeuristicConfigurationData __heuristicConfData){
+ super(REDUCER_MEMORY_CONF, __heuristicConfData);
+ }
+
+ @Override
+ protected TezTaskData[] getTasks(TezApplicationData data) {
+ return data.getReduceTaskData();
+ }
+
+
+
+
+}
diff --git a/app/com/linkedin/drelephant/tez/heuristics/ReducerTimeHeuristic.java b/app/com/linkedin/drelephant/tez/heuristics/ReducerTimeHeuristic.java
new file mode 100644
index 000000000..5e0dbfe65
--- /dev/null
+++ b/app/com/linkedin/drelephant/tez/heuristics/ReducerTimeHeuristic.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2017 Electronic Arts Inc.
+ *
+ * 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.drelephant.tez.heuristics;
+
+
+import com.linkedin.drelephant.analysis.Heuristic;
+import com.linkedin.drelephant.analysis.HeuristicResult;
+import com.linkedin.drelephant.analysis.Severity;
+import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
+import com.linkedin.drelephant.tez.data.TezApplicationData;
+import com.linkedin.drelephant.tez.data.TezTaskData;
+import com.linkedin.drelephant.util.Utils;
+import org.apache.log4j.Logger;
+
+import com.linkedin.drelephant.math.Statistics;
+
+import java.util.*;
+
+/**
+ * Analyzes reducer task runtimes
+ */
+
+public class ReducerTimeHeuristic implements Heuristic {
+
+ private static final Logger logger = Logger.getLogger(ReducerTimeHeuristic.class);
+
+ // Severity parameters.
+ private static final String SHORT_RUNTIME_SEVERITY = "short_runtime_severity_in_min";
+ private static final String LONG_RUNTIME_SEVERITY = "long_runtime_severity_in_min";
+ private static final String NUM_TASKS_SEVERITY = "num_tasks_severity";
+
+ // Default value of parameters
+ private double[] shortRuntimeLimits = {10, 4, 2, 1}; // Limits(ms) for tasks with shorter runtime
+ private double[] longRuntimeLimits = {15, 30, 60, 120}; // Limits(ms) for tasks with longer runtime
+ private double[] numTasksLimits = {50, 101, 500, 1000}; // Number of Map tasks.
+
+ private HeuristicConfigurationData _heuristicConfData;
+
+ private void loadParameters() {
+ Map paramMap = _heuristicConfData.getParamMap();
+ String heuristicName = _heuristicConfData.getHeuristicName();
+
+ double[] confShortThreshold = Utils.getParam(paramMap.get(SHORT_RUNTIME_SEVERITY), shortRuntimeLimits.length);
+ if (confShortThreshold != null) {
+ shortRuntimeLimits = confShortThreshold;
+ }
+ logger.info(heuristicName + " will use " + SHORT_RUNTIME_SEVERITY + " with the following threshold settings: "
+ + Arrays.toString(shortRuntimeLimits));
+ for (int i = 0; i < shortRuntimeLimits.length; i++) {
+ shortRuntimeLimits[i] = shortRuntimeLimits[i] * Statistics.MINUTE_IN_MS;
+ }
+
+ double[] confLongThreshold = Utils.getParam(paramMap.get(LONG_RUNTIME_SEVERITY), longRuntimeLimits.length);
+ if (confLongThreshold != null) {
+ longRuntimeLimits = confLongThreshold;
+ }
+ logger.info(heuristicName + " will use " + LONG_RUNTIME_SEVERITY + " with the following threshold settings: "
+ + Arrays.toString(longRuntimeLimits));
+ for (int i = 0; i < longRuntimeLimits.length; i++) {
+ longRuntimeLimits[i] = longRuntimeLimits[i] * Statistics.MINUTE_IN_MS;
+ }
+
+ double[] confNumTasksThreshold = Utils.getParam(paramMap.get(NUM_TASKS_SEVERITY), numTasksLimits.length);
+ if (confNumTasksThreshold != null) {
+ numTasksLimits = confNumTasksThreshold;
+ }
+ logger.info(heuristicName + " will use " + NUM_TASKS_SEVERITY + " with the following threshold settings: " + Arrays
+ .toString(numTasksLimits));
+
+
+ }
+
+ public ReducerTimeHeuristic(HeuristicConfigurationData heuristicConfData) {
+ this._heuristicConfData = heuristicConfData;
+ loadParameters();
+ }
+
+ public HeuristicConfigurationData getHeuristicConfData() {
+ return _heuristicConfData;
+ }
+
+ public HeuristicResult apply(TezApplicationData data) {
+ if(!data.getSucceeded()) {
+ return null;
+ }
+ TezTaskData[] tasks = data.getReduceTaskData();
+
+ List runtimesMs = new ArrayList();
+ long taskMinMs = Long.MAX_VALUE;
+ long taskMaxMs = 0;
+
+ for (TezTaskData task : tasks) {
+
+ if (task.isSampled()) {
+ long taskTime = task.getTotalRunTimeMs();
+ runtimesMs.add(taskTime);
+ taskMinMs = Math.min(taskMinMs, taskTime);
+ taskMaxMs = Math.max(taskMaxMs, taskTime);
+ }
+ }
+
+ if(taskMinMs == Long.MAX_VALUE) {
+ taskMinMs = 0;
+ }
+
+
+ long averageTimeMs = Statistics.average(runtimesMs);
+
+ Severity shortTaskSeverity = shortTaskSeverity(tasks.length, averageTimeMs);
+ Severity longTaskSeverity = longTaskSeverity(tasks.length, averageTimeMs);
+ Severity severity = Severity.max(shortTaskSeverity, longTaskSeverity);
+
+ HeuristicResult result = new HeuristicResult(_heuristicConfData.getClassName(),
+ _heuristicConfData.getHeuristicName(), severity, Utils.getHeuristicScore(severity, tasks.length));
+
+ result.addResultDetail("Number of tasks", Integer.toString(tasks.length));
+ result.addResultDetail("Average task runtime", tasks.length == 0 ? "0" : (Statistics.readableTimespan(averageTimeMs).equals("") ? "0 sec" : Statistics.readableTimespan(averageTimeMs)));
+ result.addResultDetail("Max task runtime", tasks.length == 0 ? "0" : (Statistics.readableTimespan(taskMaxMs).equals("") ? "0 sec" : Statistics.readableTimespan(taskMaxMs)) );
+ result.addResultDetail("Min task runtime", tasks.length == 0 ? "0" : (Statistics.readableTimespan(taskMinMs).equals("") ? "0 sec" :Statistics.readableTimespan(taskMinMs))) ;
+
+ return result;
+ }
+
+ private Severity shortTaskSeverity(long numTasks, long averageTimeMs) {
+ // We want to identify jobs with short task runtime
+ Severity severity = getShortRuntimeSeverity(averageTimeMs);
+ // Severity is reduced if number of tasks is small.
+ Severity numTaskSeverity = getNumTasksSeverity(numTasks);
+ return Severity.min(severity, numTaskSeverity);
+ }
+
+ private Severity longTaskSeverity(long numTasks, long averageTimeMs) {
+ // We want to identify jobs with long task runtime. Severity is NOT reduced if num of tasks is large
+ return getLongRuntimeSeverity(averageTimeMs);
+ }
+
+ private Severity getShortRuntimeSeverity(long runtimeMs) {
+ return Severity.getSeverityDescending(
+ runtimeMs, shortRuntimeLimits[0], shortRuntimeLimits[1], shortRuntimeLimits[2], shortRuntimeLimits[3]);
+ }
+
+ private Severity getLongRuntimeSeverity(long runtimeMs) {
+ return Severity.getSeverityAscending(
+ runtimeMs, longRuntimeLimits[0], longRuntimeLimits[1], longRuntimeLimits[2], longRuntimeLimits[3]);
+ }
+
+ private Severity getNumTasksSeverity(long numTasks) {
+ return Severity.getSeverityAscending(
+ numTasks, numTasksLimits[0], numTasksLimits[1], numTasksLimits[2], numTasksLimits[3]);
+ }
+
+}
+
diff --git a/app/com/linkedin/drelephant/util/InfoExtractor.java b/app/com/linkedin/drelephant/util/InfoExtractor.java
index 9824b79b2..6ff0f4d1b 100644
--- a/app/com/linkedin/drelephant/util/InfoExtractor.java
+++ b/app/com/linkedin/drelephant/util/InfoExtractor.java
@@ -20,6 +20,10 @@
import com.linkedin.drelephant.clients.WorkflowClient;
import com.linkedin.drelephant.configurations.scheduler.SchedulerConfiguration;
import com.linkedin.drelephant.configurations.scheduler.SchedulerConfigurationData;
+
+import com.linkedin.drelephant.tez.data.TezApplicationData;
+import com.linkedin.drelephant.clients.WorkflowClient;
+
import com.linkedin.drelephant.mapreduce.data.MapReduceApplicationData;
import com.linkedin.drelephant.schedulers.Scheduler;
import com.linkedin.drelephant.spark.data.SparkApplicationData;
@@ -116,6 +120,9 @@ public static void loadInfo(AppResult result, HadoopApplicationData data) {
} else if ( data instanceof SparkApplicationData) {
properties = retrieveSparkProperties((SparkApplicationData) data);
}
+ else if(data instanceof TezApplicationData){
+ properties = retrieveTezProperties((TezApplicationData) data);
+ }
Scheduler scheduler = getSchedulerInstance(data.getAppId(), properties);
if (scheduler == null) {
@@ -166,6 +173,10 @@ public static Properties retrieveMapreduceProperties(MapReduceApplicationData ap
return appData.getConf();
}
+ public static Properties retrieveTezProperties(TezApplicationData appData) {
+ return appData.getConf();
+ }
+
/**
* Populates the given app result with the info from the given application data and scheduler.
*
diff --git a/app/com/linkedin/drelephant/util/ThreadContextMR2.java b/app/com/linkedin/drelephant/util/ThreadContextMR2.java
new file mode 100644
index 000000000..a448a24fe
--- /dev/null
+++ b/app/com/linkedin/drelephant/util/ThreadContextMR2.java
@@ -0,0 +1,92 @@
+package com.linkedin.drelephant.util;
+
+import com.linkedin.drelephant.math.Statistics;
+import org.apache.hadoop.security.authentication.client.AuthenticatedURL;
+import org.apache.hadoop.security.authentication.client.AuthenticationException;
+import org.apache.log4j.Logger;
+import org.codehaus.jackson.JsonNode;
+import org.codehaus.jackson.map.ObjectMapper;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public final class ThreadContextMR2 {
+ private static final Logger logger = Logger.getLogger(com.linkedin.drelephant.util.ThreadContextMR2.class);
+
+ private static final AtomicInteger THREAD_ID = new AtomicInteger(1);
+
+ private static final ThreadLocal _LOCAL_THREAD_ID = new ThreadLocal() {
+ @Override
+ public Integer initialValue() {
+ return THREAD_ID.getAndIncrement();
+ }
+ };
+
+ private static final ThreadLocal _LOCAL_LAST_UPDATED = new ThreadLocal();
+ private static final ThreadLocal _LOCAL_UPDATE_INTERVAL = new ThreadLocal();
+
+ private static final ThreadLocal _LOCAL_DIAGNOSTIC_PATTERN = new ThreadLocal() {
+ @Override
+ public Pattern initialValue() {
+ // Example: "Task task_1443068695259_9143_m_000475 failed 1 times"
+ return Pattern.compile(
+ ".*[\\s\\u00A0]+(task_[0-9]+_[0-9]+_[m|r]_[0-9]+)[\\s\\u00A0]+.*");
+ }
+ };
+
+ private static final ThreadLocal _LOCAL_AUTH_TOKEN =
+ new ThreadLocal() {
+ @Override
+ public AuthenticatedURL.Token initialValue() {
+ _LOCAL_LAST_UPDATED.set(System.currentTimeMillis());
+ // Random an interval for each executor to avoid update token at the same time
+ _LOCAL_UPDATE_INTERVAL.set(Statistics.MINUTE_IN_MS * 30 + new Random().nextLong()
+ % (3 * Statistics.MINUTE_IN_MS));
+ logger.info("Executor " + _LOCAL_THREAD_ID.get() + " update interval " + _LOCAL_UPDATE_INTERVAL.get() * 1.0
+ / Statistics.MINUTE_IN_MS);
+ return new AuthenticatedURL.Token();
+ }
+ };
+
+ private static final ThreadLocal _LOCAL_AUTH_URL = new ThreadLocal() {
+ @Override
+ public AuthenticatedURL initialValue() {
+ return new AuthenticatedURL();
+ }
+ };
+
+ private static final ThreadLocal _LOCAL_MAPPER = new ThreadLocal() {
+ @Override
+ public ObjectMapper initialValue() {
+ return new ObjectMapper();
+ }
+ };
+
+ private ThreadContextMR2() {
+ // Empty on purpose
+ }
+
+ public static Matcher getDiagnosticMatcher(String diagnosticInfo) {
+ return _LOCAL_DIAGNOSTIC_PATTERN.get().matcher(diagnosticInfo);
+ }
+
+ public static JsonNode readJsonNode(URL url) throws IOException, AuthenticationException {
+ HttpURLConnection conn = _LOCAL_AUTH_URL.get().openConnection(url, _LOCAL_AUTH_TOKEN.get());
+ return _LOCAL_MAPPER.get().readTree(conn.getInputStream());
+ }
+
+ public static void updateAuthToken() {
+ long curTime = System.currentTimeMillis();
+ if (curTime - _LOCAL_LAST_UPDATED.get() > _LOCAL_UPDATE_INTERVAL.get()) {
+ logger.info("Executor " + _LOCAL_THREAD_ID.get() + " updates its AuthenticatedToken.");
+ _LOCAL_AUTH_TOKEN.set(new AuthenticatedURL.Token());
+ _LOCAL_AUTH_URL.set(new AuthenticatedURL());
+ _LOCAL_LAST_UPDATED.set(curTime);
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/com/linkedin/drelephant/mapreduce/fetchers/MapReduceFetcherHadoop2Test.java b/test/com/linkedin/drelephant/mapreduce/fetchers/MapReduceFetcherHadoop2Test.java
index 531bb3724..8ce84bec1 100644
--- a/test/com/linkedin/drelephant/mapreduce/fetchers/MapReduceFetcherHadoop2Test.java
+++ b/test/com/linkedin/drelephant/mapreduce/fetchers/MapReduceFetcherHadoop2Test.java
@@ -18,6 +18,8 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+
+import com.linkedin.drelephant.util.ThreadContextMR2;
import org.junit.Assert;
import org.junit.Test;
diff --git a/test/com/linkedin/drelephant/tez/TezTaskLevelAggregatedMetricsTest.java b/test/com/linkedin/drelephant/tez/TezTaskLevelAggregatedMetricsTest.java
new file mode 100644
index 000000000..4a42a8617
--- /dev/null
+++ b/test/com/linkedin/drelephant/tez/TezTaskLevelAggregatedMetricsTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2017 Electronic Arts Inc.
+ *
+ * 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.drelephant.tez;
+
+
+import com.linkedin.drelephant.tez.data.TezCounterData;
+import com.linkedin.drelephant.tez.data.TezTaskData;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TezTaskLevelAggregatedMetricsTest {
+
+ @Test
+ public void testZeroTasks() {
+ TezTaskData taskData[] = {};
+ TezTaskLevelAggregatedMetrics taskMetrics = new TezTaskLevelAggregatedMetrics(taskData, 0, 0);
+ Assert.assertEquals(taskMetrics.getDelay(), 0);
+ Assert.assertEquals(taskMetrics.getResourceUsed(), 0);
+ Assert.assertEquals(taskMetrics.getResourceWasted(), 0);
+ }
+
+ @Test
+ public void testNullTaskArray() {
+ TezTaskLevelAggregatedMetrics taskMetrics = new TezTaskLevelAggregatedMetrics(null, 0, 0);
+ Assert.assertEquals(taskMetrics.getDelay(), 0);
+ Assert.assertEquals(taskMetrics.getResourceUsed(), 0);
+ Assert.assertEquals(taskMetrics.getResourceWasted(), 0);
+ }
+
+ @Test
+ public void testTaskLevelData() {
+ TezTaskData taskData[] = new TezTaskData[3];
+ TezCounterData counterData = new TezCounterData();
+ counterData.set(TezCounterData.CounterName.PHYSICAL_MEMORY_BYTES, 655577088L);
+ counterData.set(TezCounterData.CounterName.VIRTUAL_MEMORY_BYTES, 3051589632L);
+ long time[] = {0,0,0,1464218501117L, 1464218534148L};
+ taskData[0] = new TezTaskData("task", "id");
+ taskData[0].setTimeAndCounter(time,counterData);
+ taskData[1] = new TezTaskData("task", "id");
+ taskData[1].setTimeAndCounter(new long[5],counterData);
+ // Non-sampled task, which does not contain time and counter data
+ taskData[2] = new TezTaskData("task", "id");
+ TezTaskLevelAggregatedMetrics taskMetrics = new TezTaskLevelAggregatedMetrics(taskData, 4096L, 1463218501117L);
+ Assert.assertEquals(taskMetrics.getDelay(), 1000000000L);
+ Assert.assertEquals(taskMetrics.getResourceUsed(), 135168L);
+ Assert.assertEquals(taskMetrics.getResourceWasted(), 66627L);
+ }
+}
\ No newline at end of file
diff --git a/test/com/linkedin/drelephant/tez/fetchers/TezFetcherTest.java b/test/com/linkedin/drelephant/tez/fetchers/TezFetcherTest.java
new file mode 100644
index 000000000..bf38f10c3
--- /dev/null
+++ b/test/com/linkedin/drelephant/tez/fetchers/TezFetcherTest.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2017 Electronic Arts Inc.
+ *
+ * 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.drelephant.tez.fetchers;
+
+import java.util.regex.Matcher;
+import org.junit.Assert;
+import org.junit.Test;
+import com.linkedin.drelephant.util.ThreadContextMR2;
+
+public class TezFetcherTest {
+
+ @Test
+ public void testDiagnosticMatcher() {
+ Matcher matcher = ThreadContextMR2.getDiagnosticMatcher("Task task_1443068695259_9143_m_000475 failed 1 time");
+ Assert.assertEquals(".*[\\s\\u00A0]+(task_[0-9]+_[0-9]+_[m|r]_[0-9]+)[\\s\\u00A0]+.*", matcher.pattern().toString());
+ Assert.assertEquals(true, matcher.matches());
+ Assert.assertEquals(1, matcher.groupCount());
+ Assert.assertEquals("task_1443068695259_9143_m_000475", matcher.group(1));
+ }
+
+}
\ No newline at end of file
diff --git a/test/com/linkedin/drelephant/tez/heuristics/MapperDataSkewHeuristicTest.java b/test/com/linkedin/drelephant/tez/heuristics/MapperDataSkewHeuristicTest.java
new file mode 100644
index 000000000..8792e70f4
--- /dev/null
+++ b/test/com/linkedin/drelephant/tez/heuristics/MapperDataSkewHeuristicTest.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2017 Electronic Arts Inc.
+ *
+ * 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.drelephant.tez.heuristics;
+
+import com.linkedin.drelephant.analysis.*;
+import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.linkedin.drelephant.tez.data.TezApplicationData;
+import com.linkedin.drelephant.tez.data.TezCounterData;
+import com.linkedin.drelephant.tez.data.TezTaskData;
+import junit.framework.TestCase;
+
+
+public class MapperDataSkewHeuristicTest extends TestCase {
+
+ private static final long UNITSIZE = HDFSContext.HDFS_BLOCK_SIZE / 64; //1MB
+ private static final long UNITSIZETIME = 1000000; //1000sec
+
+ private static Map paramsMap = new HashMap();
+ private static Heuristic _heuristic = new MapperDataSkewHeuristic(new HeuristicConfigurationData("test_heuristic",
+ "test_class", "test_view", new ApplicationType("test_apptype"), paramsMap));
+
+ public void testCritical() throws IOException {
+ assertEquals(Severity.CRITICAL, analyzeJob(200, 200, 1 * UNITSIZE, 100 * UNITSIZE));
+ }
+
+ public void testSevere() throws IOException {
+ assertEquals(Severity.SEVERE, analyzeJob(200, 200, 10 * UNITSIZE, 100 * UNITSIZE));
+ }
+
+ public void testModerate() throws IOException {
+ assertEquals(Severity.MODERATE, analyzeJob(200, 200, 20 * UNITSIZE, 100 * UNITSIZE));
+ }
+
+ public void testLow() throws IOException {
+ assertEquals(Severity.LOW, analyzeJob(200, 200, 30 * UNITSIZE, 100 * UNITSIZE));
+ }
+
+ public void testNone() throws IOException {
+ assertEquals(Severity.NONE, analyzeJob(200, 200, 50 * UNITSIZE, 100 * UNITSIZE));
+ }
+
+ public void testSmallFiles() throws IOException {
+ assertEquals(Severity.NONE, analyzeJob(200, 200, 1 * UNITSIZE, 5 * UNITSIZE));
+ }
+
+ public void testSmallTasks() throws IOException {
+ assertEquals(Severity.NONE, analyzeJob(5, 5, 10 * UNITSIZE, 100 * UNITSIZE));
+ }
+
+ public void testCriticalTime() throws IOException {
+ assertEquals(Severity.CRITICAL, analyzeJobTime(200, 200, 1 * UNITSIZETIME, 100 * UNITSIZETIME));
+ }
+
+ public void testSevereTime() throws IOException {
+ assertEquals(Severity.SEVERE, analyzeJobTime(200, 200, 10 * UNITSIZETIME, 100 * UNITSIZETIME));
+ }
+
+ public void testModerateTime() throws IOException {
+ assertEquals(Severity.MODERATE, analyzeJobTime(200, 200, 20 * UNITSIZETIME, 100 * UNITSIZETIME));
+ }
+
+ public void testLowTime() throws IOException {
+ assertEquals(Severity.LOW, analyzeJobTime(200, 200, 30 * UNITSIZETIME, 100 * UNITSIZETIME));
+ }
+
+ public void testNoneTime() throws IOException {
+ assertEquals(Severity.NONE, analyzeJobTime(200, 200, 50 * UNITSIZETIME, 100 * UNITSIZETIME));
+ }
+
+ public void testSmallTasksTime() throws IOException {
+ assertEquals(Severity.NONE, analyzeJobTime(5, 5, 10 * UNITSIZETIME, 100 * UNITSIZETIME));
+ }
+
+ private Severity analyzeJob(int numSmallTasks, int numLargeTasks, long smallInputSize, long largeInputSize)
+ throws IOException {
+ TezCounterData jobCounter = new TezCounterData();
+ TezTaskData[] mappers = new TezTaskData[numSmallTasks + numLargeTasks + 1];
+
+ TezCounterData smallCounter = new TezCounterData();
+ smallCounter.set(TezCounterData.CounterName.HDFS_BYTES_READ, smallInputSize);
+
+ TezCounterData largeCounter = new TezCounterData();
+ largeCounter.set(TezCounterData.CounterName.S3A_BYTES_READ, largeInputSize);
+
+ int i = 0;
+ for (; i < numSmallTasks; i++) {
+ mappers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
+ mappers[i].setTimeAndCounter(new long[5], smallCounter);
+ }
+ for (; i < numSmallTasks + numLargeTasks; i++) {
+ mappers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
+ mappers[i].setTimeAndCounter(new long[5], largeCounter);
+ }
+ // Non-sampled task, which does not contain time and counter data
+ mappers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
+
+ TezApplicationData data = new TezApplicationData().setCounters(jobCounter).setMapTaskData(mappers);
+ HeuristicResult result = _heuristic.apply(data);
+ return result.getSeverity();
+
+ }
+
+ private Severity analyzeJobTime(int numSmallTasks, int numLongTasks, long smallTimeTaken, long longTimeTaken)
+ throws IOException {
+ TezCounterData jobCounter = new TezCounterData();
+ TezTaskData[] mappers = new TezTaskData[numSmallTasks + numLongTasks + 1];
+
+ int i = 0;
+ for (; i < numSmallTasks; i++) {
+ mappers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
+ mappers[i].setTotalTimeMs(smallTimeTaken, true);
+ mappers[i].setCounter(jobCounter);
+ }
+ for (; i < numSmallTasks + numLongTasks; i++) {
+ mappers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
+ mappers[i].setTotalTimeMs(longTimeTaken, true);
+ mappers[i].setCounter(jobCounter);
+ }
+ // Non-sampled task, which does not contain time data
+ mappers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
+
+ TezApplicationData data = new TezApplicationData().setCounters(jobCounter).setMapTaskData(mappers);
+ HeuristicResult result = _heuristic.apply(data);
+ return result.getSeverity();
+
+ }
+}
\ No newline at end of file
diff --git a/test/com/linkedin/drelephant/tez/heuristics/MapperGCHeuristicTest.java b/test/com/linkedin/drelephant/tez/heuristics/MapperGCHeuristicTest.java
new file mode 100644
index 000000000..52cea2035
--- /dev/null
+++ b/test/com/linkedin/drelephant/tez/heuristics/MapperGCHeuristicTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2017 Electronic Arts Inc.
+ *
+ * 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.drelephant.tez.heuristics;
+
+import com.linkedin.drelephant.analysis.ApplicationType;
+import com.linkedin.drelephant.analysis.HeuristicResult;
+import com.linkedin.drelephant.analysis.Severity;
+import com.linkedin.drelephant.tez.data.TezApplicationData;
+import com.linkedin.drelephant.tez.data.TezCounterData;
+import com.linkedin.drelephant.tez.data.TezTaskData;
+import com.linkedin.drelephant.analysis.Heuristic;
+import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
+import junit.framework.TestCase;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+public class MapperGCHeuristicTest extends TestCase {
+
+ private static Map paramsMap = new HashMap();
+
+ private static Heuristic _heuristic = new MapperGCHeuristic(new HeuristicConfigurationData("test_heuristic",
+ "test_class", "test_view", new ApplicationType("test_apptype"), paramsMap));
+
+ private static int NUMTASKS = 100;
+
+ public void testGCCritical() throws IOException {
+ assertEquals(Severity.CRITICAL, analyzeJob(1000000, 50000, 2000));
+ }
+
+ public void testGCSevere() throws IOException {
+ assertEquals(Severity.SEVERE, analyzeJob(1000000, 50000, 1500));
+ }
+
+ public void testGCModerate() throws IOException {
+ assertEquals(Severity.MODERATE, analyzeJob(1000000, 50000, 1000));
+ }
+
+ public void testGCNone() throws IOException {
+ assertEquals(Severity.NONE, analyzeJob(1000000, 50000, 300));
+ }
+
+ public void testShortTasksNone() throws IOException {
+ assertEquals(Severity.NONE, analyzeJob(100000, 50000, 2000));
+ }
+
+
+ private Severity analyzeJob(long runtimeMs, long cpuMs, long gcMs) throws IOException {
+ TezCounterData jobCounter = new TezCounterData();
+ TezTaskData[] mappers = new TezTaskData[NUMTASKS + 1];
+
+ TezCounterData counter = new TezCounterData();
+ counter.set(TezCounterData.CounterName.CPU_MILLISECONDS, cpuMs);
+ counter.set(TezCounterData.CounterName.GC_TIME_MILLIS, gcMs);
+
+ int i = 0;
+ for (; i < NUMTASKS; i++) {
+ mappers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
+ mappers[i].setTimeAndCounter(new long[]{runtimeMs, 0 , 0, 0, 0}, counter);
+ }
+ // Non-sampled task, which does not contain time and counter data
+ mappers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
+
+ TezApplicationData data = new TezApplicationData().setCounters(jobCounter).setMapTaskData(mappers);
+ HeuristicResult result = _heuristic.apply(data);
+ return result.getSeverity();
+ }
+
+}
\ No newline at end of file
diff --git a/test/com/linkedin/drelephant/tez/heuristics/MapperMemoryHeuristicTest.java b/test/com/linkedin/drelephant/tez/heuristics/MapperMemoryHeuristicTest.java
new file mode 100644
index 000000000..72e426743
--- /dev/null
+++ b/test/com/linkedin/drelephant/tez/heuristics/MapperMemoryHeuristicTest.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2017 Electronic Arts Inc.
+ *
+ * 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.drelephant.tez.heuristics;
+
+import com.linkedin.drelephant.analysis.ApplicationType;
+import com.linkedin.drelephant.analysis.Heuristic;
+import com.linkedin.drelephant.analysis.HeuristicResult;
+import com.linkedin.drelephant.analysis.Severity;
+import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
+import com.linkedin.drelephant.tez.data.TezApplicationData;
+import com.linkedin.drelephant.tez.data.TezCounterData;
+import com.linkedin.drelephant.tez.data.TezTaskData;
+import junit.framework.TestCase;
+import org.apache.commons.io.FileUtils;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+public class MapperMemoryHeuristicTest extends TestCase{
+
+ private static Map paramsMap = new HashMap();
+ private static Heuristic _heuristic = new MapperMemoryHeuristic(new HeuristicConfigurationData("test_heuristic",
+ "test_class", "test_view", new ApplicationType("test_apptype"), paramsMap));
+
+ private int NUMTASKS = 100;
+
+ public void testLargeContainerSizeCritical() throws IOException {
+ assertEquals(Severity.CRITICAL, analyzeJob(2048, 8192));
+ }
+
+ public void testLargeContainerSizeSevere() throws IOException {
+ assertEquals(Severity.SEVERE, analyzeJob(3072, 8192));
+ }
+
+ public void testLargeContainerSizeModerate() throws IOException {
+ assertEquals(Severity.MODERATE, analyzeJob(4096, 8192));
+ }
+
+ public void testLargeContainerSizeNone() throws IOException {
+ assertEquals(Severity.NONE, analyzeJob(6144, 8192));
+ }
+
+ // If the task use default container size, it should not be flagged
+ // Not using Default Container param, will calculate severity irrespective of default container size Chaning NONE -> CRITICAL
+ public void testDefaultContainerNone() throws IOException {
+ assertEquals(Severity.CRITICAL, analyzeJob(256, 2048));
+ }
+
+ // Not using Default Container param, will calculate severity irrespective of default container size Chaning NONE -> MODERATE
+ public void testDefaultContainerNoneMore() throws IOException {
+ assertEquals(Severity.MODERATE, analyzeJob(1024, 2048));
+ }
+
+ private Severity analyzeJob(long taskAvgMemMB, long containerMemMB) throws IOException {
+ TezCounterData jobCounter = new TezCounterData();
+ TezTaskData[] mappers = new TezTaskData[NUMTASKS + 1];
+
+ TezCounterData counter = new TezCounterData();
+ counter.set(TezCounterData.CounterName.PHYSICAL_MEMORY_BYTES, taskAvgMemMB* FileUtils.ONE_MB);
+
+ Properties p = new Properties();
+ p.setProperty(MapperMemoryHeuristic.MAPPER_MEMORY_CONF, Long.toString(containerMemMB));
+
+ int i = 0;
+ for (; i < NUMTASKS; i++) {
+ mappers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
+ mappers[i].setTime(new long[5]);
+ mappers[i].setCounter(counter);
+ }
+ // Non-sampled task, which does not contain time and counter data
+ mappers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
+
+ TezApplicationData data = new TezApplicationData().setCounters(jobCounter).setMapTaskData(mappers);
+ data.setConf(p);
+ HeuristicResult result = _heuristic.apply(data);
+ return result.getSeverity();
+ }
+}
\ No newline at end of file
diff --git a/test/com/linkedin/drelephant/tez/heuristics/MapperSpeedHeuristicTest.java b/test/com/linkedin/drelephant/tez/heuristics/MapperSpeedHeuristicTest.java
new file mode 100644
index 000000000..2d21f856a
--- /dev/null
+++ b/test/com/linkedin/drelephant/tez/heuristics/MapperSpeedHeuristicTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2017 Electronic Arts Inc.
+ *
+ * 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.drelephant.tez.heuristics;
+
+import com.linkedin.drelephant.analysis.ApplicationType;
+import com.linkedin.drelephant.analysis.Heuristic;
+import com.linkedin.drelephant.analysis.HeuristicResult;
+import com.linkedin.drelephant.analysis.Severity;
+import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
+import com.linkedin.drelephant.math.Statistics;
+import com.linkedin.drelephant.tez.data.TezApplicationData;
+import com.linkedin.drelephant.tez.data.TezCounterData;
+import com.linkedin.drelephant.tez.data.TezTaskData;
+import junit.framework.TestCase;
+import org.apache.commons.io.FileUtils;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+public class MapperSpeedHeuristicTest extends TestCase {
+
+ private static Map paramsMap = new HashMap();
+ private static Heuristic _heuristic = new MapperSpeedHeuristic(new HeuristicConfigurationData("test_heuristic",
+ "test_class", "test_view", new ApplicationType("test_apptype"), paramsMap));
+
+ private static final long MB_IN_BYTES = FileUtils.ONE_MB;
+ private static final long MINUTE_IN_MS = Statistics.MINUTE_IN_MS;
+ private static final int NUMTASKS = 100;
+
+ public void testCritical() throws IOException {
+ long runtime = 120 * MINUTE_IN_MS;
+ long speed_factor = (runtime * MB_IN_BYTES) / 1000;
+ assertEquals(Severity.CRITICAL, analyzeJob(runtime, 1 * speed_factor));
+ }
+
+ public void testSevere() throws IOException {
+ long runtime = 120 * MINUTE_IN_MS;
+ long speed_factor = (runtime * MB_IN_BYTES) / 1000;
+ assertEquals(Severity.SEVERE, analyzeJob(runtime, 4 * speed_factor));
+ }
+
+ public void testModerate() throws IOException {
+ long runtime = 120 * MINUTE_IN_MS;
+ long speed_factor = (runtime * MB_IN_BYTES) / 1000;
+ assertEquals(Severity.MODERATE, analyzeJob(runtime, 13 * speed_factor));
+ }
+
+ public void testLow() throws IOException {
+ long runtime = 120 * MINUTE_IN_MS;
+ long speed_factor = (runtime * MB_IN_BYTES) / 1000;
+ assertEquals(Severity.LOW, analyzeJob(runtime, 50 * speed_factor));
+ }
+
+ public void testNone() throws IOException {
+ long runtime = 120 * MINUTE_IN_MS;
+ long speed_factor = (runtime * MB_IN_BYTES) / 1000;
+ assertEquals(Severity.NONE, analyzeJob(runtime, 51 * speed_factor));
+ }
+
+ public void testShortTask() throws IOException {
+ long runtime = 2 * MINUTE_IN_MS;
+ long speed_factor = (runtime * MB_IN_BYTES) / 1000;
+ assertEquals(Severity.NONE, analyzeJob(runtime, 1 * speed_factor));
+ }
+
+ private Severity analyzeJob(long runtimeMs, long readBytes) throws IOException {
+ TezCounterData jobCounter = new TezCounterData();
+ TezTaskData[] mappers = new TezTaskData[NUMTASKS + 1];
+
+ TezCounterData counter = new TezCounterData();
+ counter.set(TezCounterData.CounterName.HDFS_BYTES_READ, readBytes / 2);
+ counter.set(TezCounterData.CounterName.S3A_BYTES_READ, readBytes / 2);
+
+ int i = 0;
+ for (; i < NUMTASKS; i++) {
+ mappers[i] = new TezTaskData(counter, new long[] { runtimeMs, 0, 0 ,0, 0});
+ }
+ // Non-sampled task, which does not contain time and counter data
+ mappers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
+
+ TezApplicationData data = new TezApplicationData().setCounters(jobCounter).setMapTaskData(mappers);
+ HeuristicResult result = _heuristic.apply(data);
+ return result.getSeverity();
+ }
+}
\ No newline at end of file
diff --git a/test/com/linkedin/drelephant/tez/heuristics/MapperSpillHeuristicTest.java b/test/com/linkedin/drelephant/tez/heuristics/MapperSpillHeuristicTest.java
new file mode 100644
index 000000000..62831a727
--- /dev/null
+++ b/test/com/linkedin/drelephant/tez/heuristics/MapperSpillHeuristicTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2017 Electronic Arts Inc.
+ *
+ * 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.drelephant.tez.heuristics;
+
+import com.linkedin.drelephant.analysis.ApplicationType;
+import com.linkedin.drelephant.analysis.Heuristic;
+import com.linkedin.drelephant.analysis.HeuristicResult;
+import com.linkedin.drelephant.analysis.Severity;
+import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
+import com.linkedin.drelephant.tez.data.TezApplicationData;
+import com.linkedin.drelephant.tez.data.TezCounterData;
+import com.linkedin.drelephant.tez.data.TezTaskData;
+import junit.framework.TestCase;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+
+public class MapperSpillHeuristicTest extends TestCase{
+
+ private static Map paramsMap = new HashMap();
+ private static Heuristic _heuristic = new MapperSpillHeuristic(new HeuristicConfigurationData("test_heuristic",
+ "test_class", "test_view", new ApplicationType("test_apptype"), paramsMap));
+
+ public void testCritical() throws IOException {
+ // Spill ratio 3.0, 1000 tasks
+ assertEquals(Severity.CRITICAL, analyzeJob(3000, 1000, 1000));
+ }
+
+ public void testSevere() throws IOException {
+ // Spill ratio 2.5, 1000 tasks
+ assertEquals(Severity.SEVERE, analyzeJob(2500, 1000, 1000));
+ }
+
+ public void testModerate() throws IOException {
+ // Spill ratio 2.3, 1000 tasks
+ assertEquals(Severity.MODERATE, analyzeJob(2300, 1000, 1000));
+ }
+
+ public void testLow() throws IOException {
+ // Spill ratio 2.1, 1000 tasks
+ assertEquals(Severity.LOW, analyzeJob(2100, 1000, 1000));
+ }
+
+ public void testNone() throws IOException {
+ // Spill ratio 1.0, 1000 tasks
+ assertEquals(Severity.NONE, analyzeJob(1000, 1000, 1000));
+ }
+
+ public void testSmallNumTasks() throws IOException {
+ // Spill ratio 3.0, should be critical, but number of task is small(10), final result is NONE
+ assertEquals(Severity.NONE, analyzeJob(3000, 1000, 10));
+ }
+
+ private Severity analyzeJob(long spilledRecords, long mapRecords, int numTasks) throws IOException {
+ TezCounterData jobCounter = new TezCounterData();
+ TezTaskData[] mappers = new TezTaskData[numTasks + 1];
+
+ TezCounterData counter = new TezCounterData();
+ counter.set(TezCounterData.CounterName.SPILLED_RECORDS, spilledRecords);
+ counter.set(TezCounterData.CounterName.OUTPUT_RECORDS, mapRecords);
+
+ int i = 0;
+ for (; i < numTasks; i++) {
+ mappers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
+ mappers[i].setTimeAndCounter(new long[5], counter);
+ }
+ // Non-sampled task, which does not contain time and counter data
+ mappers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
+
+ TezApplicationData data = new TezApplicationData().setCounters(jobCounter).setMapTaskData(mappers);
+ HeuristicResult result = _heuristic.apply(data);
+ return result.getSeverity();
+ }
+}
\ No newline at end of file
diff --git a/test/com/linkedin/drelephant/tez/heuristics/MapperTimeHeuristicTest.java b/test/com/linkedin/drelephant/tez/heuristics/MapperTimeHeuristicTest.java
new file mode 100644
index 000000000..39c6ec891
--- /dev/null
+++ b/test/com/linkedin/drelephant/tez/heuristics/MapperTimeHeuristicTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2017 Electronic Arts Inc.
+ *
+ * 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.drelephant.tez.heuristics;
+
+import com.linkedin.drelephant.analysis.ApplicationType;
+import com.linkedin.drelephant.analysis.Heuristic;
+import com.linkedin.drelephant.analysis.HeuristicResult;
+import com.linkedin.drelephant.analysis.Severity;
+import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
+import com.linkedin.drelephant.math.Statistics;
+import com.linkedin.drelephant.tez.data.TezApplicationData;
+import com.linkedin.drelephant.tez.data.TezCounterData;
+import com.linkedin.drelephant.tez.data.TezTaskData;
+import junit.framework.TestCase;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+
+public class MapperTimeHeuristicTest extends TestCase {
+
+ private static final long DUMMY_INPUT_SIZE = 0;
+
+ private static Map paramsMap = new HashMap();
+ private static Heuristic _heuristic = new MapperTimeHeuristic(new HeuristicConfigurationData("test_heuristic",
+ "test_class", "test_view", new ApplicationType("test_apptype"), paramsMap));
+
+ // Test batch 1: Large runtime. Heuristic is not affected by various number of tasks */
+
+ public void testLongRuntimeTasksCritical() throws IOException {
+ // Should decrease split size and increase number of tasks
+ assertEquals(Severity.CRITICAL, analyzeJob(10, 120 * Statistics.MINUTE_IN_MS));
+ }
+
+ public void testLongRuntimeTasksCriticalMore() throws IOException {
+ // Should decrease split size and increase number of tasks
+ assertEquals(Severity.CRITICAL, analyzeJob(1000, 120 * Statistics.MINUTE_IN_MS));
+ }
+
+ public void testLongRuntimeTasksSevere() throws IOException {
+ // Should decrease split size and increase number of tasks
+ assertEquals(Severity.SEVERE, analyzeJob(10, 60 * Statistics.MINUTE_IN_MS));
+ }
+
+ public void testLongRuntimeTasksSevereMore() throws IOException {
+ // Should decrease split size and increase number of tasks
+ assertEquals(Severity.SEVERE, analyzeJob(1000, 60 * Statistics.MINUTE_IN_MS));
+ }
+
+ // Test batch 2: Short runtime and various number of tasks
+
+ public void testShortRuntimeTasksCritical() throws IOException {
+ // Should increase split size and decrease number of tasks
+ assertEquals(Severity.CRITICAL, analyzeJob(1000, 1 * Statistics.MINUTE_IN_MS));
+ }
+
+ public void testShortRuntimeTasksSevere() throws IOException {
+ // Should increase split size and decrease number of tasks
+ assertEquals(Severity.SEVERE, analyzeJob(500, 1 * Statistics.MINUTE_IN_MS));
+ }
+
+ public void testShortRuntimeTasksModerate() throws IOException {
+ assertEquals(Severity.MODERATE, analyzeJob(101, 1 * Statistics.MINUTE_IN_MS));
+ }
+
+ public void testShortRuntimeTasksLow() throws IOException {
+ assertEquals(Severity.LOW, analyzeJob(50, 1 * Statistics.MINUTE_IN_MS));
+ }
+
+ public void testShortRuntimeTasksNone() throws IOException {
+ // Small file with small number of tasks and short runtime. This should be the common case.
+ assertEquals(Severity.NONE, analyzeJob(5, 1 * Statistics.MINUTE_IN_MS));
+ }
+
+ private Severity analyzeJob(int numTasks, long runtime) throws IOException {
+ TezCounterData jobCounter = new TezCounterData();
+ TezTaskData[] mappers = new TezTaskData[numTasks + 1];
+
+ TezCounterData taskCounter = new TezCounterData();
+ taskCounter.set(TezCounterData.CounterName.S3A_BYTES_READ, DUMMY_INPUT_SIZE / 4);
+
+
+ int i = 0;
+ for (; i < numTasks; i++) {
+ mappers[i] = new TezTaskData(jobCounter,new long[] { runtime, 0, 0, 0, 0 });
+ }
+ // Non-sampled task, which does not contain time and counter data
+ mappers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
+
+ TezApplicationData data = new TezApplicationData().setCounters(jobCounter).setMapTaskData(mappers);
+ HeuristicResult result = _heuristic.apply(data);
+ return result.getSeverity();
+ }
+
+
+}
\ No newline at end of file
diff --git a/test/com/linkedin/drelephant/tez/heuristics/ReducerDataSkewHeuristicTest.java b/test/com/linkedin/drelephant/tez/heuristics/ReducerDataSkewHeuristicTest.java
new file mode 100644
index 000000000..ff95e862a
--- /dev/null
+++ b/test/com/linkedin/drelephant/tez/heuristics/ReducerDataSkewHeuristicTest.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2017 Electronic Arts Inc.
+ *
+ * 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.drelephant.tez.heuristics;
+
+import com.linkedin.drelephant.analysis.*;
+import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
+import com.linkedin.drelephant.tez.data.TezApplicationData;
+import com.linkedin.drelephant.tez.data.TezCounterData;
+import com.linkedin.drelephant.tez.data.TezTaskData;
+import junit.framework.TestCase;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+public class ReducerDataSkewHeuristicTest extends TestCase{
+
+ private static final long UNITSIZE = HDFSContext.HDFS_BLOCK_SIZE / 64; //1mb
+ private static final long UNITSIZETIME = 1000000; //1000sec
+
+ private static Map paramsMap = new HashMap();
+ private static Heuristic _heuristic = new ReducerDataSkewHeuristic(new HeuristicConfigurationData("test_heuristic",
+ "test_class", "test_view", new ApplicationType("test_apptype"), paramsMap));
+
+ public void testCritical() throws IOException {
+ assertEquals(Severity.CRITICAL, analyzeJob(200, 200, 1 * UNITSIZE, 100 * UNITSIZE));
+ }
+
+ public void testSevere() throws IOException {
+ assertEquals(Severity.SEVERE, analyzeJob(200, 200, 10 * UNITSIZE, 100 * UNITSIZE));
+ }
+
+ public void testModerate() throws IOException {
+ assertEquals(Severity.MODERATE, analyzeJob(200, 200, 20 * UNITSIZE, 100 * UNITSIZE));
+ }
+
+ public void testLow() throws IOException {
+ assertEquals(Severity.LOW, analyzeJob(200, 200, 30 * UNITSIZE, 100 * UNITSIZE));
+ }
+
+ public void testNone() throws IOException {
+ assertEquals(Severity.NONE, analyzeJob(200, 200, 50 * UNITSIZE, 100 * UNITSIZE));
+ }
+
+ public void testSmallFiles() throws IOException {
+ assertEquals(Severity.NONE, analyzeJob(200, 200, 1 * UNITSIZE, 5 * UNITSIZE));
+ }
+
+ public void testSmallTasks() throws IOException {
+ assertEquals(Severity.NONE, analyzeJob(5, 5, 10 * UNITSIZE, 100 * UNITSIZE));
+ }
+
+ public void testCriticalTime() throws IOException {
+ assertEquals(Severity.CRITICAL, analyzeJobTime(200, 200, 1 * UNITSIZETIME, 100 * UNITSIZETIME));
+ }
+
+ public void testSevereTime() throws IOException {
+ assertEquals(Severity.SEVERE, analyzeJobTime(200, 200, 10 * UNITSIZETIME, 100 * UNITSIZETIME));
+ }
+
+ public void testModerateTime() throws IOException {
+ assertEquals(Severity.MODERATE, analyzeJobTime(200, 200, 20 * UNITSIZETIME, 100 * UNITSIZETIME));
+ }
+
+ public void testLowTime() throws IOException {
+ assertEquals(Severity.LOW, analyzeJobTime(200, 200, 30 * UNITSIZETIME, 100 * UNITSIZETIME));
+ }
+
+ public void testNoneTime() throws IOException {
+ assertEquals(Severity.NONE, analyzeJobTime(200, 200, 50 * UNITSIZETIME, 100 * UNITSIZETIME));
+ }
+
+ public void testSmallTasksTime() throws IOException {
+ assertEquals(Severity.NONE, analyzeJobTime(5, 5, 10 * UNITSIZETIME, 100 * UNITSIZETIME));
+ }
+
+ private Severity analyzeJob(int numSmallTasks, int numLargeTasks, long smallInputSize, long largeInputSize)
+ throws IOException {
+ TezCounterData jobCounter = new TezCounterData();
+ TezTaskData[] reducers = new TezTaskData[numSmallTasks + numLargeTasks + 1];
+
+ TezCounterData smallCounter = new TezCounterData();
+ smallCounter.set(TezCounterData.CounterName.SHUFFLE_BYTES, smallInputSize);
+
+ TezCounterData largeCounter = new TezCounterData();
+ largeCounter.set(TezCounterData.CounterName.SHUFFLE_BYTES, largeInputSize);
+
+ int i = 0;
+ for (; i < numSmallTasks; i++) {
+ reducers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
+ reducers[i].setTimeAndCounter(new long[5], smallCounter);
+ }
+ for (; i < numSmallTasks + numLargeTasks; i++) {
+ reducers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
+ reducers[i].setTimeAndCounter(new long[5], largeCounter);
+ }
+ // Non-sampled task, which does not contain time and counter data
+ reducers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
+
+ TezApplicationData data = new TezApplicationData().setCounters(jobCounter).setReduceTaskData(reducers);
+ HeuristicResult result = _heuristic.apply(data);
+ return result.getSeverity();
+ }
+
+ private Severity analyzeJobTime(int numSmallTasks, int numLongTasks, long smallTimeTaken, long longTimeTaken)
+ throws IOException {
+ TezCounterData jobCounter = new TezCounterData();
+ TezTaskData[] reducers = new TezTaskData[numSmallTasks + numLongTasks + 1];
+
+ int i = 0;
+ for (; i < numSmallTasks; i++) {
+ reducers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
+ reducers[i].setTotalTimeMs(smallTimeTaken, true);
+ reducers[i].setCounter(jobCounter);
+ }
+ for (; i < numSmallTasks + numLongTasks; i++) {
+ reducers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
+ reducers[i].setTotalTimeMs(longTimeTaken, true);
+ reducers[i].setCounter(jobCounter);
+ }
+ // Non-sampled task, which does not contain time data
+ reducers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
+ TezApplicationData data = new TezApplicationData().setCounters(jobCounter).setReduceTaskData(reducers);
+ HeuristicResult result = _heuristic.apply(data);
+ return result.getSeverity();
+
+ }
+}
\ No newline at end of file
diff --git a/test/com/linkedin/drelephant/tez/heuristics/ReducerGCHeuristicTest.java b/test/com/linkedin/drelephant/tez/heuristics/ReducerGCHeuristicTest.java
new file mode 100644
index 000000000..4a86fbefb
--- /dev/null
+++ b/test/com/linkedin/drelephant/tez/heuristics/ReducerGCHeuristicTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2017 Electronic Arts Inc.
+ *
+ * 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.drelephant.tez.heuristics;
+
+import com.linkedin.drelephant.analysis.ApplicationType;
+import com.linkedin.drelephant.analysis.Heuristic;
+import com.linkedin.drelephant.analysis.HeuristicResult;
+import com.linkedin.drelephant.analysis.Severity;
+import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
+import com.linkedin.drelephant.tez.data.TezApplicationData;
+import com.linkedin.drelephant.tez.data.TezCounterData;
+import com.linkedin.drelephant.tez.data.TezTaskData;
+import junit.framework.TestCase;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+public class ReducerGCHeuristicTest extends TestCase{
+
+ private static Map paramsMap = new HashMap();
+
+ private static Heuristic _heuristic = new ReducerGCHeuristic(new HeuristicConfigurationData("test_heuristic",
+ "test_class", "test_view", new ApplicationType("test_apptype"), paramsMap));
+
+ private static int NUMTASKS = 100;
+
+ public void testGCCritical() throws IOException {
+ assertEquals(Severity.CRITICAL, analyzeJob(1000000, 50000, 2000));
+ }
+
+ public void testGCSevere() throws IOException {
+ assertEquals(Severity.SEVERE, analyzeJob(1000000, 50000, 1500));
+ }
+
+ public void testGCModerate() throws IOException {
+ assertEquals(Severity.MODERATE, analyzeJob(1000000, 50000, 1000));
+ }
+
+ public void testGCNone() throws IOException {
+ assertEquals(Severity.NONE, analyzeJob(1000000, 50000, 300));
+ }
+
+ public void testShortTasksNone() throws IOException {
+ assertEquals(Severity.NONE, analyzeJob(100000, 50000, 2000));
+ }
+
+
+ private Severity analyzeJob(long runtimeMs, long cpuMs, long gcMs) throws IOException {
+ TezCounterData jobCounter = new TezCounterData();
+ TezTaskData[] reducers = new TezTaskData[NUMTASKS + 1];
+
+ TezCounterData counter = new TezCounterData();
+ counter.set(TezCounterData.CounterName.CPU_MILLISECONDS, cpuMs);
+ counter.set(TezCounterData.CounterName.GC_TIME_MILLIS, gcMs);
+
+ int i = 0;
+ for (; i < NUMTASKS; i++) {
+ reducers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
+ reducers[i].setTimeAndCounter(new long[] { runtimeMs, 0, 0, 0, 0 }, counter);
+ }
+ // Non-sampled task, which does not contain time and counter data
+ reducers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
+
+ TezApplicationData data = new TezApplicationData().setCounters(jobCounter).setReduceTaskData(reducers);
+ HeuristicResult result = _heuristic.apply(data);
+ return result.getSeverity();
+ }
+}
\ No newline at end of file
diff --git a/test/com/linkedin/drelephant/tez/heuristics/ReducerMemoryHeuristicTest.java b/test/com/linkedin/drelephant/tez/heuristics/ReducerMemoryHeuristicTest.java
new file mode 100644
index 000000000..66c3e193c
--- /dev/null
+++ b/test/com/linkedin/drelephant/tez/heuristics/ReducerMemoryHeuristicTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2017 Electronic Arts Inc.
+ *
+ * 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.drelephant.tez.heuristics;
+
+import com.linkedin.drelephant.analysis.ApplicationType;
+import com.linkedin.drelephant.analysis.Heuristic;
+import com.linkedin.drelephant.analysis.HeuristicResult;
+import com.linkedin.drelephant.analysis.Severity;
+import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
+import com.linkedin.drelephant.tez.data.TezApplicationData;
+import com.linkedin.drelephant.tez.data.TezCounterData;
+import com.linkedin.drelephant.tez.data.TezTaskData;
+import junit.framework.TestCase;
+import org.apache.commons.io.FileUtils;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+public class ReducerMemoryHeuristicTest extends TestCase {
+
+ private static Map paramsMap = new HashMap();
+ private static Heuristic _heuristic = new ReducerMemoryHeuristic(new HeuristicConfigurationData("test_heuristic",
+ "test_class", "test_view", new ApplicationType("test_apptype"), paramsMap));
+
+ private int NUMTASKS = 100;
+
+ public void testLargeContainerSizeCritical() throws IOException {
+ assertEquals(Severity.CRITICAL, analyzeJob(2048, 8192));
+ }
+
+ public void testLargeContainerSizeSevere() throws IOException {
+ assertEquals(Severity.SEVERE, analyzeJob(3072, 8192));
+ }
+
+ public void testLargeContainerSizeModerate() throws IOException {
+ assertEquals(Severity.MODERATE, analyzeJob(4096, 8192));
+ }
+
+ public void testLargeContainerSizeNone() throws IOException {
+ assertEquals(Severity.NONE, analyzeJob(6144, 8192));
+ }
+
+ // If the task use default container size, it should not be flagged
+ // Not using Default Container param, will calculate severity irrespective of default container size Chaning NONE -> CRITICAL
+ public void testDefaultContainerNone() throws IOException {
+ assertEquals(Severity.CRITICAL, analyzeJob(256, 2048));
+ }
+
+ // Not using Default Container param, will calculate severity irrespective of default container size Chaning NONE -> MODERATE
+ public void testDefaultContainerNoneMore() throws IOException {
+ assertEquals(Severity.MODERATE, analyzeJob(1024, 2048));
+ }
+
+ private Severity analyzeJob(long taskAvgMemMB, long containerMemMB) throws IOException {
+ TezCounterData jobCounter = new TezCounterData();
+ TezTaskData[] reducers = new TezTaskData[NUMTASKS + 1];
+
+ TezCounterData counter = new TezCounterData();
+ counter.set(TezCounterData.CounterName.PHYSICAL_MEMORY_BYTES, taskAvgMemMB* FileUtils.ONE_MB);
+
+ Properties p = new Properties();
+ p.setProperty(com.linkedin.drelephant.mapreduce.heuristics.ReducerMemoryHeuristic.REDUCER_MEMORY_CONF, Long.toString(containerMemMB));
+
+ int i = 0;
+ for (; i < NUMTASKS; i++) {
+ reducers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
+ reducers[i].setTimeAndCounter(new long[5], counter);
+ }
+ // Non-sampled task, which does not contain time and counter data
+ reducers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
+
+ TezApplicationData data = new TezApplicationData().setCounters(jobCounter).setReduceTaskData(reducers);
+ data.setConf(p);
+ HeuristicResult result = _heuristic.apply(data);
+ return result.getSeverity();
+ }
+}
\ No newline at end of file
diff --git a/test/com/linkedin/drelephant/tez/heuristics/ReducerTimeHeuristicTest.java b/test/com/linkedin/drelephant/tez/heuristics/ReducerTimeHeuristicTest.java
new file mode 100644
index 000000000..ab0abbe38
--- /dev/null
+++ b/test/com/linkedin/drelephant/tez/heuristics/ReducerTimeHeuristicTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2017 Electronic Arts Inc.
+ *
+ * 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.drelephant.tez.heuristics;
+
+import com.linkedin.drelephant.analysis.ApplicationType;
+import com.linkedin.drelephant.analysis.Heuristic;
+import com.linkedin.drelephant.analysis.HeuristicResult;
+import com.linkedin.drelephant.analysis.Severity;
+import com.linkedin.drelephant.configurations.heuristic.HeuristicConfigurationData;
+import com.linkedin.drelephant.math.Statistics;
+import com.linkedin.drelephant.tez.data.TezApplicationData;
+import com.linkedin.drelephant.tez.data.TezCounterData;
+import com.linkedin.drelephant.tez.data.TezTaskData;
+import junit.framework.TestCase;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+
+public class ReducerTimeHeuristicTest extends TestCase {
+
+ private static Map paramsMap = new HashMap();
+ private static Heuristic _heuristic = new ReducerTimeHeuristic(new HeuristicConfigurationData("test_heuristic",
+ "test_class", "test_view", new ApplicationType("test_apptype"), paramsMap));
+
+ private static final long MINUTE_IN_MS = Statistics.MINUTE_IN_MS;;
+
+ public void testShortRunetimeCritical() throws IOException {
+ assertEquals(Severity.CRITICAL, analyzeJob(1 * MINUTE_IN_MS, 1000));
+ }
+
+ public void testShortRunetimeSevere() throws IOException {
+ assertEquals(Severity.SEVERE, analyzeJob(1 * MINUTE_IN_MS, 500));
+ }
+
+ public void testShortRunetimeModerate() throws IOException {
+ assertEquals(Severity.MODERATE, analyzeJob(1 * MINUTE_IN_MS, 101));
+ }
+
+ public void testShortRunetimeLow() throws IOException {
+ assertEquals(Severity.LOW, analyzeJob(1 * MINUTE_IN_MS, 50));
+ }
+
+ public void testShortRunetimeNone() throws IOException {
+ assertEquals(Severity.NONE, analyzeJob(1 * MINUTE_IN_MS, 2));
+ }
+
+ public void testLongRunetimeCritical() throws IOException {
+ assertEquals(Severity.CRITICAL, analyzeJob(120 * MINUTE_IN_MS, 10));
+ }
+
+ // Long runtime severity is not affected by number of tasks
+ public void testLongRunetimeCriticalMore() throws IOException {
+ assertEquals(Severity.CRITICAL, analyzeJob(120 * MINUTE_IN_MS, 1000));
+ }
+
+ public void testLongRunetimeSevere() throws IOException {
+ assertEquals(Severity.SEVERE, analyzeJob(60 * MINUTE_IN_MS, 10));
+ }
+
+ public void testLongRunetimeSevereMore() throws IOException {
+ assertEquals(Severity.SEVERE, analyzeJob(60 * MINUTE_IN_MS, 1000));
+ }
+
+ private Severity analyzeJob(long runtimeMs, int numTasks) throws IOException {
+ TezCounterData dummyCounter = new TezCounterData();
+ TezTaskData[] reducers = new TezTaskData[numTasks + 1];
+
+ int i = 0;
+ for (; i < numTasks; i++) {
+ reducers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
+ reducers[i].setTime(new long[] { runtimeMs, 0, 0, 0, 0 });
+ reducers[i].setCounter(dummyCounter);
+ }
+ // Non-sampled task, which does not contain time and counter data
+ reducers[i] = new TezTaskData("task-id-"+i, "task-attempt-id-"+i);
+
+ TezApplicationData data = new TezApplicationData().setCounters(dummyCounter).setReduceTaskData(reducers);
+ HeuristicResult result = _heuristic.apply(data);
+ return result.getSeverity();
+ }
+}
\ No newline at end of file
From 51fe80a35055e9de9167b8f60f71e035e72e0a94 Mon Sep 17 00:00:00 2001
From: Arpan Agrawal
Date: Tue, 10 Jul 2018 22:55:01 -0700
Subject: [PATCH 11/15] Auto tuning: Support for parameter set multi-try (#386)
---
.../tuning/AutoTuningAPIHelper.java | 562 +++++++++---------
.../tuning/AzkabanJobCompleteDetector.java | 59 +-
.../tuning/BaselineComputeUtil.java | 13 +-
.../drelephant/tuning/FitnessComputeUtil.java | 403 ++++++++-----
.../tuning/JobCompleteDetector.java | 87 +--
.../drelephant/tuning/JobTuningInfo.java | 2 -
.../drelephant/tuning/PSOParamGenerator.java | 20 +-
.../drelephant/tuning/ParamGenerator.java | 183 +++---
.../linkedin/drelephant/tuning/Particle.java | 1 -
.../drelephant/tuning/TuningInput.java | 48 +-
app/controllers/Application.java | 41 +-
app/models/FlowDefinition.java | 24 +
app/models/FlowExecution.java | 24 +
app/models/JobDefinition.java | 17 +-
app/models/JobExecution.java | 31 +-
app/models/JobSavedState.java | 14 +
app/models/JobSuggestedParamSet.java | 118 ++++
app/models/JobSuggestedParamValue.java | 30 +-
app/models/TuningAlgorithm.java | 15 +
app/models/TuningJobDefinition.java | 16 +-
...n.java => TuningJobExecutionParamSet.java} | 60 +-
app/models/TuningParameter.java | 16 +-
conf/evolutions/default/2.sql | 18 +-
conf/evolutions/default/3.sql | 16 +
conf/evolutions/default/4.sql | 16 +
conf/evolutions/default/5.sql | 109 ++--
conf/evolutions/default/6.sql | 11 -
.../tuning/PSOParamGeneratorTest.java | 16 +-
test/resources/AutoTuningConf.xml | 4 +-
test/resources/test-init.sql | 39 +-
test/resources/tunein-test1.sql | 56 +-
test/rest/RestAPITest.java | 43 +-
32 files changed, 1269 insertions(+), 843 deletions(-)
create mode 100644 app/models/JobSuggestedParamSet.java
rename app/models/{TuningJobExecution.java => TuningJobExecutionParamSet.java} (50%)
delete mode 100644 conf/evolutions/default/6.sql
diff --git a/app/com/linkedin/drelephant/tuning/AutoTuningAPIHelper.java b/app/com/linkedin/drelephant/tuning/AutoTuningAPIHelper.java
index 14346fa17..b6b5b68ef 100644
--- a/app/com/linkedin/drelephant/tuning/AutoTuningAPIHelper.java
+++ b/app/com/linkedin/drelephant/tuning/AutoTuningAPIHelper.java
@@ -19,21 +19,27 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.linkedin.drelephant.ElephantContext;
import com.linkedin.drelephant.util.Utils;
+
import controllers.AutoTuningMetricsController;
+
+import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+
import models.FlowDefinition;
import models.FlowExecution;
import models.JobDefinition;
import models.JobExecution;
import models.JobExecution.ExecutionState;
+import models.JobSuggestedParamSet;
+import models.JobSuggestedParamSet.ParamSetStatus;
import models.JobSuggestedParamValue;
import models.TuningAlgorithm;
import models.TuningJobDefinition;
-import models.TuningJobExecution;
-import models.TuningJobExecution.ParamSetStatus;
+import models.TuningJobExecutionParamSet;
import models.TuningParameter;
+
import org.apache.commons.io.FileUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.log4j.Logger;
@@ -44,96 +50,46 @@
*/
public class AutoTuningAPIHelper {
- public static final String ALLOWED_MAX_RESOURCE_USAGE_PERCENT_DEFAULT =
+ private static final String ALLOWED_MAX_RESOURCE_USAGE_PERCENT_DEFAULT =
"autotuning.default.allowed_max_resource_usage_percent";
- public static final String ALLOWED_MAX_EXECUTION_TIME_PERCENT_DEFAULT =
+ private static final String ALLOWED_MAX_EXECUTION_TIME_PERCENT_DEFAULT =
"autotuning.default.allowed_max_execution_time_percent";
private static final Logger logger = Logger.getLogger(AutoTuningAPIHelper.class);
/**
- * For a job, returns the execution with the best parameter set if available else the one with the default parameter set.
+ * For a job, returns the best parameter set of the given job if it exists else the default parameter set
* @param jobDefId Sting JobDefId of the job
- * @return TuningJobExecution with the best parameter set if available else the one with the default parameter set.
+ * @return JobSuggestedParamSet the best parameter set of the given job if it exists else the default parameter set
*/
-
- private TuningJobExecution getBestParamSetTuningJobExecution(String jobDefId) {
- TuningJobExecution tuningJobExecutionBestParamSet = TuningJobExecution.find.select("*")
+ private JobSuggestedParamSet getBestParamSet(String jobDefId) {
+ JobSuggestedParamSet jobSuggestedParamSetBestParamSet = JobSuggestedParamSet.find.select("*")
.where()
- .eq(TuningJobExecution.TABLE.jobExecution + "." + JobExecution.TABLE.job + "." + JobDefinition.TABLE.jobDefId,
- jobDefId)
- .eq(TuningJobExecution.TABLE.isParamSetBest, true)
+ .eq(JobSuggestedParamSet.TABLE.jobDefinition + "." + JobDefinition.TABLE.jobDefId, jobDefId)
+ .eq(JobSuggestedParamSet.TABLE.isParamSetBest, true)
.setMaxRows(1)
.findUnique();
- if (tuningJobExecutionBestParamSet == null) {
- tuningJobExecutionBestParamSet = TuningJobExecution.find.select("*")
+ if (jobSuggestedParamSetBestParamSet == null) {
+ jobSuggestedParamSetBestParamSet = JobSuggestedParamSet.find.select("*")
.where()
- .eq(TuningJobExecution.TABLE.jobExecution + "." + JobExecution.TABLE.job + "." + JobDefinition.TABLE.jobDefId,
- jobDefId)
- .eq(TuningJobExecution.TABLE.isDefaultExecution, true)
+ .eq(JobSuggestedParamSet.TABLE.jobDefinition + "." + JobDefinition.TABLE.jobDefId, jobDefId)
+ .eq(JobSuggestedParamSet.TABLE.isParamSetDefault, true)
.setMaxRows(1)
.findUnique();
}
- return tuningJobExecutionBestParamSet;
+ return jobSuggestedParamSetBestParamSet;
}
/**
- * Returns the param set corresponding to a given job execution id
- * @param jobExecutionId Long job execution id of the execution
- * @return List: list of parameters
+ * Returns the param values corresponding to the given param set id
+ * @param paramSetId Long parameter set id
+ * @return List list of parameters
*/
- private List getJobExecutionParamSet(Long jobExecutionId) {
- return JobSuggestedParamValue.find.where()
- .eq(JobSuggestedParamValue.TABLE.jobExecution + "." + JobExecution.TABLE.id, jobExecutionId)
+ private List getParamSetValues(Long paramSetId) {
+ List jobSuggestedParamValues = JobSuggestedParamValue.find.where()
+ .eq(JobSuggestedParamValue.TABLE.jobSuggestedParamSet + '.' + JobSuggestedParamSet.TABLE.id, paramSetId)
.findList();
- }
-
- /**
- * This method creates a job execution with best parameter set. This is required when there is no parameters set or tuning has been switch off for the job
- * remains for suggestion.
- * @param tuningJobDefinition Job definition
- * @return Tuning Job Execution with best parameters
- */
- private TuningJobExecution cloneBestParamSetTuningJobExecution(TuningJobDefinition tuningJobDefinition) {
- logger.info("Searching for best param set for job: " + tuningJobDefinition.job.jobName);
-
- TuningJobExecution bestParamSetTuningJobExecution =
- getBestParamSetTuningJobExecution(tuningJobDefinition.job.jobDefId);
- List jobSuggestedParamValueList =
- getJobExecutionParamSet(bestParamSetTuningJobExecution.jobExecution.id);
-
- TuningJobExecution tuningJobExecution = new TuningJobExecution();
- JobExecution jobExecution = new JobExecution();
- jobExecution.id = 0L;
- jobExecution.job = bestParamSetTuningJobExecution.jobExecution.job;
- jobExecution.executionState = ExecutionState.NOT_STARTED;
- jobExecution.save();
-
- tuningJobExecution.jobExecution = jobExecution;
- tuningJobExecution.isDefaultExecution = bestParamSetTuningJobExecution.isDefaultExecution;
- tuningJobExecution.tuningAlgorithm = bestParamSetTuningJobExecution.tuningAlgorithm;
- tuningJobExecution.paramSetState = ParamSetStatus.CREATED;
- tuningJobExecution.save();
-
- logger.debug("Execution with default parameter created with execution id: " + tuningJobExecution.jobExecution.id);
-
- //Save default parameters corresponding to new default execution
- for (JobSuggestedParamValue jobSuggestedParamValue : jobSuggestedParamValueList) {
- JobSuggestedParamValue jobSuggestedParamValue1 = new JobSuggestedParamValue();
- jobSuggestedParamValue1.id = 0;
- jobSuggestedParamValue1.jobExecution = jobExecution;
- jobSuggestedParamValue1.paramValue = jobSuggestedParamValue.paramValue;
- jobSuggestedParamValue1.tuningParameter = jobSuggestedParamValue.tuningParameter;
- jobSuggestedParamValue1.save();
- }
-
- tuningJobExecution = TuningJobExecution.find.select("*")
- .where()
- .eq(TuningJobExecution.TABLE.jobExecution + "." + JobExecution.TABLE.id, tuningJobExecution.jobExecution.id)
- .setMaxRows(1)
- .findUnique();
-
- return tuningJobExecution;
+ return jobSuggestedParamValues;
}
/**
@@ -158,27 +114,39 @@ private void setMaxAllowedMetricIncreasePercentage(TuningInput tuningInput) {
* Sets the tuning algorithm based on the job type and optimization metric
* @param tuningInput TuningInput for which tuning algorithm is to be set
*/
- private void setTuningAlgorithm(TuningInput tuningInput) {
+ private void setTuningAlgorithm(TuningInput tuningInput) throws IllegalArgumentException {
//Todo: Handle algorithm version later
TuningAlgorithm tuningAlgorithm = TuningAlgorithm.find.select("*")
.where()
.eq(TuningAlgorithm.TABLE.jobType, tuningInput.getJobType())
.eq(TuningAlgorithm.TABLE.optimizationMetric, tuningInput.getOptimizationMetric())
.findUnique();
+ if (tuningAlgorithm == null) {
+ throw new IllegalArgumentException(
+ "Wrong job type or optimization metric. Job Type " + tuningInput.getJobType() + ". Optimization Metrics: "
+ + tuningInput.getOptimizationMetric());
+ }
tuningInput.setTuningAlgorithm(tuningAlgorithm);
}
/**
- * Applies penalty to the given execution
- * @param jobExecId String jobExecId of the execution to which penalty has to be applied
+ * Applies penalty to the param set corresponding to the given execution
+ * @param jobExecId String job execution id/url of the execution whose parameter set has to be penalized
+ * Assumption: Best param set will never be penalized
*/
private void applyPenalty(String jobExecId) {
Integer penaltyConstant = 3;
logger.info("Execution " + jobExecId + " failed/cancelled. Applying penalty");
- TuningJobExecution tuningJobExecution = TuningJobExecution.find.where()
- .eq(TuningJobExecution.TABLE.jobExecution + '.' + JobExecution.TABLE.jobExecId, jobExecId)
+
+ TuningJobExecutionParamSet tuningJobExecutionParamSet = TuningJobExecutionParamSet.find.where()
+ .eq(TuningJobExecutionParamSet.TABLE.jobExecution + '.' + JobExecution.TABLE.jobExecId, jobExecId)
+ .setMaxRows(1)
.findUnique();
- JobDefinition jobDefinition = tuningJobExecution.jobExecution.job;
+
+ JobSuggestedParamSet jobSuggestedParamSet = tuningJobExecutionParamSet.jobSuggestedParamSet;
+ JobExecution jobExecution = tuningJobExecutionParamSet.jobExecution;
+ JobDefinition jobDefinition = jobExecution.job;
+
TuningJobDefinition tuningJobDefinition = TuningJobDefinition.find.where()
.eq(TuningJobDefinition.TABLE.job + '.' + JobDefinition.TABLE.id, jobDefinition.id)
.findUnique();
@@ -186,11 +154,12 @@ private void applyPenalty(String jobExecId) {
tuningJobDefinition.averageResourceUsage * FileUtils.ONE_GB / tuningJobDefinition.averageInputSizeInBytes;
Double maxDesiredResourceUsagePerGBInput =
averageResourceUsagePerGBInput * tuningJobDefinition.allowedMaxResourceUsagePercent / 100.0;
- tuningJobExecution.fitness = penaltyConstant * maxDesiredResourceUsagePerGBInput;
- tuningJobExecution.paramSetState = ParamSetStatus.FITNESS_COMPUTED;
- tuningJobExecution.update();
- JobExecution jobExecution = tuningJobExecution.jobExecution;
+ jobSuggestedParamSet.fitness = penaltyConstant * maxDesiredResourceUsagePerGBInput;
+ jobSuggestedParamSet.paramSetState = ParamSetStatus.FITNESS_COMPUTED;
+ jobSuggestedParamSet.fitnessJobExecution = jobExecution;
+ jobSuggestedParamSet.update();
+
jobExecution.resourceUsage = 0D;
jobExecution.executionTime = 0D;
jobExecution.inputSizeInBytes = 1D;
@@ -198,128 +167,30 @@ private void applyPenalty(String jobExecId) {
}
/**
- * Handles the api request and returns param suggestions as response
- * @param tuningInput Rest api parameters
- * @return Parameter Suggestion
+ * Returns flow definition corresponding to the given tuning input if it exists, else creates one and returns it
+ * @param tuningInput TuningInput containing the flow definition id corresponding to which flow definition
+ * is to be returned
+ * @return FlowDefinition flow definition
*/
- public Map getCurrentRunParameters(TuningInput tuningInput) {
- logger.info("Parameter suggestion request for execution: " + tuningInput.getJobExecId());
- List jobSuggestedParamValues;
-
- if (tuningInput.getAllowedMaxExecutionTimePercent() == null
- || tuningInput.getAllowedMaxResourceUsagePercent() == null) {
- setMaxAllowedMetricIncreasePercentage(tuningInput);
- }
- setTuningAlgorithm(tuningInput);
- String jobDefId = tuningInput.getJobDefId();
-
- if (tuningInput.getRetry()) {
- applyPenalty(tuningInput.getJobExecId());
- TuningJobExecution bestParamSetTuningJobExecution = getBestParamSetTuningJobExecution(jobDefId);
- jobSuggestedParamValues = getJobExecutionParamSet(bestParamSetTuningJobExecution.jobExecution.id);
- } else {
- boolean isJobNewToTuning = false;
- boolean isTuningEnabledForJob;
-
- TuningJobDefinition tuningJobDefinition = TuningJobDefinition.find.select("*")
- .fetch(TuningJobDefinition.TABLE.job, "*")
- .where()
- .eq(TuningJobDefinition.TABLE.job + "." + JobDefinition.TABLE.jobDefId, jobDefId)
- .eq(TuningJobDefinition.TABLE.tuningEnabled, 1)
- .findUnique();
-
- isTuningEnabledForJob = tuningJobDefinition != null;
-
- if (!isTuningEnabledForJob) {
- //Tuning not enabled for the job currently. Either the job is new to tuning or tuning has been turned off for the job
- //TuningJobDefinition will have a unique entry for every time a job is turned on for tuning
- tuningJobDefinition = TuningJobDefinition.find.select("*")
- .fetch(TuningJobDefinition.TABLE.job, "*")
- .where()
- .eq(TuningJobDefinition.TABLE.job + "." + JobDefinition.TABLE.jobDefId, jobDefId)
- .setMaxRows(1)
- .orderBy(TuningJobDefinition.TABLE.createdTs + " desc")
- .findUnique();
-
- isJobNewToTuning = tuningJobDefinition == null;
-
- if (isJobNewToTuning) {
- //The job is new to tuning
- logger.debug("Registering job: " + tuningInput.getJobName() + " for auto tuning tuning");
- AutoTuningMetricsController.markNewAutoTuningJob();
- tuningJobDefinition = addNewJobForTuning(tuningInput);
- }
- }
-
- TuningJobExecution tuningJobExecution;
- if (isJobNewToTuning || isTuningEnabledForJob) {
- logger.debug("Finding parameter suggestion for job: " + tuningJobDefinition.job.jobName);
- tuningJobExecution = getNewTuningJobExecution(tuningJobDefinition);
- } else {
- //Tuning has been switched off for the job. Returning best param set
- tuningJobExecution = cloneBestParamSetTuningJobExecution(tuningJobDefinition);
- }
- updateJobExecutionParameter(tuningJobExecution, tuningInput);
- jobSuggestedParamValues = getJobExecutionParamSet(tuningJobExecution.jobExecution.id);
- }
- logger.debug("Number of output parameters : " + jobSuggestedParamValues.size());
- logger.info("Finishing getCurrentRunParameters");
- return jobSuggestedParamValueListToMap(jobSuggestedParamValues);
- }
-
- /**
- * Returns an execution with unsent parameters corresponding to the given job definition
- * @param tuningJobDefinition TuningJobDefinition corresponding to which execution is to be returned
- * @return TuningJobExecution corresponding to the given job definition
- */
- private TuningJobExecution getNewTuningJobExecution(TuningJobDefinition tuningJobDefinition) {
- TuningJobExecution tuningJobExecution = TuningJobExecution.find.select("*")
- .fetch(TuningJobExecution.TABLE.jobExecution, "*")
- .fetch(TuningJobExecution.TABLE.jobExecution + "." + JobExecution.TABLE.job, "*")
- .where()
- .eq(TuningJobExecution.TABLE.jobExecution + "." + JobExecution.TABLE.job + "." + JobDefinition.TABLE.id,
- tuningJobDefinition.job.id)
- .eq(TuningJobExecution.TABLE.paramSetState, ParamSetStatus.CREATED)
- .order()
- .asc(TuningJobExecution.TABLE.jobExecution + "." + JobExecution.TABLE.createdTs)
- .setMaxRows(1)
- .findUnique();
-
- //If no new parameter set for suggestion, create a new suggestion with best parameter set
- if (tuningJobExecution == null) {
- logger.info(
- "Returning best parameter set as no parameter suggestion found for job: " + tuningJobDefinition.job.jobName);
- AutoTuningMetricsController.markParamSetNotFound();
- tuningJobExecution = cloneBestParamSetTuningJobExecution(tuningJobDefinition);
- }
- return tuningJobExecution;
- }
-
- /**
- * Returns the list of JobSuggestedParamValue as Map of String to Double
- * @param jobSuggestedParamValues List of JobSuggestedParamValue
- * @return Map of string to double containing the parameter name and corresponding value
- */
- private Map jobSuggestedParamValueListToMap(List jobSuggestedParamValues) {
- Map paramValues = new HashMap();
- if (jobSuggestedParamValues != null) {
- for (JobSuggestedParamValue jobSuggestedParamValue : jobSuggestedParamValues) {
- logger.debug("Param Name is " + jobSuggestedParamValue.tuningParameter.paramName + " And value is "
- + jobSuggestedParamValue.paramValue);
- paramValues.put(jobSuggestedParamValue.tuningParameter.paramName, jobSuggestedParamValue.paramValue);
- }
+ private FlowDefinition getFlowDefinition(TuningInput tuningInput) {
+ FlowDefinition flowDefinition =
+ FlowDefinition.find.where().eq(FlowDefinition.TABLE.flowDefId, tuningInput.getFlowDefId()).findUnique();
+ if (flowDefinition == null) {
+ flowDefinition = new FlowDefinition();
+ flowDefinition.flowDefId = tuningInput.getFlowDefId();
+ flowDefinition.flowDefUrl = tuningInput.getFlowDefUrl();
+ flowDefinition.save();
}
- return paramValues;
+ return flowDefinition;
}
/**
- *This is to update job execution with IN_PROGRESS and parameter set with IN_PROGRESS. Also update flow_exec_id
- *, flowExecURL, JobExecID and jobExecURL
- * @param tuningJobExecution TuningJobExecution which is to be updated
- * @param tuningInput TuningInput corresponding to the TuningJobExecution
+ * Returns flow execution corresponding to the given tuning input if it exists, else creates one and returns it
+ * @param tuningInput TuningInput containing the flow execution id corresponding to which flow execution
+ * is to be returned
+ * @return FlowExecution flow execution
*/
- private void updateJobExecutionParameter(TuningJobExecution tuningJobExecution, TuningInput tuningInput) {
-
+ private FlowExecution getFlowExecution(TuningInput tuningInput) {
FlowExecution flowExecution =
FlowExecution.find.where().eq(FlowExecution.TABLE.flowExecId, tuningInput.getFlowExecId()).findUnique();
@@ -327,47 +198,23 @@ private void updateJobExecutionParameter(TuningJobExecution tuningJobExecution,
flowExecution = new FlowExecution();
flowExecution.flowExecId = tuningInput.getFlowExecId();
flowExecution.flowExecUrl = tuningInput.getFlowExecUrl();
- flowExecution.flowDefinition = tuningJobExecution.jobExecution.job.flowDefinition;
+ flowExecution.flowDefinition = getFlowDefinition(tuningInput);
flowExecution.save();
}
-
- JobExecution jobExecution = tuningJobExecution.jobExecution;
- jobExecution.jobExecId = tuningInput.getJobExecId();
- jobExecution.jobExecUrl = tuningInput.getJobExecUrl();
- jobExecution.executionState = ExecutionState.IN_PROGRESS;
- jobExecution.flowExecution = flowExecution;
-
- logger.debug("Saving job execution" + jobExecution.jobExecId);
-
- jobExecution.save();
-
- tuningJobExecution.jobExecution = jobExecution;
- tuningJobExecution.paramSetState = ParamSetStatus.SENT;
- tuningJobExecution.save();
+ return flowExecution;
}
/**
- * Add new job for tuning
+ * Adds new job for tuning
* @param tuningInput Tuning input parameters
* @return Job
*/
private TuningJobDefinition addNewJobForTuning(TuningInput tuningInput) {
-
logger.info("Adding new job for tuning, job id: " + tuningInput.getJobDefId());
-
+ FlowDefinition flowDefinition = getFlowDefinition(tuningInput);
JobDefinition job =
JobDefinition.find.select("*").where().eq(JobDefinition.TABLE.jobDefId, tuningInput.getJobDefId()).findUnique();
- FlowDefinition flowDefinition =
- FlowDefinition.find.where().eq(FlowDefinition.TABLE.flowDefId, tuningInput.getFlowDefId()).findUnique();
-
- if (flowDefinition == null) {
- flowDefinition = new FlowDefinition();
- flowDefinition.flowDefId = tuningInput.getFlowDefId();
- flowDefinition.flowDefUrl = tuningInput.getFlowDefUrl();
- flowDefinition.save();
- }
-
if (job == null) {
job = new JobDefinition();
job.jobDefId = tuningInput.getJobDefId();
@@ -379,99 +226,231 @@ private TuningJobDefinition addNewJobForTuning(TuningInput tuningInput) {
job.save();
}
- String flowExecId = tuningInput.getFlowExecId();
- String jobExecId = tuningInput.getJobExecId();
- String flowExecUrl = tuningInput.getFlowExecUrl();
- String jobExecUrl = tuningInput.getJobExecUrl();
String client = tuningInput.getClient();
- String defaultParams = tuningInput.getDefaultParams();
-
+ Map defaultParams = null;
+ try {
+ defaultParams = tuningInput.getDefaultParams();
+ } catch (IOException e) {
+ logger.error("Error in getting default parameters from request. ", e);
+ }
TuningJobDefinition tuningJobDefinition = new TuningJobDefinition();
tuningJobDefinition.job = job;
tuningJobDefinition.client = client;
tuningJobDefinition.tuningAlgorithm = tuningInput.getTuningAlgorithm();
- tuningJobDefinition.tuningEnabled = 1;
+ tuningJobDefinition.tuningEnabled = true;
tuningJobDefinition.allowedMaxExecutionTimePercent = tuningInput.getAllowedMaxExecutionTimePercent();
tuningJobDefinition.allowedMaxResourceUsagePercent = tuningInput.getAllowedMaxResourceUsagePercent();
tuningJobDefinition.save();
- TuningJobExecution tuningJobExecution =
- insertDefaultJobExecution(job, flowExecId, jobExecId, flowExecUrl, jobExecUrl, flowDefinition,
- tuningInput.getTuningAlgorithm());
- insertDefaultParameters(tuningJobExecution, defaultParams);
+ insertParamSet(job, tuningInput.getTuningAlgorithm(), defaultParams);
logger.info("Added job: " + tuningInput.getJobDefId() + " for tuning");
return tuningJobDefinition;
}
/**
- * Inserts default job execution in database
- * @param job Job
- * @param flowExecId Flow execution id
- * @param jobExecId Job execution id
- * @param flowExecUrl Flow execution url
- * @param jobExecUrl Job execution url
- * @return default job execution
+ * Returns the job definition corresponding to the given tuning input if it exists, else creates one and returns it
+ * @param tuningInput Tuning Input corresponding to which job definition is to be returned
+ * @return JobDefinition corresponding to the given tuning input
*/
- private TuningJobExecution insertDefaultJobExecution(JobDefinition job, String flowExecId, String jobExecId,
- String flowExecUrl, String jobExecUrl, FlowDefinition flowDefinition, TuningAlgorithm tuningAlgorithm) {
- logger.debug("Starting insertDefaultJobExecution");
+ private JobDefinition getJobDefinition(TuningInput tuningInput) {
- FlowExecution flowExecution =
- FlowExecution.find.where().eq(FlowExecution.TABLE.flowExecId, flowExecId).findUnique();
+ String jobDefId = tuningInput.getJobDefId();
- if (flowExecution == null) {
- flowExecution = new FlowExecution();
- flowExecution.flowExecId = flowExecId;
- flowExecution.flowExecUrl = flowExecUrl;
- flowExecution.flowDefinition = flowDefinition;
- flowExecution.save();
+ TuningJobDefinition tuningJobDefinition = TuningJobDefinition.find.select("*")
+ .fetch(TuningJobDefinition.TABLE.job, "*")
+ .where()
+ .eq(TuningJobDefinition.TABLE.job + "." + JobDefinition.TABLE.jobDefId, jobDefId)
+ .setMaxRows(1)
+ .orderBy(TuningJobDefinition.TABLE.createdTs + " desc")
+ .findUnique();
+
+ if (tuningJobDefinition == null) {
+ // Job new to tuning
+ logger.debug("Registering job: " + tuningInput.getJobName() + " for auto tuning tuning");
+ AutoTuningMetricsController.markNewAutoTuningJob();
+ tuningJobDefinition = addNewJobForTuning(tuningInput);
}
+ return tuningJobDefinition.job;
+ }
+
+ /**
+ * Creates a new job execution entry corresponding to the given tuning input
+ * @param tuningInput Input corresponding to which job execution is to be created
+ * @return JobExecution: the newly created job execution
+ */
+ private JobExecution addNewExecution(TuningInput tuningInput) {
+ JobDefinition jobDefinition = getJobDefinition(tuningInput);
+ FlowExecution flowExecution = getFlowExecution(tuningInput);
+
+ JobExecution jobExecution = new JobExecution();
+ jobExecution.jobExecId = tuningInput.getJobExecId();
+ jobExecution.jobExecUrl = tuningInput.getJobExecUrl();
+ jobExecution.job = jobDefinition;
+ jobExecution.executionState = ExecutionState.IN_PROGRESS;
+ jobExecution.flowExecution = flowExecution;
+ jobExecution.save();
+ return jobExecution;
+ }
- JobExecution jobExecution = JobExecution.find.where().eq(JobExecution.TABLE.jobExecId, jobExecId).findUnique();
+ /**
+ * Returns the job execution corresponding to the given tuning input if it exists, else creates one and returns it
+ * @param tuningInput Tuning Input corresponding to which job execution is to be returned
+ * @return JobExecution corresponding to the given tuning input
+ */
+ private JobExecution getJobExecution(TuningInput tuningInput) {
+
+ JobExecution jobExecution = JobExecution.find.select("*")
+ .fetch(JobExecution.TABLE.job, "*")
+ .where()
+ .eq(JobExecution.TABLE.jobExecId, tuningInput.getJobExecId())
+ .findUnique();
if (jobExecution == null) {
- jobExecution = new JobExecution();
- jobExecution.job = job;
- jobExecution.executionState = ExecutionState.NOT_STARTED;
- jobExecution.jobExecId = jobExecId;
- jobExecution.jobExecUrl = jobExecUrl;
- jobExecution.flowExecution = flowExecution;
- jobExecution.save();
+ jobExecution = addNewExecution(tuningInput);
+ }
+ return jobExecution;
+ }
+
+ /**
+ * Handles the api request and returns param suggestions as response
+ * @param tuningInput Rest api parameters
+ * @return Parameter Suggestion
+ */
+ public Map getCurrentRunParameters(TuningInput tuningInput) throws Exception {
+ logger.info("Parameter set request received from execution: " + tuningInput.getJobExecId());
+
+ if (tuningInput.getAllowedMaxExecutionTimePercent() == null
+ || tuningInput.getAllowedMaxResourceUsagePercent() == null) {
+ setMaxAllowedMetricIncreasePercentage(tuningInput);
}
+ setTuningAlgorithm(tuningInput);
- TuningJobExecution tuningJobExecution = new TuningJobExecution();
- tuningJobExecution.jobExecution = jobExecution;
- tuningJobExecution.tuningAlgorithm = tuningAlgorithm;
- tuningJobExecution.paramSetState = ParamSetStatus.CREATED;
- tuningJobExecution.isDefaultExecution = true;
- tuningJobExecution.save();
+ JobSuggestedParamSet jobSuggestedParamSet;
+ JobExecution jobExecution = getJobExecution(tuningInput);
- logger.debug("Finishing insertDefaultJobExecution. Job Execution ID " + jobExecution.jobExecId);
+ if (tuningInput.getRetry()) {
+ applyPenalty(tuningInput.getJobExecId());
+ jobSuggestedParamSet = getBestParamSet(tuningInput.getJobDefId());
+ } else {
+ logger.debug("Finding parameter suggestion for job: " + jobExecution.job.jobName);
+ jobSuggestedParamSet = getNewSuggestedParamSet(jobExecution.job);
+ markParameterSetSent(jobSuggestedParamSet);
+ }
+
+ addNewTuningJobExecutionParamSet(jobSuggestedParamSet, jobExecution);
- return tuningJobExecution;
+ List jobSuggestedParamValues = getParamSetValues(jobSuggestedParamSet.id);
+ logger.debug("Number of output parameters for execution " + tuningInput.getJobExecId() + " = "
+ + jobSuggestedParamValues.size());
+ logger.info("Finishing getCurrentRunParameters");
+ return jobSuggestedParamValueListToMap(jobSuggestedParamValues);
}
/**
- * Inserts default execution parameters in database
- * @param tuningJobExecution Tuning Job Execution
- * @param defaultParams Default parameters map as string
+ * Adds a new entry to the "tuning_job_execution_param_set" for the given param set and job execution
+ * @param jobSuggestedParamSet JobSuggestedParamSet: param set
+ * @param jobExecution JobExecution
*/
- @SuppressWarnings("unchecked")
- private void insertDefaultParameters(TuningJobExecution tuningJobExecution, String defaultParams) {
- JobExecution jobExecution = tuningJobExecution.jobExecution;
- TuningAlgorithm.JobType jobType = tuningJobExecution.tuningAlgorithm.jobType;
+ private void addNewTuningJobExecutionParamSet(JobSuggestedParamSet jobSuggestedParamSet, JobExecution jobExecution) {
+ TuningJobExecutionParamSet tuningJobExecutionParamSet = new TuningJobExecutionParamSet();
+ tuningJobExecutionParamSet.jobSuggestedParamSet = jobSuggestedParamSet;
+ tuningJobExecutionParamSet.jobExecution = jobExecution;
- ObjectMapper mapper = new ObjectMapper();
- Map paramValueMap = null;
- try {
- paramValueMap = (Map) mapper.readValue(defaultParams, Map.class);
- } catch (Exception e) {
- logger.error(e);
+ TuningJobDefinition tuningJobDefinition = TuningJobDefinition.find.where()
+ .eq(TuningJobDefinition.TABLE.job + '.' + JobDefinition.TABLE.id, jobExecution.job.id)
+ .order()
+ .desc(TuningJobDefinition.TABLE.createdTs)
+ .setMaxRows(1)
+ .findUnique();
+ tuningJobExecutionParamSet.tuningEnabled = tuningJobDefinition.tuningEnabled;
+ tuningJobExecutionParamSet.save();
+ }
+
+ /**
+ * Returns a parameter set in "CREATED" state corresponding to the given job definition if it exists, else returns
+ * the best parameter set
+ * @param jobDefinition jobDefinition for which param set is to be returned
+ * @return JobSuggestedParamSet corresponding to the given job definition
+ */
+ private JobSuggestedParamSet getNewSuggestedParamSet(JobDefinition jobDefinition) {
+ JobSuggestedParamSet jobSuggestedParamSet = JobSuggestedParamSet.find.select("*")
+ .fetch(JobSuggestedParamSet.TABLE.jobDefinition, "*")
+ .where()
+ .eq(JobSuggestedParamSet.TABLE.jobDefinition + "." + JobDefinition.TABLE.id, jobDefinition.id)
+ .eq(JobSuggestedParamSet.TABLE.paramSetState, ParamSetStatus.CREATED)
+ .order()
+ .asc(JobSuggestedParamSet.TABLE.id)
+ .setMaxRows(1)
+ .findUnique();
+
+ if (jobSuggestedParamSet == null) {
+ //No new parameter set exists, returning the best parameter set
+ logger.info("Returning best parameter set as no parameter suggestion found for job: " + jobDefinition.jobName);
+ AutoTuningMetricsController.markParamSetNotFound();
+ jobSuggestedParamSet = getBestParamSet(jobDefinition.jobDefId);
}
+ return jobSuggestedParamSet;
+ }
+
+ /**
+ * Returns the list of JobSuggestedParamValue as Map of String to Double
+ * @param jobSuggestedParamValues List of JobSuggestedParamValue
+ * @return Map of string to double containing the parameter name and corresponding value
+ */
+ private Map jobSuggestedParamValueListToMap(List jobSuggestedParamValues) {
+ Map paramValues = new HashMap();
+ if (jobSuggestedParamValues != null) {
+ for (JobSuggestedParamValue jobSuggestedParamValue : jobSuggestedParamValues) {
+ logger.debug("Param Name is " + jobSuggestedParamValue.tuningParameter.paramName + " And value is "
+ + jobSuggestedParamValue.paramValue);
+ paramValues.put(jobSuggestedParamValue.tuningParameter.paramName, jobSuggestedParamValue.paramValue);
+ }
+ }
+ return paramValues;
+ }
+
+ /**
+ *Updates parameter set state to SENT if it is in CREATED state
+ * @param jobSuggestedParamSet JobSuggestedParamSet which is to be updated
+ */
+ private void markParameterSetSent(JobSuggestedParamSet jobSuggestedParamSet) {
+ if (jobSuggestedParamSet.paramSetState.equals(ParamSetStatus.CREATED)) {
+ logger.info("Marking paramSetID: " + jobSuggestedParamSet.id + " SENT");
+ jobSuggestedParamSet.paramSetState = ParamSetStatus.SENT;
+ jobSuggestedParamSet.save();
+ }
+ }
+
+ /**
+ * Inserts a parameter set in database
+ * @param job Job
+ */
+ private void insertParamSet(JobDefinition job, TuningAlgorithm tuningAlgorithm, Map paramValueMap) {
+ logger.debug("Inserting default parameter set for job: " + job.jobName);
+ JobSuggestedParamSet jobSuggestedParamSet = new JobSuggestedParamSet();
+ jobSuggestedParamSet.jobDefinition = job;
+ jobSuggestedParamSet.tuningAlgorithm = tuningAlgorithm;
+ jobSuggestedParamSet.paramSetState = ParamSetStatus.CREATED;
+ jobSuggestedParamSet.isParamSetDefault = true;
+ jobSuggestedParamSet.areConstraintsViolated = false;
+ jobSuggestedParamSet.isParamSetBest = false;
+ jobSuggestedParamSet.save();
+ insertParameterValues(jobSuggestedParamSet, paramValueMap);
+ logger.debug("Default parameter set inserted for job: " + job.jobName);
+ }
+
+ /**
+ * Inserts parameter values in database
+ * @param jobSuggestedParamSet Set of the parameters which is to be inserted
+ * @param paramValueMap Map of parameter values as string
+ */
+ @SuppressWarnings("unchecked")
+ private void insertParameterValues(JobSuggestedParamSet jobSuggestedParamSet, Map paramValueMap) {
+ ObjectMapper mapper = new ObjectMapper();
if (paramValueMap != null) {
for (Map.Entry paramValue : paramValueMap.entrySet()) {
- insertExecutionParameter(jobExecution, paramValue.getKey(), paramValue.getValue());
+ insertParameterValue(jobSuggestedParamSet, paramValue.getKey(), paramValue.getValue());
}
} else {
logger.warn("ParamValueMap is null ");
@@ -479,24 +458,21 @@ private void insertDefaultParameters(TuningJobExecution tuningJobExecution, Stri
}
/**
- * Inserts parameter of an execution in database
- * @param jobExecution Job execution
+ * Inserts parameter value in database
+ * @param jobSuggestedParamSet Parameter set to which the parameter belongs
* @param paramName Parameter name
* @param paramValue Parameter value
*/
- private void insertExecutionParameter(JobExecution jobExecution, String paramName, Double paramValue) {
- logger.debug("Starting insertExecutionParameter");
+ private void insertParameterValue(JobSuggestedParamSet jobSuggestedParamSet, String paramName, Double paramValue) {
+ logger.debug("Starting insertParameterValue");
JobSuggestedParamValue jobSuggestedParamValue = new JobSuggestedParamValue();
- jobSuggestedParamValue.jobExecution = jobExecution;
+ jobSuggestedParamValue.jobSuggestedParamSet = jobSuggestedParamSet;
TuningParameter tuningParameter =
TuningParameter.find.where().eq(TuningParameter.TABLE.paramName, paramName).findUnique();
if (tuningParameter != null) {
jobSuggestedParamValue.tuningParameter = tuningParameter;
jobSuggestedParamValue.paramValue = paramValue;
jobSuggestedParamValue.save();
- logger.debug(
- "Finishing insertDefaultJobExecution. Job Execution ID. Param ID " + jobSuggestedParamValue.tuningParameter.id
- + " Param Name: " + jobSuggestedParamValue.tuningParameter.paramName);
} else {
logger.warn("TuningAlgorithm param null " + paramName);
}
diff --git a/app/com/linkedin/drelephant/tuning/AzkabanJobCompleteDetector.java b/app/com/linkedin/drelephant/tuning/AzkabanJobCompleteDetector.java
index 9e527791f..4bd0113cc 100644
--- a/app/com/linkedin/drelephant/tuning/AzkabanJobCompleteDetector.java
+++ b/app/com/linkedin/drelephant/tuning/AzkabanJobCompleteDetector.java
@@ -24,8 +24,9 @@
import java.util.Map;
import models.JobExecution;
import models.JobExecution.ExecutionState;
-import models.TuningJobExecution;
-import models.TuningJobExecution.ParamSetStatus;
+import models.JobSuggestedParamSet;
+import models.JobSuggestedParamSet.ParamSetStatus;
+import models.TuningJobExecutionParamSet;
import org.apache.log4j.Logger;
@@ -39,26 +40,27 @@ public class AzkabanJobCompleteDetector extends JobCompleteDetector {
private AzkabanJobStatusUtil _azkabanJobStatusUtil;
public enum AzkabanJobStatus {
- FAILED, CANCELLED, KILLED, SUCCEEDED
+ FAILED, CANCELLED, KILLED, SUCCEEDED, SKIPPED
}
/**
* Returns the list of completed executions
- * @param jobExecutions Started Execution list
+ * @param inProgressExecutionParamSet List of executions (with corresponding param set) in progress
* @return List of completed executions
- * @throws MalformedURLException
- * @throws URISyntaxException
+ * @throws MalformedURLException MalformedURLException
+ * @throws URISyntaxException URISyntaxException
*/
- protected List getCompletedExecutions(List jobExecutions)
+ protected List getCompletedExecutions(List inProgressExecutionParamSet)
throws MalformedURLException, URISyntaxException {
logger.info("Fetching the list of executions completed since last iteration");
- List completedExecutions = new ArrayList();
+ List completedExecutions = new ArrayList();
try {
- for (TuningJobExecution tuningJobExecution : jobExecutions) {
+ for (TuningJobExecutionParamSet tuningJobExecutionParamSet : inProgressExecutionParamSet) {
- JobExecution jobExecution = tuningJobExecution.jobExecution;
+ JobSuggestedParamSet jobSuggestedParamSet = tuningJobExecutionParamSet.jobSuggestedParamSet;
+ JobExecution jobExecution = tuningJobExecutionParamSet.jobExecution;
- logger.info("Checking current status of started execution: " + tuningJobExecution.jobExecution.jobExecId);
+ logger.info("Checking current status of started execution: " + jobExecution.jobExecId);
if (_azkabanJobStatusUtil == null) {
logger.info("Initializing AzkabanJobStatusUtil");
@@ -72,23 +74,32 @@ protected List getCompletedExecutions(List tuningJobDefinitions) {
*/
private List getJobForBaselineComputation() {
logger.info("Fetching jobs for which baseline metrics need to be computed");
- List tuningJobDefinitions = new ArrayList();
- try {
- tuningJobDefinitions =
- TuningJobDefinition.find.where().eq(TuningJobDefinition.TABLE.averageResourceUsage, null).findList();
- } catch (NullPointerException e) {
- logger.info("There are no jobs for which baseline has to be computed", e);
- }
+ List tuningJobDefinitions =
+ TuningJobDefinition.find.where().eq(TuningJobDefinition.TABLE.averageResourceUsage, null).findList();
return tuningJobDefinitions;
}
diff --git a/app/com/linkedin/drelephant/tuning/FitnessComputeUtil.java b/app/com/linkedin/drelephant/tuning/FitnessComputeUtil.java
index 658ecbbee..bf6f64d66 100644
--- a/app/com/linkedin/drelephant/tuning/FitnessComputeUtil.java
+++ b/app/com/linkedin/drelephant/tuning/FitnessComputeUtil.java
@@ -34,11 +34,12 @@
import models.AppResult;
import models.JobDefinition;
import models.JobExecution;
+import models.JobSuggestedParamSet;
+import models.JobSuggestedParamSet.ParamSetStatus;
import models.JobSuggestedParamValue;
import models.TuningAlgorithm;
import models.TuningJobDefinition;
-import models.TuningJobExecution;
-import models.TuningJobExecution.ParamSetStatus;
+import models.TuningJobExecutionParamSet;
import models.TuningParameter;
import org.apache.commons.io.FileUtils;
import org.apache.hadoop.conf.Configuration;
@@ -48,20 +49,53 @@
/**
* This class computes the fitness of the suggested parameters after the execution is complete. This uses
* Dr Elephant's DB to compute the fitness.
- * Fitness is : Resource Usage/Input Size in GB
+ * Fitness is : Resource Usage/(Input Size in GB)
* In case there is failure or resource usage/execution time goes beyond configured limit, fitness is computed by
* adding a penalty.
*/
public class FitnessComputeUtil {
private static final Logger logger = Logger.getLogger(FitnessComputeUtil.class);
private static final String FITNESS_COMPUTE_WAIT_INTERVAL = "fitness.compute.wait_interval.ms";
- private static final int MAX_TUNING_EXECUTIONS = 39;
- private static final int MIN_TUNING_EXECUTIONS = 18;
- private Long waitInterval;
+ private static final String IGNORE_EXECUTION_WAIT_INTERVAL = "ignore.execution.wait.interval.ms";
+ private static final String MAX_TUNING_EXECUTIONS = "max.tuning.executions";
+ private static final String MIN_TUNING_EXECUTIONS = "min.tuning.executions";
+ private int maxTuningExecutions;
+ private int minTuningExecutions;
+ private Long fitnessComputeWaitInterval;
+ private Long ignoreExecutionWaitInterval;
public FitnessComputeUtil() {
Configuration configuration = ElephantContext.instance().getAutoTuningConf();
- waitInterval = Utils.getNonNegativeLong(configuration, FITNESS_COMPUTE_WAIT_INTERVAL, 5 * AutoTuner.ONE_MIN);
+
+ // Time duration to wait for computing the fitness of a param set once the corresponding execution is completed
+ fitnessComputeWaitInterval =
+ Utils.getNonNegativeLong(configuration, FITNESS_COMPUTE_WAIT_INTERVAL, 5 * AutoTuner.ONE_MIN);
+
+ // Time duration to wait for metrics (resource usage, execution time) of an execution to be computed before
+ // discarding it for fitness computation
+ ignoreExecutionWaitInterval =
+ Utils.getNonNegativeLong(configuration, IGNORE_EXECUTION_WAIT_INTERVAL, 2 * 60 * AutoTuner.ONE_MIN);
+
+ // #executions after which tuning will stop even if parameters don't converge
+ maxTuningExecutions =
+ Utils.getNonNegativeInt(configuration, MAX_TUNING_EXECUTIONS, 39);
+
+ // #executions before which tuning cannot stop even if parameters converge
+ minTuningExecutions =
+ Utils.getNonNegativeInt(configuration, MIN_TUNING_EXECUTIONS, 18);
+ }
+
+ private boolean isTuningEnabled(Integer jobDefinitionId) {
+ TuningJobDefinition tuningJobDefinition = TuningJobDefinition.find.where()
+ .eq(TuningJobDefinition.TABLE.job + '.' + JobDefinition.TABLE.id, jobDefinitionId)
+ .order()
+ // There can be multiple entries in tuningJobDefinition if the job is switch on/off multiple times.
+ // The latest entry gives the information regarding whether tuning is enabled or not
+ .desc(TuningJobDefinition.TABLE.createdTs)
+ .setMaxRows(1)
+ .findUnique();
+
+ return tuningJobDefinition != null && tuningJobDefinition.tuningEnabled;
}
/**
@@ -70,59 +104,69 @@ public FitnessComputeUtil() {
*/
public void updateFitness() {
logger.info("Computing and updating fitness for completed executions");
- List completedExecutions = getCompletedExecutions();
- updateExecutionMetrics(completedExecutions);
- updateMetrics(completedExecutions);
+ List completedJobExecutionParamSets = getCompletedJobExecutionParamSets();
+ updateExecutionMetrics(completedJobExecutionParamSets);
+ updateMetrics(completedJobExecutionParamSets);
Set jobDefinitionSet = new HashSet();
- for (TuningJobExecution tuningJobExecution : completedExecutions) {
- jobDefinitionSet.add(tuningJobExecution.jobExecution.job);
+ for (TuningJobExecutionParamSet completedJobExecutionParamSet : completedJobExecutionParamSets) {
+ JobDefinition jobDefinition = completedJobExecutionParamSet.jobSuggestedParamSet.jobDefinition;
+ if (isTuningEnabled(jobDefinition.id)) {
+ jobDefinitionSet.add(jobDefinition);
+ }
}
checkToDisableTuning(jobDefinitionSet);
}
/**
* Checks if the tuning parameters converge
- * @param jobExecutions List of previous executions on which parameter convergence is to be checked
+ * @param tuningJobExecutionParamSets List of previous executions and corresponding param sets
* @return true if the parameters converge, else false
*/
- private boolean doesParameterSetConverge(List jobExecutions) {
+ private boolean didParameterSetConverge(List tuningJobExecutionParamSets) {
boolean result = false;
- int num_param_set_for_convergence = 3;
+ int numParamSetForConvergence = 3;
- TuningJobExecution tuningJobExecution = TuningJobExecution.find.where()
- .eq(TuningJobExecution.TABLE.jobExecution + '.' + JobExecution.TABLE.id, jobExecutions.get(0).id)
- .findUnique();
- TuningAlgorithm.JobType jobType = tuningJobExecution.tuningAlgorithm.jobType;
+ if (tuningJobExecutionParamSets.size() < numParamSetForConvergence) {
+ return false;
+ }
+
+ TuningAlgorithm.JobType jobType = tuningJobExecutionParamSets.get(0).jobSuggestedParamSet.tuningAlgorithm.jobType;
if (jobType == TuningAlgorithm.JobType.PIG) {
+
Map> paramValueSet = new HashMap>();
- for (JobExecution jobExecution : jobExecutions) {
- List jobSuggestedParamValueList = new ArrayList();
- try {
- jobSuggestedParamValueList = JobSuggestedParamValue.find.where()
- .eq(JobSuggestedParamValue.TABLE.jobExecution + '.' + JobExecution.TABLE.id, jobExecution.id)
- .or(Expr.eq(JobSuggestedParamValue.TABLE.tuningParameter + '.' + TuningParameter.TABLE.id, 2),
- Expr.eq(JobSuggestedParamValue.TABLE.tuningParameter + '.' + TuningParameter.TABLE.id, 5))
- .findList();
- } catch (NullPointerException e) {
- logger.info("Checking param convergence: Map memory and reduce memory parameter not found");
- }
- if (jobSuggestedParamValueList.size() > 0) {
- num_param_set_for_convergence -= 1;
+
+ for (TuningJobExecutionParamSet tuningJobExecutionParamSet : tuningJobExecutionParamSets) {
+
+ JobSuggestedParamSet jobSuggestedParamSet = tuningJobExecutionParamSet.jobSuggestedParamSet;
+
+ List jobSuggestedParamValueList = JobSuggestedParamValue.find.where()
+ .eq(JobSuggestedParamValue.TABLE.jobSuggestedParamSet + '.' + JobSuggestedParamSet.TABLE.id,
+ jobSuggestedParamSet.id)
+ .or(Expr.eq(JobSuggestedParamValue.TABLE.tuningParameter + '.' + TuningParameter.TABLE.paramName,
+ "mapreduce.map.memory.mb"),
+ Expr.eq(JobSuggestedParamValue.TABLE.tuningParameter + '.' + TuningParameter.TABLE.paramName,
+ "mapreduce.reduce.memory.mb"))
+ .findList();
+
+ // if jobSuggestedParamValueList contains both mapreduce.map.memory.mb and mapreduce.reduce.memory.mb
+ // ie, if the size of jobSuggestedParamValueList is 2
+ if (jobSuggestedParamValueList != null && jobSuggestedParamValueList.size() == 2) {
+ numParamSetForConvergence -= 1;
for (JobSuggestedParamValue jobSuggestedParamValue : jobSuggestedParamValueList) {
- Set tmp;
+ Set tmp;
if (paramValueSet.containsKey(jobSuggestedParamValue.id)) {
tmp = paramValueSet.get(jobSuggestedParamValue.id);
} else {
- tmp = new HashSet();
+ tmp = new HashSet();
}
tmp.add(jobSuggestedParamValue.paramValue);
paramValueSet.put(jobSuggestedParamValue.id, tmp);
}
}
- if (num_param_set_for_convergence == 0) {
+ if (numParamSetForConvergence == 0) {
break;
}
}
@@ -136,8 +180,8 @@ private boolean doesParameterSetConverge(List jobExecutions) {
}
if (result) {
- logger.info(
- "Switching off tuning for job: " + jobExecutions.get(0).job.jobName + " Reason: parameter set converged");
+ logger.info("Switching off tuning for job: " + tuningJobExecutionParamSets.get(
+ 0).jobSuggestedParamSet.jobDefinition.jobName + " Reason: parameter set converged");
}
return result;
}
@@ -147,22 +191,25 @@ private boolean doesParameterSetConverge(List jobExecutions) {
* Last 6 executions constitutes 2 iterations of PSO (given the swarm size is three). Negative average gains in
* latest 2 algorithm iterations (after a fixed number of minimum iterations) imply that either the algorithm hasn't
* converged or there isn't enough scope for tuning. In both the cases, switching tuning off is desired
- * @param jobExecutions List of previous executions
+ * @param tuningJobExecutionParamSets List of previous executions
* @return true if the median gain is negative, else false
*/
- private boolean isMedianGainNegative(List jobExecutions) {
- int num_fitness_for_median = 6;
- Double[] fitnessArray = new Double[num_fitness_for_median];
+ private boolean isMedianGainNegative(List tuningJobExecutionParamSets) {
+ int numFitnessForMedian = 6;
+ Double[] fitnessArray = new Double[numFitnessForMedian];
int entries = 0;
- for (JobExecution jobExecution : jobExecutions) {
- TuningJobExecution tuningJobExecution = TuningJobExecution.find.where()
- .eq(TuningJobExecution.TABLE.jobExecution + '.' + JobExecution.TABLE.id, jobExecution.id)
- .findUnique();
+
+ if (tuningJobExecutionParamSets.size() < numFitnessForMedian) {
+ return false;
+ }
+ for (TuningJobExecutionParamSet tuningJobExecutionParamSet : tuningJobExecutionParamSets) {
+ JobSuggestedParamSet jobSuggestedParamSet = tuningJobExecutionParamSet.jobSuggestedParamSet;
+ JobExecution jobExecution = tuningJobExecutionParamSet.jobExecution;
if (jobExecution.executionState == JobExecution.ExecutionState.SUCCEEDED
- && tuningJobExecution.paramSetState == ParamSetStatus.FITNESS_COMPUTED) {
- fitnessArray[entries] = tuningJobExecution.fitness;
+ && jobSuggestedParamSet.paramSetState == ParamSetStatus.FITNESS_COMPUTED) {
+ fitnessArray[entries] = jobSuggestedParamSet.fitness;
entries += 1;
- if (entries == num_fitness_for_median) {
+ if (entries == numFitnessForMedian) {
break;
}
}
@@ -175,15 +222,14 @@ private boolean isMedianGainNegative(List jobExecutions) {
medianFitness = fitnessArray[fitnessArray.length / 2];
}
- JobDefinition jobDefinition = jobExecutions.get(0).job;
+ JobDefinition jobDefinition = tuningJobExecutionParamSets.get(0).jobSuggestedParamSet.jobDefinition;
TuningJobDefinition tuningJobDefinition = TuningJobDefinition.find.where().
eq(TuningJobDefinition.TABLE.job + '.' + JobDefinition.TABLE.id, jobDefinition.id).findUnique();
double baselineFitness =
tuningJobDefinition.averageResourceUsage * FileUtils.ONE_GB / tuningJobDefinition.averageInputSizeInBytes;
if (medianFitness > baselineFitness) {
- logger.info(
- "Switching off tuning for job: " + jobExecutions.get(0).job.jobName + " Reason: unable to tune enough");
+ logger.info("Switching off tuning for job: " + jobDefinition.jobName + " Reason: unable to tune enough");
return true;
} else {
return false;
@@ -198,9 +244,8 @@ private void disableTuning(JobDefinition jobDefinition, String reason) {
TuningJobDefinition tuningJobDefinition = TuningJobDefinition.find.where()
.eq(TuningJobDefinition.TABLE.job + '.' + JobDefinition.TABLE.id, jobDefinition.id)
.findUnique();
- if (tuningJobDefinition.tuningEnabled == 1) {
- logger.info("Disabling tuning for job: " + tuningJobDefinition.job.jobDefId);
- tuningJobDefinition.tuningEnabled = 0;
+ if (tuningJobDefinition.tuningEnabled) {
+ tuningJobDefinition.tuningEnabled = false;
tuningJobDefinition.tuningDisabledReason = reason;
tuningJobDefinition.save();
}
@@ -209,45 +254,46 @@ private void disableTuning(JobDefinition jobDefinition, String reason) {
/**
* Checks and disables tuning for the given job definitions.
* Tuning can be disabled if:
- * - Number of tuning executions >= MAX_TUNING_EXECUTIONS
- * - or number of tuning executions >= MIN_TUNING_EXECUTIONS and parameters converge
- * - or number of tuning executions >= MIN_TUNING_EXECUTIONS and median gain (in cost function) in last 6 executions is negative
+ * - Number of tuning executions >= maxTuningExecutions
+ * - or number of tuning executions >= minTuningExecutions and parameters converge
+ * - or number of tuning executions >= minTuningExecutions and median gain (in cost function) in last 6 executions is negative
* @param jobDefinitionSet Set of jobs to check if tuning can be switched off for them
*/
private void checkToDisableTuning(Set jobDefinitionSet) {
for (JobDefinition jobDefinition : jobDefinitionSet) {
- try {
- List jobExecutions = JobExecution.find.where()
- .eq(JobExecution.TABLE.job + '.' + JobDefinition.TABLE.id, jobDefinition.id)
- .isNotNull(JobExecution.TABLE.jobExecId)
- .orderBy("id desc")
- .findList();
- if (jobExecutions.size() >= MIN_TUNING_EXECUTIONS) {
- if (doesParameterSetConverge(jobExecutions)) {
+ List tuningJobExecutionParamSets =
+ TuningJobExecutionParamSet.find.fetch(TuningJobExecutionParamSet.TABLE.jobSuggestedParamSet, "*")
+ .fetch(TuningJobExecutionParamSet.TABLE.jobExecution, "*")
+ .where()
+ .eq(TuningJobExecutionParamSet.TABLE.jobSuggestedParamSet + '.'
+ + JobSuggestedParamSet.TABLE.jobDefinition + '.' + JobDefinition.TABLE.id, jobDefinition.id)
+ .order()
+ .desc("job_execution_id")
+ .findList();
+
+ if (tuningJobExecutionParamSets.size() >= minTuningExecutions) {
+ if (didParameterSetConverge(tuningJobExecutionParamSets)) {
logger.info("Parameters converged. Disabling tuning for job: " + jobDefinition.jobName);
disableTuning(jobDefinition, "Parameters converged");
- } else if (isMedianGainNegative(jobExecutions)) {
+ } else if (isMedianGainNegative(tuningJobExecutionParamSets)) {
logger.info("Unable to get gain while tuning. Disabling tuning for job: " + jobDefinition.jobName);
disableTuning(jobDefinition, "Unable to get gain");
- } else if (jobExecutions.size() >= MAX_TUNING_EXECUTIONS) {
+ } else if (tuningJobExecutionParamSets.size() >= maxTuningExecutions) {
logger.info("Maximum tuning executions limit reached. Disabling tuning for job: " + jobDefinition.jobName);
disableTuning(jobDefinition, "Maximum executions reached");
}
}
- } catch (NullPointerException e) {
- logger.info("No execution found for job: " + jobDefinition.jobName);
- }
}
}
/**
* This method update metrics for auto tuning monitoring for fitness compute daemon
- * @param completedExecutions List of completed tuning job executions
+ * @param completedJobExecutionParamSets List of completed tuning job executions
*/
- private void updateMetrics(List completedExecutions) {
+ private void updateMetrics(List completedJobExecutionParamSets) {
int fitnessNotUpdated = 0;
- for (TuningJobExecution tuningJobExecution : completedExecutions) {
- if (!tuningJobExecution.paramSetState.equals(ParamSetStatus.FITNESS_COMPUTED)) {
+ for (TuningJobExecutionParamSet completedJobExecutionParamSet : completedJobExecutionParamSets) {
+ if (!completedJobExecutionParamSet.jobSuggestedParamSet.paramSetState.equals(ParamSetStatus.FITNESS_COMPUTED)) {
fitnessNotUpdated++;
} else {
AutoTuningMetricsController.markFitnessComputedJobs();
@@ -260,57 +306,61 @@ private void updateMetrics(List completedExecutions) {
* Returns the list of completed executions whose metrics are not computed
* @return List of job execution
*/
- private List getCompletedExecutions() {
+ private List getCompletedJobExecutionParamSets() {
logger.info("Fetching completed executions whose fitness are yet to be computed");
- List jobExecutions = new ArrayList();
- List outputJobExecutions = new ArrayList();
+ List completedJobExecutionParamSet = new ArrayList();
- try {
- jobExecutions = TuningJobExecution.find.select("*")
+ List tuningJobExecutionParamSets = TuningJobExecutionParamSet.find.select("*")
+ .fetch(TuningJobExecutionParamSet.TABLE.jobExecution, "*")
+ .fetch(TuningJobExecutionParamSet.TABLE.jobSuggestedParamSet, "*")
.where()
- .eq(TuningJobExecution.TABLE.paramSetState, ParamSetStatus.EXECUTED)
+ .or(Expr.or(Expr.eq(TuningJobExecutionParamSet.TABLE.jobExecution + '.' + JobExecution.TABLE.executionState,
+ JobExecution.ExecutionState.SUCCEEDED),
+ Expr.eq(TuningJobExecutionParamSet.TABLE.jobExecution + '.' + JobExecution.TABLE.executionState,
+ JobExecution.ExecutionState.FAILED)),
+ Expr.eq(TuningJobExecutionParamSet.TABLE.jobExecution + '.' + JobExecution.TABLE.executionState,
+ JobExecution.ExecutionState.CANCELLED))
+ .isNull(TuningJobExecutionParamSet.TABLE.jobExecution + '.' + JobExecution.TABLE.resourceUsage)
.findList();
- for (TuningJobExecution tuningJobExecution : jobExecutions) {
- long diff = System.currentTimeMillis() - tuningJobExecution.jobExecution.updatedTs.getTime();
- logger.debug("Current Time in millis: " + System.currentTimeMillis() + ", Job execution last updated time "
- + tuningJobExecution.jobExecution.updatedTs.getTime());
- if (diff < waitInterval) {
- logger.debug("Delaying fitness compute for execution: " + tuningJobExecution.jobExecution.jobExecId);
+ logger.info("#completed executions whose metrics are not computed: " + tuningJobExecutionParamSets.size());
+
+ for (TuningJobExecutionParamSet tuningJobExecutionParamSet : tuningJobExecutionParamSets) {
+ JobExecution jobExecution = tuningJobExecutionParamSet.jobExecution;
+ long diff = System.currentTimeMillis() - jobExecution.updatedTs.getTime();
+ logger.info("Current Time in millis: " + System.currentTimeMillis() + ", Job execution last updated time "
+ + jobExecution.updatedTs.getTime());
+ if (diff < fitnessComputeWaitInterval) {
+ logger.info("Delaying fitness compute for execution: " + jobExecution.jobExecId);
} else {
- logger.debug("Adding execution " + tuningJobExecution.jobExecution.jobExecId + " for fitness computation");
- outputJobExecutions.add(tuningJobExecution);
+ logger.info("Adding execution " + jobExecution.jobExecId + " to fitness computation queue");
+ completedJobExecutionParamSet.add(tuningJobExecutionParamSet);
}
}
- } catch (NullPointerException e) {
- logger.error("No completed execution found for which fitness is to be computed", e);
- }
- logger.info("Number of completed execution fetched for fitness computation: " + outputJobExecutions.size());
- logger.debug("Finished fetching completed executions for fitness computation");
- return outputJobExecutions;
+ logger.info(
+ "Number of completed execution fetched for fitness computation: " + completedJobExecutionParamSet.size());
+ return completedJobExecutionParamSet;
}
/**
* Updates the execution metrics
- * @param completedExecutions List of completed executions
+ * @param completedJobExecutionParamSets List of completed executions
*/
- private void updateExecutionMetrics(List completedExecutions) {
+ private void updateExecutionMetrics(List completedJobExecutionParamSets) {
+ for (TuningJobExecutionParamSet completedJobExecutionParamSet : completedJobExecutionParamSets) {
- //To artificially increase the cost function value 3 times (as a penalty) in case of metric value violation
- Integer penaltyConstant = 3;
+ JobExecution jobExecution = completedJobExecutionParamSet.jobExecution;
+ JobSuggestedParamSet jobSuggestedParamSet = completedJobExecutionParamSet.jobSuggestedParamSet;
+ JobDefinition job = jobExecution.job;
- for (TuningJobExecution tuningJobExecution : completedExecutions) {
- logger.info("Updating execution metrics and fitness for execution: " + tuningJobExecution.jobExecution.jobExecId);
+ logger.info("Updating execution metrics and fitness for execution: " + jobExecution.jobExecId);
try {
- JobExecution jobExecution = tuningJobExecution.jobExecution;
- JobDefinition job = jobExecution.job;
-
- // job id match and tuning enabled
TuningJobDefinition tuningJobDefinition = TuningJobDefinition.find.select("*")
.fetch(TuningJobDefinition.TABLE.job, "*")
.where()
.eq(TuningJobDefinition.TABLE.job + "." + JobDefinition.TABLE.id, job.id)
- .eq(TuningJobDefinition.TABLE.tuningEnabled, 1)
+ .order()
+ .desc(TuningJobDefinition.TABLE.createdTs)
.findUnique();
List results = AppResult.find.select("*")
@@ -323,12 +373,9 @@ private void updateExecutionMetrics(List completedExecutions
.findList();
if (results != null && results.size() > 0) {
- Long totalExecutionTime = 0L;
Double totalResourceUsed = 0D;
Double totalInputBytesInBytes = 0D;
- Map counterValuesMap = new HashMap();
-
for (AppResult appResult : results) {
totalResourceUsed += appResult.resourceUsed;
totalInputBytesInBytes += getTotalInputBytes(appResult);
@@ -336,13 +383,13 @@ private void updateExecutionMetrics(List completedExecutions
Long totalRunTime = Utils.getTotalRuntime(results);
Long totalDelay = Utils.getTotalWaittime(results);
- totalExecutionTime = totalRunTime - totalDelay;
+ Long totalExecutionTime = totalRunTime - totalDelay;
if (totalExecutionTime != 0) {
jobExecution.executionTime = totalExecutionTime * 1.0 / (1000 * 60);
jobExecution.resourceUsage = totalResourceUsed * 1.0 / (1024 * 3600);
jobExecution.inputSizeInBytes = totalInputBytesInBytes;
-
+ jobExecution.update();
logger.info(
"Metric Values for execution " + jobExecution.jobExecId + ": Execution time = " + totalExecutionTime
+ ", Resource usage = " + totalResourceUsed + " and total input size = " + totalInputBytesInBytes);
@@ -356,54 +403,112 @@ private void updateExecutionMetrics(List completedExecutions
}
//Compute fitness
- Double averageResourceUsagePerGBInput =
- tuningJobDefinition.averageResourceUsage * FileUtils.ONE_GB / tuningJobDefinition.averageInputSizeInBytes;
- Double maxDesiredResourceUsagePerGBInput =
- averageResourceUsagePerGBInput * tuningJobDefinition.allowedMaxResourceUsagePercent / 100.0;
- Double averageExecutionTimePerGBInput =
- tuningJobDefinition.averageExecutionTime * FileUtils.ONE_GB / tuningJobDefinition.averageInputSizeInBytes;
- Double maxDesiredExecutionTimePerGBInput =
- averageExecutionTimePerGBInput * tuningJobDefinition.allowedMaxExecutionTimePercent / 100.0;
- Double resourceUsagePerGBInput =
- jobExecution.resourceUsage * FileUtils.ONE_GB / jobExecution.inputSizeInBytes;
- Double executionTimePerGBInput =
- jobExecution.executionTime * FileUtils.ONE_GB / jobExecution.inputSizeInBytes;
-
- if (resourceUsagePerGBInput > maxDesiredResourceUsagePerGBInput
- || executionTimePerGBInput > maxDesiredExecutionTimePerGBInput) {
- logger.info("Execution " + jobExecution.jobExecId + " violates constraint on resource usage per GB input");
- tuningJobExecution.fitness = penaltyConstant * maxDesiredResourceUsagePerGBInput;
- } else {
- tuningJobExecution.fitness = resourceUsagePerGBInput;
- }
- tuningJobExecution.paramSetState = ParamSetStatus.FITNESS_COMPUTED;
- jobExecution.update();
- tuningJobExecution.update();
- }
- TuningJobExecution currentBestTuningJobExecution;
- try {
- currentBestTuningJobExecution =
- TuningJobExecution.find.where().eq("jobExecution.job.id", tuningJobExecution.jobExecution.job.id).
- eq(TuningJobExecution.TABLE.isParamSetBest, 1).findUnique();
- if (currentBestTuningJobExecution.fitness > tuningJobExecution.fitness) {
- currentBestTuningJobExecution.isParamSetBest = false;
- tuningJobExecution.isParamSetBest = true;
- currentBestTuningJobExecution.save();
- tuningJobExecution.save();
+ if (!jobSuggestedParamSet.paramSetState.equals(ParamSetStatus.FITNESS_COMPUTED)) {
+ if (jobExecution.executionState.equals(JobExecution.ExecutionState.SUCCEEDED)) {
+ logger.info("Execution id: " + jobExecution.id + " succeeded");
+ updateJobSuggestedParamSetSucceededExecution(jobExecution, jobSuggestedParamSet, tuningJobDefinition);
+ } else {
+ // Resetting param set to created state because this case captures the scenarios when
+ // either the job failed for reasons other than auto tuning or was killed/cancelled/skipped etc.
+ // In all the above scenarios, fitness cannot be computed for the param set correctly.
+ // Note that the penalty on failures caused by auto tuning is applied when the job execution is retried
+ // after failure.
+ logger.info("Execution id: " + jobExecution.id + " was not successful for reason other than tuning."
+ + "Resetting param set: " + jobSuggestedParamSet.id + " to CREATED state");
+ resetParamSetToCreated(jobSuggestedParamSet);
+ }
+ }
+ } else {
+ long diff = System.currentTimeMillis() - jobExecution.updatedTs.getTime();
+ logger.debug("Current Time in millis: " + System.currentTimeMillis() + ", job execution last updated time "
+ + jobExecution.updatedTs.getTime());
+ if (diff > ignoreExecutionWaitInterval) {
+ logger.info("Fitness of param set " + jobSuggestedParamSet.id + " corresponding to execution id: " +
+ jobExecution.id + " not computed for more than the maximum duration specified to compute fitness. "
+ + "Resetting the param set to CREATED state");
+ resetParamSetToCreated(jobSuggestedParamSet);
}
- } catch (NullPointerException e) {
- tuningJobExecution.isParamSetBest = true;
- tuningJobExecution.save();
}
} catch (Exception e) {
- logger.error("Error updating fitness of execution: " + tuningJobExecution.jobExecution.id + "\n Stacktrace: ",
- e);
+ logger.error("Error updating fitness of execution: " + jobExecution.id + "\n Stacktrace: ", e);
}
}
logger.info("Execution metrics updated");
}
+ /**
+ * Resets the param set to CREATED state if its fitness is not already computed
+ * @param jobSuggestedParamSet Param set which is to be reset
+ */
+ private void resetParamSetToCreated(JobSuggestedParamSet jobSuggestedParamSet) {
+ if (!jobSuggestedParamSet.paramSetState.equals(ParamSetStatus.FITNESS_COMPUTED)) {
+ logger.info("Resetting parameter set to created: " + jobSuggestedParamSet.id);
+ jobSuggestedParamSet.paramSetState = ParamSetStatus.CREATED;
+ jobSuggestedParamSet.save();
+ }
+ }
+
+ /**
+ * Updates the job suggested param set when the corresponding execution was succeeded
+ * @param jobExecution JobExecution: succeeded job execution corresponding to the param set which is to be updated
+ * @param jobSuggestedParamSet param set which is to be updated
+ * @param tuningJobDefinition TuningJobDefinition of the job to which param set corresponds
+ */
+ private void updateJobSuggestedParamSetSucceededExecution(JobExecution jobExecution,
+ JobSuggestedParamSet jobSuggestedParamSet, TuningJobDefinition tuningJobDefinition) {
+ int penaltyConstant = 3;
+ Double averageResourceUsagePerGBInput =
+ tuningJobDefinition.averageResourceUsage * FileUtils.ONE_GB / tuningJobDefinition.averageInputSizeInBytes;
+ Double maxDesiredResourceUsagePerGBInput =
+ averageResourceUsagePerGBInput * tuningJobDefinition.allowedMaxResourceUsagePercent / 100.0;
+ Double averageExecutionTimePerGBInput =
+ tuningJobDefinition.averageExecutionTime * FileUtils.ONE_GB / tuningJobDefinition.averageInputSizeInBytes;
+ Double maxDesiredExecutionTimePerGBInput =
+ averageExecutionTimePerGBInput * tuningJobDefinition.allowedMaxExecutionTimePercent / 100.0;
+ Double resourceUsagePerGBInput = jobExecution.resourceUsage * FileUtils.ONE_GB / jobExecution.inputSizeInBytes;
+ Double executionTimePerGBInput = jobExecution.executionTime * FileUtils.ONE_GB / jobExecution.inputSizeInBytes;
+
+ if (resourceUsagePerGBInput > maxDesiredResourceUsagePerGBInput
+ || executionTimePerGBInput > maxDesiredExecutionTimePerGBInput) {
+ logger.info("Execution " + jobExecution.jobExecId + " violates constraint on resource usage per GB input");
+ jobSuggestedParamSet.fitness = penaltyConstant * maxDesiredResourceUsagePerGBInput;
+ } else {
+ jobSuggestedParamSet.fitness = resourceUsagePerGBInput;
+ }
+ jobSuggestedParamSet.paramSetState = ParamSetStatus.FITNESS_COMPUTED;
+ jobSuggestedParamSet.fitnessJobExecution = jobExecution;
+ jobSuggestedParamSet = updateBestJobSuggestedParamSet(jobSuggestedParamSet);
+ jobSuggestedParamSet.update();
+ }
+
+ /**
+ * Updates the given job suggested param set to be the best param set if its fitness is less than the current best param set
+ * (since the objective is to minimize the fitness, the param set with the lowest fitness is the best)
+ * @param jobSuggestedParamSet JobSuggestedParamSet
+ */
+ private JobSuggestedParamSet updateBestJobSuggestedParamSet(JobSuggestedParamSet jobSuggestedParamSet) {
+ logger.info("Checking if a new best param set is found for job: " + jobSuggestedParamSet.jobDefinition.jobDefId);
+ JobSuggestedParamSet currentBestJobSuggestedParamSet = JobSuggestedParamSet.find.where()
+ .eq(JobSuggestedParamSet.TABLE.jobDefinition + "." + JobDefinition.TABLE.id,
+ jobSuggestedParamSet.jobDefinition.id)
+ .eq(JobSuggestedParamSet.TABLE.isParamSetBest, 1)
+ .findUnique();
+ if (currentBestJobSuggestedParamSet != null) {
+ if (currentBestJobSuggestedParamSet.fitness > jobSuggestedParamSet.fitness) {
+ logger.info("Param set: " + jobSuggestedParamSet.id + " is the new best param set for job: " + jobSuggestedParamSet.jobDefinition.jobDefId);
+ currentBestJobSuggestedParamSet.isParamSetBest = false;
+ jobSuggestedParamSet.isParamSetBest = true;
+ currentBestJobSuggestedParamSet.save();
+ }
+ } else {
+ logger.info("No best param set found for job: " + jobSuggestedParamSet.jobDefinition.jobDefId
+ + ". Marking current param set " + jobSuggestedParamSet.id + " as best");
+ jobSuggestedParamSet.isParamSetBest = true;
+ }
+ return jobSuggestedParamSet;
+ }
+
/**
* Returns the total input size
* @param appResult appResult
diff --git a/app/com/linkedin/drelephant/tuning/JobCompleteDetector.java b/app/com/linkedin/drelephant/tuning/JobCompleteDetector.java
index 694cfb329..21b8a909b 100644
--- a/app/com/linkedin/drelephant/tuning/JobCompleteDetector.java
+++ b/app/com/linkedin/drelephant/tuning/JobCompleteDetector.java
@@ -16,21 +16,15 @@
package com.linkedin.drelephant.tuning;
+import controllers.AutoTuningMetricsController;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
-import java.util.ArrayList;
import java.util.List;
-
import models.JobExecution;
import models.JobExecution.ExecutionState;
-import models.TuningJobExecution;
-import models.TuningJobExecution.ParamSetStatus;
-
+import models.TuningJobExecutionParamSet;
import org.apache.log4j.Logger;
-import controllers.AutoTuningMetricsController;
-import play.libs.Json;
-
/**
* This class pools the scheduler for completion status of execution and updates the database with current status
@@ -41,77 +35,54 @@ public abstract class JobCompleteDetector {
/**
* Updates the status of completed executions
- * @return List of completed executions
* @throws MalformedURLException MalformedURLException
* @throws URISyntaxException URISyntaxException
*/
- public List updateCompletedExecutions() throws MalformedURLException, URISyntaxException {
- logger.info("Checking execution status");
- List runningExecutions = getStartedExecutions();
- List completedExecutions = getCompletedExecutions(runningExecutions);
- updateExecutionStatus(completedExecutions);
+ public void updateCompletedExecutions() throws MalformedURLException, URISyntaxException {
+ logger.info("Updating execution status");
+ List inProgressExecutionParamSet = getExecutionsInProgress();
+ List completedExecutions = getCompletedExecutions(inProgressExecutionParamSet);
updateMetrics(completedExecutions);
logger.info("Finished updating execution status");
- return completedExecutions;
}
/**
- * This method is for updating metrics for auto tuning monitoring for job completion daemon
+ * Updates metrics for auto tuning monitoring for job completion daemon
* @param completedExecutions List completed job executions
*/
- private void updateMetrics(List completedExecutions) {
- for (TuningJobExecution tuningJobExecution : completedExecutions) {
- if (tuningJobExecution.paramSetState.equals(ParamSetStatus.EXECUTED)) {
- if (tuningJobExecution.jobExecution.executionState.equals(ExecutionState.SUCCEEDED)) {
- AutoTuningMetricsController.markSuccessfulJobs();
- } else if (tuningJobExecution.jobExecution.executionState.equals(ExecutionState.FAILED)) {
- AutoTuningMetricsController.markFailedJobs();
- }
+ private void updateMetrics(List completedExecutions) {
+ for (JobExecution jobExecution : completedExecutions) {
+ if (jobExecution.executionState.equals(ExecutionState.SUCCEEDED)) {
+ AutoTuningMetricsController.markSuccessfulJobs();
+ } else if (jobExecution.executionState.equals(ExecutionState.FAILED)) {
+ AutoTuningMetricsController.markFailedJobs();
}
}
}
/**
- * Returns the list of executions which have already received param suggestion
+ * Returns the executions in progress
* @return JobExecution list
*/
- private List getStartedExecutions() {
- logger.info("Fetching the executions which were running");
- List tuningJobExecutionList = new ArrayList();
- try {
- tuningJobExecutionList = TuningJobExecution.find.select("*")
- .where()
- .eq(TuningJobExecution.TABLE.paramSetState, ParamSetStatus.SENT)
- .findList();
- } catch (NullPointerException e) {
- logger.info("None of the executions were running ", e);
- }
- logger.info("Number of executions which were in running state: " + tuningJobExecutionList.size());
- return tuningJobExecutionList;
+ private List getExecutionsInProgress() {
+ logger.info("Fetching the executions which are in progress");
+ List tuningJobExecutionParamSets = TuningJobExecutionParamSet.find.fetch(TuningJobExecutionParamSet.TABLE.jobExecution)
+ .fetch(TuningJobExecutionParamSet.TABLE.jobSuggestedParamSet)
+ .where()
+ .eq(TuningJobExecutionParamSet.TABLE.jobExecution + '.' + JobExecution.TABLE.executionState,
+ ExecutionState.IN_PROGRESS)
+ .findList();
+ logger.info("Number of executions which are in progress: " + tuningJobExecutionParamSets.size());
+ return tuningJobExecutionParamSets;
}
/**
* Returns the list of completed executions.
- * @param jobExecutions Started Execution list
+ * @param inProgressExecutionParamSet List of executions (with corresponding param set) in progress
* @return List of completed executions
- * @throws MalformedURLException
- * @throws URISyntaxException
- */
- protected abstract List getCompletedExecutions(List jobExecutions)
- throws MalformedURLException, URISyntaxException;
-
- /**
- * Updates the job execution status
- * @param jobExecutions JobExecution list
- * @return Update status
+ * @throws MalformedURLException MalformedURLException
+ * @throws URISyntaxException URISyntaxException
*/
- private void updateExecutionStatus(List jobExecutions) {
- logger.info("Updating status of executions completed since last iteration");
- for (TuningJobExecution tuningJobExecution : jobExecutions) {
- JobExecution jobExecution = tuningJobExecution.jobExecution;
- logger.info("Updating execution status to EXECUTED for the execution: " + jobExecution.jobExecId);
- jobExecution.update();
- tuningJobExecution.update();
- }
- }
+ protected abstract List getCompletedExecutions(
+ List inProgressExecutionParamSet) throws MalformedURLException, URISyntaxException;
}
diff --git a/app/com/linkedin/drelephant/tuning/JobTuningInfo.java b/app/com/linkedin/drelephant/tuning/JobTuningInfo.java
index 3da56b6ab..36ce00f6a 100644
--- a/app/com/linkedin/drelephant/tuning/JobTuningInfo.java
+++ b/app/com/linkedin/drelephant/tuning/JobTuningInfo.java
@@ -18,9 +18,7 @@
import models.TuningParameter;
import models.JobDefinition;
-
import java.util.List;
-
import models.TuningAlgorithm.JobType;
/**
diff --git a/app/com/linkedin/drelephant/tuning/PSOParamGenerator.java b/app/com/linkedin/drelephant/tuning/PSOParamGenerator.java
index b553f8772..1d1651cad 100644
--- a/app/com/linkedin/drelephant/tuning/PSOParamGenerator.java
+++ b/app/com/linkedin/drelephant/tuning/PSOParamGenerator.java
@@ -18,17 +18,14 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.linkedin.drelephant.ElephantContext;
-
-import org.apache.hadoop.conf.Configuration;
-import org.apache.log4j.Logger;
-
-import play.libs.Json;
-
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.log4j.Logger;
+import play.libs.Json;
/**
@@ -36,12 +33,11 @@
*/
public class PSOParamGenerator extends ParamGenerator {
- private final Logger logger = Logger.getLogger(PSOParamGenerator.class);
private static final String PARAMS_TO_TUNE_FIELD_NAME = "parametersToTune";
private static final String PYTHON_PATH_CONF = "python.path";
private static final String PSO_DIR_PATH_ENV_VARIABLE = "PSO_DIR_PATH";
private static final String PYTHON_PATH_ENV_VARIABLE = "PYTHONPATH";
-
+ private final Logger logger = Logger.getLogger(PSOParamGenerator.class);
private String PYTHON_PATH = null;
private String TUNING_SCRIPT_PATH = null;
@@ -78,9 +74,7 @@ public JobTuningInfo generateParamSet(JobTuningInfo jobTuningInfo) {
newJobTuningInfo.setJobType(jobTuningInfo.getJobType());
JsonNode jsonJobTuningInfo = Json.toJson(jobTuningInfo);
- logger.info("Job Tuning Info for " + jobTuningInfo.getTuningJob().jobName + ": " + jsonJobTuningInfo);
String parametersToTune = jsonJobTuningInfo.get(PARAMS_TO_TUNE_FIELD_NAME).toString();
- logger.info("Parameters to tune for job: " + parametersToTune);
String stringTunerState = jobTuningInfo.getTunerState();
stringTunerState = stringTunerState.replaceAll("\\s+", "");
String jobType = jobTuningInfo.getJobType().toString();
@@ -88,14 +82,12 @@ public JobTuningInfo generateParamSet(JobTuningInfo jobTuningInfo) {
List error = new ArrayList();
try {
- logger.info(
- "Calling PSO with Job type = " + jobType + " StringTunerState= " + stringTunerState + "\nand Parameters to tune: " + parametersToTune);
Process p = Runtime.getRuntime()
- .exec(PYTHON_PATH + " " + TUNING_SCRIPT_PATH + " " + stringTunerState + " " + parametersToTune + " " + jobType);
+ .exec(
+ PYTHON_PATH + " " + TUNING_SCRIPT_PATH + " " + stringTunerState + " " + parametersToTune + " " + jobType);
BufferedReader inputStream = new BufferedReader(new InputStreamReader(p.getInputStream()));
BufferedReader errorStream = new BufferedReader(new InputStreamReader(p.getErrorStream()));
String updatedStringTunerState = inputStream.readLine();
- logger.info("Output from PSO script: " + updatedStringTunerState);
newJobTuningInfo.setTunerState(updatedStringTunerState);
String errorLine;
while ((errorLine = errorStream.readLine()) != null) {
diff --git a/app/com/linkedin/drelephant/tuning/ParamGenerator.java b/app/com/linkedin/drelephant/tuning/ParamGenerator.java
index 952a9e22b..4b3ea3508 100644
--- a/app/com/linkedin/drelephant/tuning/ParamGenerator.java
+++ b/app/com/linkedin/drelephant/tuning/ParamGenerator.java
@@ -17,37 +17,34 @@
package com.linkedin.drelephant.tuning;
import com.avaje.ebean.Expr;
+import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
-
+import controllers.AutoTuningMetricsController;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
-
-import models.*;
-
-import com.fasterxml.jackson.databind.JsonNode;
-
-import controllers.AutoTuningMetricsController;
-
+import models.JobDefinition;
+import models.JobExecution;
+import models.JobSavedState;
+import models.JobSuggestedParamSet;
+import models.JobSuggestedParamValue;
+import models.TuningAlgorithm;
+import models.TuningJobDefinition;
+import models.TuningParameter;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
-
import play.libs.Json;
-import java.util.ArrayList;
-import java.util.List;
-
-import static java.lang.Math.*;
-
/**
* This is an abstract class for generating parameter suggestions for jobs
*/
public abstract class ParamGenerator {
- private final Logger logger = Logger.getLogger(getClass());
-
private static final String JSON_CURRENT_POPULATION_KEY = "current_population";
+ private final Logger logger = Logger.getLogger(getClass());
/**
* Generates the parameters using tuningJobInfo and returns it in updated JobTuningInfo
@@ -82,58 +79,45 @@ private List jsonToParticleList(JsonNode jsonParticleList) {
* Fetches the list to job which need new parameter suggestion
* @return Job list
*/
- private List fetchJobsForParamSuggestion() {
-
+ private List getJobsForParamSuggestion() {
// Todo: [Important] Change the logic. This is very rigid. Ideally you should look at the param set ids in the saved state,
// todo: [continuation] if their fitness is computed, pso can generate new params for the job
logger.info("Checking which jobs need new parameter suggestion");
List jobsForParamSuggestion = new ArrayList();
- List pendingParamExecutionList = new ArrayList();
- try {
- pendingParamExecutionList = TuningJobExecution.find.select("*")
- .fetch(TuningJobExecution.TABLE.jobExecution, "*")
- .where()
- .or(Expr.or(Expr.eq(TuningJobExecution.TABLE.paramSetState, TuningJobExecution.ParamSetStatus.CREATED),
- Expr.eq(TuningJobExecution.TABLE.paramSetState, TuningJobExecution.ParamSetStatus.SENT)),
- Expr.eq(TuningJobExecution.TABLE.paramSetState, TuningJobExecution.ParamSetStatus.EXECUTED))
- .eq(TuningJobExecution.TABLE.isDefaultExecution, 0)
- .findList();
- } catch (NullPointerException e) {
- logger.info("None of the non-default executions are in CREATED, SENT OR EXECUTED state");
- }
+ List pendingParamSetList = JobSuggestedParamSet.find.select("*")
+ .fetch(JobSuggestedParamSet.TABLE.jobDefinition, "*")
+ .where()
+ .or(Expr.or(Expr.eq(JobSuggestedParamSet.TABLE.paramSetState, JobSuggestedParamSet.ParamSetStatus.CREATED),
+ Expr.eq(JobSuggestedParamSet.TABLE.paramSetState, JobSuggestedParamSet.ParamSetStatus.SENT)),
+ Expr.eq(JobSuggestedParamSet.TABLE.paramSetState, JobSuggestedParamSet.ParamSetStatus.EXECUTED))
+ .eq(JobSuggestedParamSet.TABLE.isParamSetDefault, 0)
+ .eq(JobSuggestedParamSet.TABLE.isParamSetBest, 0).findList();
List pendingParamJobList = new ArrayList();
- for (TuningJobExecution pendingParamExecution : pendingParamExecutionList) {
- if (!pendingParamJobList.contains(pendingParamExecution.jobExecution.job)) {
- pendingParamJobList.add(pendingParamExecution.jobExecution.job);
+ for (JobSuggestedParamSet pendingParamSet : pendingParamSetList) {
+ if (!pendingParamJobList.contains(pendingParamSet.jobDefinition)) {
+ pendingParamJobList.add(pendingParamSet.jobDefinition);
}
}
- List tuningJobDefinitionList = new ArrayList();
+ List tuningJobDefinitionList = TuningJobDefinition.find.select("*")
+ .fetch(TuningJobDefinition.TABLE.job, "*")
+ .where()
+ .eq(TuningJobDefinition.TABLE.tuningEnabled, 1)
+ .findList();
- try {
- tuningJobDefinitionList = TuningJobDefinition.find.select("*")
- .fetch(TuningJobDefinition.TABLE.job, "*")
- .where()
- .eq(TuningJobDefinition.TABLE.tuningEnabled, 1)
- .findList();
- } catch (NullPointerException e) {
+ if (tuningJobDefinitionList.size() == 0) {
logger.error("No auto-tuning enabled jobs found");
}
for (TuningJobDefinition tuningJobDefinition : tuningJobDefinitionList) {
if (!pendingParamJobList.contains(tuningJobDefinition.job)) {
- jobsForParamSuggestion.add(tuningJobDefinition);
- }
- }
- if (jobsForParamSuggestion.size() > 0) {
- for (TuningJobDefinition tuningJobDefinition : jobsForParamSuggestion) {
logger.info("New parameter suggestion needed for job: " + tuningJobDefinition.job.jobName);
+ jobsForParamSuggestion.add(tuningJobDefinition);
}
- } else {
- logger.info("None of the jobs need new parameter suggestion");
}
+ logger.info("Number of job(s) which need new parameter suggestion: " + jobsForParamSuggestion.size());
return jobsForParamSuggestion;
}
@@ -171,19 +155,19 @@ private List getJobsTuningInfo(List tuningJo
.eq(TuningParameter.TABLE.isDerived, 0)
.findList();
- try {
logger.info("Fetching default parameter values for job " + tuningJobDefinition.job.jobDefId);
- TuningJobExecution defaultJobExecution = TuningJobExecution.find.where()
- .eq(TuningJobExecution.TABLE.jobExecution + "." + JobExecution.TABLE.job + "." + JobDefinition.TABLE.id,
- tuningJobDefinition.job.id)
- .eq(TuningJobExecution.TABLE.isDefaultExecution, 1)
- .orderBy(TuningJobExecution.TABLE.jobExecution + "." + JobExecution.TABLE.id + " desc")
+ JobSuggestedParamSet defaultJobParamSet = JobSuggestedParamSet.find.where()
+ .eq(JobSuggestedParamSet.TABLE.jobDefinition + "." + JobDefinition.TABLE.id, tuningJobDefinition.job.id)
+ .eq(JobSuggestedParamSet.TABLE.isParamSetDefault, 1)
+ .order()
+ .desc(JobSuggestedParamSet.TABLE.id)
.setMaxRows(1)
.findUnique();
- if (defaultJobExecution != null && defaultJobExecution.jobExecution != null) {
+
+ if (defaultJobParamSet != null) {
List jobSuggestedParamValueList = JobSuggestedParamValue.find.where()
- .eq(JobSuggestedParamValue.TABLE.jobExecution + "." + JobExecution.TABLE.id,
- defaultJobExecution.jobExecution.id)
+ .eq(JobSuggestedParamValue.TABLE.jobSuggestedParamSet + "." + JobExecution.TABLE.id,
+ defaultJobParamSet.id)
.findList();
if (jobSuggestedParamValueList.size() > 0) {
@@ -205,9 +189,6 @@ private List