Skip to content

MINOR: Add JDK 20 CI build and remove some branch builds#12948

Merged
ijuma merged 12 commits intoapache:trunkfrom
ijuma:java-19-build
Jun 30, 2023
Merged

MINOR: Add JDK 20 CI build and remove some branch builds#12948
ijuma merged 12 commits intoapache:trunkfrom
ijuma:java-19-build

Conversation

@ijuma
Copy link
Copy Markdown
Member

@ijuma ijuma commented Dec 3, 2022

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)

  • Verify design and implementation
  • Verify test coverage and CI build status
  • Verify documentation (including upgrade notes)

@ijuma
Copy link
Copy Markdown
Member Author

ijuma commented Dec 4, 2022

#12675 needs to be merged for this to work.

@ijuma
Copy link
Copy Markdown
Member Author

ijuma commented Dec 5, 2022

Rebased to include #12675.

@ijuma
Copy link
Copy Markdown
Member Author

ijuma commented Dec 5, 2022

Interesting failure with Java 19:

09:03:40 Gradle Test Run :core:integrationTest > Gradle Test Executor 166 > DynamicBrokerReconfigurationTest > testTrustStoreAlter(String) > kafka.server.DynamicBrokerReconfigurationTest.testTrustStoreAlter(String)[2] FAILED
09:03:40 org.apache.kafka.common.KafkaException: Failed to close kafka consumer
09:03:40 at app//org.apache.kafka.clients.consumer.KafkaConsumer.close(KafkaConsumer.java:2430)
09:03:40 at app//org.apache.kafka.clients.consumer.KafkaConsumer.close(KafkaConsumer.java:2378)
09:03:40 at app//kafka.server.DynamicBrokerReconfigurationTest.$anonfun$tearDown$6(DynamicBrokerReconfigurationTest.scala:172)
09:03:40 at app//kafka.server.DynamicBrokerReconfigurationTest.$anonfun$tearDown$6$adapted(DynamicBrokerReconfigurationTest.scala:172)
09:03:40 at app//scala.collection.IterableOnceOps.foreach(IterableOnce.scala:575)
09:03:40 at app//scala.collection.IterableOnceOps.foreach$(IterableOnce.scala:573)
09:03:40 at app//scala.collection.AbstractIterable.foreach(Iterable.scala:933)
09:03:40 at app//kafka.server.DynamicBrokerReconfigurationTest.tearDown(DynamicBrokerReconfigurationTest.scala:172)
09:03:40
09:03:40 Caused by:
09:03:40 org.apache.kafka.common.errors.SslAuthenticationException: Failed to process post-handshake messages
09:03:40
09:03:40 Caused by:
09:03:40 javax.net.ssl.SSLHandshakeException: Received fatal alert: certificate_unknown

Needs investigation.

@ijuma ijuma changed the title MINOR: Add JDK 19 & Scala 2.13 CI build MINOR: Add JDK 20 CI build Dec 24, 2022
@divijvaidya divijvaidya added the dependencies Pull requests that update a dependency file label Jun 19, 2023
Copy link
Copy Markdown
Member Author

@ijuma ijuma Jun 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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)?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the main blocker remaining before this PR can be non draft.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am on it

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, in that case we can perhaps change bytesSkipped to be an int?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tweaked this a bit to make it more obvious why it's safe to cast. Please take a look.

@ijuma ijuma marked this pull request as draft June 21, 2023 14:01
@ijuma ijuma changed the title MINOR: Add JDK 20 CI build MINOR: Add JDK 20 CI build and remove some branch builds Jun 21, 2023
@ijuma ijuma force-pushed the java-19-build branch 2 times, most recently from 4804537 to ad2b5d0 Compare June 22, 2023 18:57
@ijuma ijuma marked this pull request as ready for review June 23, 2023 16:10
@ijuma
Copy link
Copy Markdown
Member Author

ijuma commented Jun 23, 2023

@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)
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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);

Copy link
Copy Markdown
Member Author

@ijuma ijuma Jun 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please share an example where these two variants would have a different result given that avail is a positive int?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

Comment thread Jenkinsfile
// are pushed to trunk and/or release branches. We achieve this via
// the `when` clause.

stage('JDK 8 and Scala 2.13') {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member

@divijvaidya divijvaidya left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes look good to me. Feel free to merge after the CI passes or has unrelated flaky tests.

@ijuma
Copy link
Copy Markdown
Member Author

ijuma commented Jun 30, 2023

Build looks good - the failures in the last build are all flakes.

@ijuma ijuma merged commit 1f4cbc5 into apache:trunk Jun 30, 2023
@ijuma ijuma deleted the java-19-build branch June 30, 2023 08:12
@ijuma
Copy link
Copy Markdown
Member Author

ijuma commented Jun 30, 2023

I pasted the wrong name on the reviewers field - sorry @divijvaidya.

@divijvaidya
Copy link
Copy Markdown
Member

I pasted the wrong name on the reviewers field - sorry @divijvaidya.

No worries! I am glad to see this merged in.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants