MINOR: Add JDK 20 CI build and remove some branch builds#12948
MINOR: Add JDK 20 CI build and remove some branch builds#12948ijuma merged 12 commits intoapache:trunkfrom
Conversation
|
#12675 needs to be merged for this to work. |
|
Rebased to include #12675. |
|
Interesting failure with Java 19:
Needs investigation. |
There was a problem hiding this comment.
@divijvaidya Looks like one of your changes introduced this issue where we are adding a long to an int in a couple of places in this file. Can you please take a look and suggest what's the right fix (I just did the simplest thing to make the compiler not complain - it's not safe as it stands)?
There was a problem hiding this comment.
This is the main blocker remaining before this PR can be non draft.
There was a problem hiding this comment.
@ijuma casting to int here is safe. Can you please add the following comment here:
// cast to int is fine here since the value of bytesSkipped is upper bound by value of avail
// which is an int
It is safe because as per the lines above it long bytesSkipped = (avail < remaining) ? avail : remaining;, bytesSkipped is minimum of avail or remaining which means it's max value is available which we know is an int.
Same for the other place.
There was a problem hiding this comment.
Hmm, in that case we can perhaps change bytesSkipped to be an int?
There was a problem hiding this comment.
I am afraid that won't be possible.
This is because there are two branches where bytesSkipped is updated. In one branch when we call bytesSkipped = getInIfOpen().skip(remaining), bytesSkipped holds a long value since getInIfOpen().skip(remaining) returns a long. In the other branch, when we call bytesSkipped = (avail < remaining) ? avail : remaining, bytesSkipped holds an int since this min statement bounds it to max of avail, which is an int.
I can attempt to do a bunch of refactoring if that will make things clearer but that shouldn't block this PR.
There was a problem hiding this comment.
I tweaked this a bit to make it more obvious why it's safe to cast. Please take a look.
4804537 to
ad2b5d0
Compare
It's a good idea for us to track the latest JDK version supported by Gradle.
|
@divijvaidya Do you have cycles to review this PR? It is ready for review. |
| new ListenerName(""), SecurityProtocol.PLAINTEXT, mock(classOf[ClientInformation]), false) | ||
| val request = new RequestChannel.Request(0, context, time.nanoseconds(), | ||
| mock(classOf[MemoryPool]), mock(classOf[ByteBuffer]), metrics) | ||
| mock(classOf[MemoryPool]), ByteBuffer.allocate(0), metrics) |
There was a problem hiding this comment.
It's not possible to mock this class with Java 20, but there's no reason to mock it.
| // Skip bytes stored in intermediate buffer first | ||
| int avail = count - pos; | ||
| long bytesSkipped = (avail < remaining) ? avail : remaining; | ||
| int bytesSkipped = Math.min(avail, (int) remaining); |
There was a problem hiding this comment.
I am not sure that this is a good idea to downcast and then perform comparison. Comparison amongst different types is performed by upgrading them to the higher type and then performing comparison. In current approach, we downcasting remaining to int is unsafe.
May I suggest an alternative where we upgrade avail to long before performing comparison and then converting result to int: int bytesSkipped = (int) Math.min((long) avail, remaining);
There was a problem hiding this comment.
Can you please share an example where these two variants would have a different result given that avail is a positive int?
There was a problem hiding this comment.
I am concerned about cases where remaining is outside the range of representable int values, let's say remaining = Integer.MIN_VALUE + 1000. When down casting in cases of overflow (i.e. in Math.min(avail, (int) remaining)), the behaviour is implementation defined and numeric value for (int)remaining may end up leading to a negative value numeric int. Calculating min for this negative value with avail will lead to bytesSkipped as negative.
In the implementation I suggested, the result of Math.min((long) avail, remaining) is guaranteed to fit in int because it's upper bound by avail.
There was a problem hiding this comment.
behaviour is implementation defined
Can you share a reference for this? My understanding is that the Java specifies the behavior in these cases. Perhaps you're thinking of a C or something like that?
let's say remaining = Integer.MIN_VALUE + 1000
I think you meant to say that if we have a long of value (Integer.MAX_VALUE + 1000) may result in sign extension during truncation resulting in a negative int. That's a fair point - my bad. I'll fix this. It does show a gap in the tests for this class, we should ideally have tests that cover these boundary cases.
There was a problem hiding this comment.
It does show a gap in the tests for this class, we should ideally have tests that cover these boundary cases.
Fair point. Added a JIRA https://issues.apache.org/jira/browse/KAFKA-15123
| // are pushed to trunk and/or release branches. We achieve this via | ||
| // the `when` clause. | ||
|
|
||
| stage('JDK 8 and Scala 2.13') { |
There was a problem hiding this comment.
Adding this note here for future visitors to this PR (and to ensure that we are on the same page here about the reason to remove these stages from CI)
Removing this combination from tests in fine because scala 2.13 is fully compatible with JDK 8 [1]. If we can guarantee that code is compiling correctly with 2.13 and JDK 8 separately, then that is sufficient and we do not necessarily have to test a unique combination of both.
[1] https://docs.scala-lang.org/overviews/jdk-compatibility/overview.html
There was a problem hiding this comment.
That statement isn't strictly true since there could be bugs in the implementation - that's why we had these tests in the first place. But the truth is that they're low value since (1) the probability of a bug affecting JDK 8 and Scala 2.13 but not JDK 8 and Scala 2.12 is low (2) having too many variants makes it even less likely for people to pay attention to the flaky failures that exist (3) it's expensive to run so many variants.
divijvaidya
left a comment
There was a problem hiding this comment.
The changes look good to me. Feel free to merge after the CI passes or has unrelated flaky tests.
|
Build looks good - the failures in the last build are all flakes. |
|
I pasted the wrong name on the reviewers field - sorry @divijvaidya. |
No worries! I am glad to see this merged in. |
It's good for us to add support for Java 20 in preparation for Java 21 - the next LTS.
Given that Scala 2.12 support has been deprecated, a Scala 2.12 variant is not included.
Also remove some branch builds that add load to the CI, but have
low value: JDK 8 & Scala 2.13 (JDK 8 support has been deprecated),
JDK 11 & Scala 2.12 (Scala 2.12 support has been deprecated) and
JDK 17 & Scala 2.12 (Scala 2.12 support has been deprecated).
A newer version of Mockito (4.9.0 -> 4.11.0) is required for Java 20 support, but we
only use it with Scala 2.13+ since it causes compilation errors with Scala 2.12. Similarly,
we upgrade easymock when the Java version is 16 or newer as it's incompatible
with powermock (which doesn't support Java 16 or newer).
Filed KAFKA-15117 for a test that fails with Java 20 (SslTransportLayerTest.testValidEndpointIdentificationCN).
Finally, fixed some lossy conversions that were added after #13582 was submitted.
Committer Checklist (excluded from commit message)