-
Notifications
You must be signed in to change notification settings - Fork 29.3k
[SPARK-32175][CORE] Fix the order between initialization for ExecutorPlugin and starting heartbeat thread #29002
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
d724179
1ccb1b7
9ed108d
dba4c91
d768385
9995ef1
0b8da96
f106fe9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -154,11 +154,6 @@ private[spark] class Executor( | |
| // for fetching remote cached RDD blocks, so need to make sure it uses the right classloader too. | ||
| env.serializerManager.setDefaultClassLoader(replClassLoader) | ||
|
|
||
| // Plugins need to load using a class loader that includes the executor's user classpath | ||
| private val plugins: Option[PluginContainer] = Utils.withContextClassLoader(replClassLoader) { | ||
| PluginContainer(env, resources.asJava) | ||
| } | ||
|
|
||
| // Max size of direct result. If task result is bigger than this, we use the block manager | ||
| // to send the result back. | ||
| private val maxDirectResultSize = Math.min( | ||
|
|
@@ -227,6 +222,11 @@ private[spark] class Executor( | |
|
|
||
| metricsPoller.start() | ||
|
tgravescs marked this conversation as resolved.
Outdated
|
||
|
|
||
| // Plugins need to load using a class loader that includes the executor's user classpath | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you add comments to explain why we need to initialize plugin after heartbeater? |
||
| private val plugins: Option[PluginContainer] = Utils.withContextClassLoader(replClassLoader) { | ||
| PluginContainer(env, resources.asJava) | ||
| } | ||
|
|
||
| private[executor] def numRunningTasks: Int = runningTasks.size() | ||
|
|
||
| /** | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,7 +17,7 @@ | |
|
|
||
| package org.apache.spark.executor | ||
|
|
||
| import java.io.{Externalizable, ObjectInput, ObjectOutput} | ||
| import java.io.{Externalizable, File, ObjectInput, ObjectOutput} | ||
| import java.lang.Thread.UncaughtExceptionHandler | ||
| import java.nio.ByteBuffer | ||
| import java.util.Properties | ||
|
|
@@ -41,6 +41,7 @@ import org.scalatestplus.mockito.MockitoSugar | |
| import org.apache.spark._ | ||
| import org.apache.spark.TaskState.TaskState | ||
| import org.apache.spark.broadcast.Broadcast | ||
| import org.apache.spark.deploy.{SimpleApplicationTest, SparkSubmitSuite} | ||
| import org.apache.spark.internal.config._ | ||
| import org.apache.spark.internal.config.UI._ | ||
| import org.apache.spark.memory.TestMemoryManager | ||
|
|
@@ -52,7 +53,7 @@ import org.apache.spark.scheduler.{DirectTaskResult, FakeTask, ResultTask, Task, | |
| import org.apache.spark.serializer.{JavaSerializer, SerializerInstance, SerializerManager} | ||
| import org.apache.spark.shuffle.FetchFailedException | ||
| import org.apache.spark.storage.{BlockManager, BlockManagerId} | ||
| import org.apache.spark.util.{LongAccumulator, UninterruptibleThread} | ||
| import org.apache.spark.util.{LongAccumulator, UninterruptibleThread, Utils} | ||
|
|
||
| class ExecutorSuite extends SparkFunSuite | ||
| with LocalSparkContext with MockitoSugar with Eventually with PrivateMethodTester { | ||
|
|
@@ -402,6 +403,77 @@ class ExecutorSuite extends SparkFunSuite | |
| assert(taskMetrics.getMetricValue("JVMHeapMemory") > 0) | ||
| } | ||
|
|
||
| test("SPARK-32175: Plugin initialization should start after heartbeater started") { | ||
| val tempDir = Utils.createTempDir() | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. clean it up at the end of the test? (though I know it will be cleaned by shutdown hook anyway.)
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll try to use |
||
|
|
||
| val importStatements = | ||
| """ | ||
| |import java.util.Map; | ||
| |import org.apache.spark.api.plugin.*; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about using the qualified class name to avoid adding the new parameter
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, it's better. |
||
| """.stripMargin | ||
| val sparkPluginCodeBody = | ||
| """ | ||
| |@Override | ||
| |public ExecutorPlugin executorPlugin() { | ||
| | return new TestExecutorPlugin(); | ||
| |} | ||
| | | ||
| |@Override | ||
| |public DriverPlugin driverPlugin() { return null; } | ||
| """.stripMargin | ||
| val executorPluginBody = | ||
| """ | ||
| |@Override | ||
| |public void init(PluginContext ctx, Map<String, String> extraConf) { | ||
| | try { | ||
| | Thread.sleep(30 * 1000); | ||
| | } catch (InterruptedException e) { | ||
| | throw new RuntimeException(e); | ||
| | } | ||
| |} | ||
| """.stripMargin | ||
|
|
||
| val compiledExecutorPlugin = TestUtils.createCompiledClass( | ||
| "TestExecutorPlugin", | ||
| tempDir, | ||
| "", | ||
| null, | ||
| Seq.empty, | ||
| importStatements, | ||
| Seq("ExecutorPlugin"), | ||
| executorPluginBody) | ||
|
|
||
| val thisClassPath = | ||
| sys.props("java.class.path").split(File.pathSeparator).map(p => new File(p).toURI.toURL) | ||
| val compiledSparkPlugin = TestUtils.createCompiledClass( | ||
| "TestSparkPlugin", | ||
| tempDir, | ||
| "", | ||
| null, | ||
| Seq(tempDir.toURI.toURL) ++ thisClassPath, | ||
| importStatements, | ||
| Seq("SparkPlugin"), | ||
| sparkPluginCodeBody) | ||
|
|
||
| val jarUrl = TestUtils.createJar( | ||
| Seq(compiledSparkPlugin, compiledExecutorPlugin), | ||
| new File(tempDir, "testPlugin.jar")) | ||
|
|
||
| val unusedJar = TestUtils.createJarWithClasses(Seq.empty) | ||
| val args = Seq( | ||
| "--class", SimpleApplicationTest.getClass.getName.stripSuffix("$"), | ||
| "--name", "testApp", | ||
| "--master", "local-cluster[1,1,1024]", | ||
| "--conf", "spark.plugins=TestSparkPlugin", | ||
| "--conf", "spark.storage.blockManagerSlaveTimeoutMs=" + 10 * 1000, | ||
| "--conf", "spark.network.timeoutInterval=" + 10 * 1000, | ||
| "--conf", "spark.executor.extraClassPath=" + jarUrl.toString, | ||
| "--conf", "spark.driver.extraClassPath=" + jarUrl.toString, | ||
| "--conf", "spark.ui.enabled=false", | ||
| unusedJar.toString) | ||
| SparkSubmitSuite.runSparkSubmit(args) | ||
| } | ||
|
|
||
| private def createMockEnv(conf: SparkConf, serializer: JavaSerializer): SparkEnv = { | ||
| val mockEnv = mock[SparkEnv] | ||
| val mockRpcEnv = mock[RpcEnv] | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. To be much simpler, I'll make it
(implementsClasses :+ "java.io.Serializable").mkString(", ").