MINOR: MiniKdc JVM shutdown hook fix#7946
Conversation
ijuma
left a comment
There was a problem hiding this comment.
Thanks for finding and fixing the issue. Could we add a unit or integration test that verifies the fix? That will help us not regress in the same way in the future.
|
ok to test |
@ijuma I can think of two ways but not sure which one (if either) is appropriate.
I think the possibility of regressing exists in the A similar potential for mistake exists with the other 6 cases (that create a Which leads us to the second possibility:
Such a utility method eliminates the possibility of mistakenly not overriding the
Static type checking that forces us to write it correctly:
A test would make sure that whatever is passed in doesn't actually run right away. A process that leverages the method wouldn't need so much configuration (just the test jar on the CLASSPATH and no Kerberos configs), so if we wanted to we could actually test that the A disadvantage of this second approach is that we won't test that (2) seems like the better choice, but since shutdown hooks are generally difficult to test I wanted to confirm that the approach of writing the utility method seems reasonable and worth it before moving ahead. |
|
I'm not sure that having a separate utility method is really that much more error-proof than having a constructor for KafkaThread like we do now. After all, you could always forget to call the utility method. Since this is tested through ducktape, maybe that's enough for now? Unless we really want to add a junit test of spawning this thing in a separate JVM... |
|
Can you not call |
|
@ijuma Do you mean pass in something at the command line (e.g. I was tending to agree more with @cmccabe, that maybe this isn't worth trying to test, but it did occur to me that creating a utility method and doing in-process testing (i.e. confirm the shutdown hook is not being called immediately) would be quick to implement, and doing so would provide the static type checking benefits relatively cheaply. Out-of-process testing to confirm that the utility method is actually registering the hook seems like overkill, but we could creating something akin to It is expensive to confirm that anything spawning as a process is adding the hook. Maybe we just skip this part and optimize for cost/benefit as opposed to perfection? |
|
@rondagostino I mean simply calling |
|
@ijuma That method is defined as |
|
You can make it |
|
@ijuma I'm think a two-pronged approach would be quick and effective:
Unless I hear otherwise I'll go ahead with both. |
|
Sounds good. I agree that a utility method that takes a |
|
@ijuma I changed the 7 Scala classes that were calling There were 5 Java instances under |
|
retest this please |
| throw new AssertionError("halt should not return, but it did.") | ||
| } | ||
|
|
||
| def addShutdownHook(runnable: Runnable, name: Option[String] = None): Unit = { |
There was a problem hiding this comment.
Should we use a by-name parameter instead?
There was a problem hiding this comment.
I don't think so because the runnable is dereferenced once, immediately, and the code passes it to the underlying Java implementation org.apache.kafka.common.utils.Exit
There was a problem hiding this comment.
I don't understand why any of that disqualifies a by name parameter. The benefit of using a by-name is that the caller code becomes cleaner (no need for () => ...).
There was a problem hiding this comment.
I got it working with a by-name parameter, and the below test passes. Is this what you were thinking? If so, I can add a new commit with the changes for a better look.
@Test
def shouldNotInvokeShutdownHookImmediately(): Unit = {
val value = "value"
val array:Array[Any] = Array(value)
def sideEffect(): Unit = {
// mutate the first element
array(0) = array(0).toString + array(0).toString
}
Exit.addShutdownHook(sideEffect) // by-name parameter, not invoked
// make sure the first element wasn't mutated
assertEquals(value, array(0))
Exit.addShutdownHook(sideEffect()) // by-name parameter, not invoked
// again make sure the first element wasn't mutated
assertEquals(value, array(0))
Exit.addShutdownHook(sideEffect, Some("message")) // by-name parameter, not invoked
// make sure the first element still isn't mutated
assertEquals(value, array(0))
}
|
|
||
| def addShutdownHook(code: => Unit, name: Option[String] = None): Unit = { | ||
| JExit.addShutdownHook(() => code, name.orNull) | ||
| def addShutdownHook(statementByName: => Unit, name: Option[String] = None): Unit = { |
There was a problem hiding this comment.
Scala is an expression oriented language, there are no statements. This could be simply f or shutdownHook. Also, I think name should not be optional and should be the first parameter.
There was a problem hiding this comment.
there are no statements
This could be simplyforshutdownHook
I was under the impression that statements were expressions that return no value (i.e. :Unit), but I could see how this may be technically incorrect. I'll rename it shutdownHook.
I think
nameshould not be optional and should be the first parameter.
There are places where currently a name is not given, and the Thread class auto-generates a name when it is not supplied, but I don't see a problem with making it a mandatory first parameter and supplying a thread name in the places where one is not yet supplied to the shutdown hook thread; I will do that. I will switch the order in the Java Exit class as well (name first, Runnable second).
There was a problem hiding this comment.
Yeah, Unit in Scala is actually a type and () a value. So, no statements. :)
With regards to names, I personally think it's very useful to give names to threads and we should require it. I think you've done that for the Scala code. Can we do it for the Java code too?
|
@ijuma Looks like @rondagostino has addressed your comments. Does this PR need another full review or have you already reviewed the changes? |
|
@rajinisivaram I didn't do a proper review, just looked at the method definitions. So, it would probably be good for you to review. |
|
@rajinisivaram The last commit addresses the last review comment from @ijuma; we now accept a thread name with a |
rajinisivaram
left a comment
There was a problem hiding this comment.
@rondagostino Thanks for the PR. Left a few minor comments, apart from that LGTM.
|
Thanks, @rajinisivaram. All set. I reverted the other format changes -- I'm not sure how that happened. Probably the IDE did it without me realizing it. |
|
@rondagostino Thanks for the updates, merging to trunk. |
Conflicts and/or compiler errors due to the fact that we temporarily reverted the commit that removes Scala 2.11 support: * Exit.scala: replace SAMs with anonymous inner classes. * MiniKdc.scala: take upstream changes. # By A. Sophie Blee-Goldman (1) and others # Via Jason Gustafson * apache-github/trunk: KAFKA-9254; Overridden topic configs are reset after dynamic default change (apache#7870) MINOR: MiniKdc JVM shutdown hook fix (apache#7946) KAFKA-9152; Improve Sensor Retrieval (apache#7928) Correct exception message in DistributedHerder (apache#7995) KAFKA-7317: Use collections subscription for main consumer to reduce metadata (apache#7969) KAFKA-9181; Maintain clean separation between local and group subscriptions in consumer's SubscriptionState (apache#7941) KAFKA-7737; Use single path in producer for initializing the producerId (apache#7920) # Conflicts: # core/src/test/scala/kafka/security/minikdc/MiniKdc.scala
When started as a separate process (which happens only in system tests), the shutdown hook for
MiniKdcwas running immediately, which causes tests that depend on it for Kerberos to fail. The system tests inzookeeper_security_upgrade_test.py, for example, fail because ZooKeeper is unable to contact the KDC.I ran the above system test locally and confirmed it failed prior to the fix and succeeded after the fix:
WITHOUT FIX:
WITH FIX: