From 10456330927c32527ca7e4aeb5ee4a58951c75cf Mon Sep 17 00:00:00 2001 From: Jorge Esteban Quilcate Otoya Date: Wed, 15 Jul 2026 01:16:33 +0300 Subject: [PATCH 1/3] fix(inkless:ci): update upstream tests for diskless additions Three upstream Kafka unit tests broke on main because they were not adapted to Inkless changes: - TopicImageNodeTest.testChildPartitionId: PartitionRegistration.toString now includes the diskless fields (classicToDisklessStartOffset, disklessProducerStates, disklessLeaderEpoch); update expected string. - NodeApiVersionsTest.testUnknownApiVersionsToString: the "unknown" apiKey 337 no longer sorts last once the diskless client APIs (500/501) exist, so toString no longer ends with it; use Short.MAX_VALUE instead. - MetricsTest.testGeneralBrokerTopicMetricsAreGreedilyRegistered: all-topics BytesIn/BytesOut are registered for both topicType=classic and diskless. The diskless variants share a metric name with their classic counterparts, so they are extra JMX MBeans but not extra metricMapKeySet() entries; account for the diskless-tagged duplicates in the assertion. Test-only changes; no production code touched. --- .../org/apache/kafka/clients/NodeApiVersionsTest.java | 6 ++++-- .../test/scala/unit/kafka/metrics/MetricsTest.scala | 11 +++++++++-- .../apache/kafka/image/node/TopicImageNodeTest.java | 3 ++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/clients/src/test/java/org/apache/kafka/clients/NodeApiVersionsTest.java b/clients/src/test/java/org/apache/kafka/clients/NodeApiVersionsTest.java index 8b22bc7963d..6b3ea33b494 100644 --- a/clients/src/test/java/org/apache/kafka/clients/NodeApiVersionsTest.java +++ b/clients/src/test/java/org/apache/kafka/clients/NodeApiVersionsTest.java @@ -58,8 +58,10 @@ public void testUnsupportedVersionsToString() { @Test public void testUnknownApiVersionsToString() { - NodeApiVersions versions = NodeApiVersions.create((short) 337, (short) 0, (short) 1); - assertTrue(versions.toString().endsWith("UNKNOWN(337): 0 to 1)")); + // Inkless: use an id above all real ApiKeys (incl. diskless keys 500/501) so that the + // unknown key sorts last in NodeApiVersions.toString()'s ascending-by-id output. + NodeApiVersions versions = NodeApiVersions.create(Short.MAX_VALUE, (short) 0, (short) 1); + assertTrue(versions.toString().endsWith("UNKNOWN(" + Short.MAX_VALUE + "): 0 to 1)")); } @Test diff --git a/core/src/test/scala/unit/kafka/metrics/MetricsTest.scala b/core/src/test/scala/unit/kafka/metrics/MetricsTest.scala index 5d13fc97bfb..89dac4f6219 100644 --- a/core/src/test/scala/unit/kafka/metrics/MetricsTest.scala +++ b/core/src/test/scala/unit/kafka/metrics/MetricsTest.scala @@ -139,8 +139,15 @@ class MetricsTest extends KafkaServerTestHarness with Logging { createTopic(topic, 2) // The broker metrics for all topics should be greedily registered - assertTrue(topicMetrics(None).nonEmpty, "General topic metrics don't exist") - assertEquals(brokers.head.brokerTopicStats.allTopicsStats.metricMapKeySet().size, topicMetrics(None).size) + val allTopicsMetrics = topicMetrics(None) + assertTrue(allTopicsMetrics.nonEmpty, "General topic metrics don't exist") + // Inkless: all-topics BytesIn/BytesOut meters are registered for both topicType=classic and + // topicType=diskless. The diskless variants share the same metric name as their classic + // counterparts (they differ only by tag), so they are not tracked as extra keys in + // metricMapKeySet() but do appear as separate JMX MBeans. Account for those duplicates. + val disklessAllTopicsMetrics = allTopicsMetrics.count(_.contains("topicType=diskless")) + assertEquals(brokers.head.brokerTopicStats.allTopicsStats.metricMapKeySet().size + disklessAllTopicsMetrics, + allTopicsMetrics.size) assertEquals(0, brokers.head.brokerTopicStats.allTopicsStats.metricGaugeMap.size) // topic metrics should be lazily registered assertTrue(topicMetricGroups(topic).isEmpty, "Topic metrics aren't lazily registered") diff --git a/metadata/src/test/java/org/apache/kafka/image/node/TopicImageNodeTest.java b/metadata/src/test/java/org/apache/kafka/image/node/TopicImageNodeTest.java index 84d70cd90c1..6d13c80937f 100644 --- a/metadata/src/test/java/org/apache/kafka/image/node/TopicImageNodeTest.java +++ b/metadata/src/test/java/org/apache/kafka/image/node/TopicImageNodeTest.java @@ -83,7 +83,8 @@ public void testChildPartitionId() { assertEquals("PartitionRegistration(replicas=[2, 3, 4], " + "directories=[AAAAAAAAAAAAAAAAAAAAAA, AAAAAAAAAAAAAAAAAAAAAA, AAAAAAAAAAAAAAAAAAAAAA], " + "isr=[2, 3], removingReplicas=[], addingReplicas=[], elr=[], lastKnownElr=[], leader=2, " + - "leaderRecoveryState=RECOVERED, leaderEpoch=1, partitionEpoch=345)", stringifier.toString()); + "leaderRecoveryState=RECOVERED, leaderEpoch=1, partitionEpoch=345, " + + "classicToDisklessStartOffset=-1, disklessProducerStates=[], disklessLeaderEpoch=-1)", stringifier.toString()); } @Test From 61fe0261df57c08c173a03fce20c055d9d2e0ecc Mon Sep 17 00:00:00 2001 From: Jorge Esteban Quilcate Otoya Date: Wed, 15 Jul 2026 15:17:21 +0300 Subject: [PATCH 2/3] test(inkless:ci): quarantine known-flaky upstream integration tests with @Flaky These integration tests are documented-flaky on upstream Apache Kafka (several with currently-open flaky-test JIRA tickets naming the exact methods/params) and reddened the noflaky-nonew CI bucket independent of any Inkless change. Tag them @Flaky so they are excluded from the main suite and run only in the flaky bucket (with retries), while tracking the upstream tickets. MM2 integration family (tagged once on the base methods in MirrorConnectorsIntegrationBaseTest; inherited by IdentityReplication, ExactlyOnce, SSL, Transactions, CustomForwardingAdmin): - testReplicateSourceDefault -> KAFKA-15926 (open; also KAFKA-15927) - testReplicateTargetDefault -> KAFKA-15926 (no dedicated ticket; same harness) - testSyncTopicConfigs -> KAFKA-15945 (also KAFKA-15523, KAFKA-14971) - testReplication -> KAFKA-16488 (also KAFKA-12566) Standalone: - MonitorableSinkIntegrationTest.testMonitorableSinkConnectorAndTask -> KAFKA-18952 - TaskAssignorConvergenceTest.randomClusterPerturbationsShouldConverge -> KAFKA-16491 (open) - ShareConsumerTest.testPollThrowsInterruptExceptionIfInterrupted -> KAFKA-19961 (open) - SaslOAuthBearerSslEndToEndAuthorizationTest.testNoConsumeWithoutDescribeAclViaSubscribe -> KAFKA-9655; overridden solely to tag the OAUTHBEARER variant so other SASL mechanisms keep upstream coverage. build.gradle: add testImplementation test-common-util to :streams and :connect:mirror (source of the @Flaky annotation; the other affected modules already depend on it). Verified: compile + checkstyle pass for all touched modules, and the flaky filter excludes the tagged tests from the noflaky bucket (inherited MM2 method and the SASL override both report "No tests found" in default mode). --- build.gradle | 2 ++ .../kafka/clients/consumer/ShareConsumerTest.java | 2 ++ .../MirrorConnectorsIntegrationBaseTest.java | 5 +++++ .../integration/MonitorableSinkIntegrationTest.java | 2 ++ .../SaslOAuthBearerSslEndToEndAuthorizationTest.scala | 11 +++++++++++ .../assignment/TaskAssignorConvergenceTest.java | 2 ++ 6 files changed, 24 insertions(+) diff --git a/build.gradle b/build.gradle index c9910764c65..bf46d7a4dba 100644 --- a/build.gradle +++ b/build.gradle @@ -3027,6 +3027,7 @@ project(':streams') { testCompileOnly libs.bndlib testImplementation project(':clients').sourceSets.test.output + testImplementation project(':test-common:test-common-util') testImplementation libs.jacksonDataformatYaml testImplementation libs.junitJupiter testImplementation libs.bcpkix @@ -4192,6 +4193,7 @@ project(':connect:mirror') { testImplementation project(':connect:runtime').sourceSets.test.output testImplementation project(':core') testImplementation project(':test-common:test-common-runtime') + testImplementation project(':test-common:test-common-util') testImplementation project(':server') testImplementation project(':server-common') diff --git a/clients/clients-integration-tests/src/test/java/org/apache/kafka/clients/consumer/ShareConsumerTest.java b/clients/clients-integration-tests/src/test/java/org/apache/kafka/clients/consumer/ShareConsumerTest.java index 5ce121fc602..184e5a36f67 100644 --- a/clients/clients-integration-tests/src/test/java/org/apache/kafka/clients/consumer/ShareConsumerTest.java +++ b/clients/clients-integration-tests/src/test/java/org/apache/kafka/clients/consumer/ShareConsumerTest.java @@ -62,6 +62,7 @@ import org.apache.kafka.common.test.api.ClusterConfigProperty; import org.apache.kafka.common.test.api.ClusterTest; import org.apache.kafka.common.test.api.ClusterTestDefaults; +import org.apache.kafka.common.test.api.Flaky; import org.apache.kafka.common.test.api.Type; import org.apache.kafka.common.utils.Utils; import org.apache.kafka.coordinator.group.GroupConfig; @@ -1716,6 +1717,7 @@ public void onComplete(Map> offsetsMap, Exception ex * causes it to throw InterruptException */ @ClusterTest + @Flaky(value = "KAFKA-19961", comment = "Flaky ShareConsumerTest#testPollThrowsInterruptExceptionIfInterrupted.") public void testPollThrowsInterruptExceptionIfInterrupted() { alterShareAutoOffsetReset("group1", "earliest"); try (ShareConsumer shareConsumer = createShareConsumer("group1")) { diff --git a/connect/mirror/src/test/java/org/apache/kafka/connect/mirror/integration/MirrorConnectorsIntegrationBaseTest.java b/connect/mirror/src/test/java/org/apache/kafka/connect/mirror/integration/MirrorConnectorsIntegrationBaseTest.java index 6d1d50f558b..fd22ac16105 100644 --- a/connect/mirror/src/test/java/org/apache/kafka/connect/mirror/integration/MirrorConnectorsIntegrationBaseTest.java +++ b/connect/mirror/src/test/java/org/apache/kafka/connect/mirror/integration/MirrorConnectorsIntegrationBaseTest.java @@ -35,6 +35,7 @@ import org.apache.kafka.common.TopicPartition; import org.apache.kafka.common.config.ConfigResource; import org.apache.kafka.common.config.TopicConfig; +import org.apache.kafka.common.test.api.Flaky; import org.apache.kafka.common.utils.Exit; import org.apache.kafka.common.utils.Time; import org.apache.kafka.common.utils.Timer; @@ -280,6 +281,7 @@ public void shutdownClusters() throws Exception { } @Test + @Flaky(value = "KAFKA-16488", comment = "Chronically flaky MM2 integration test across all subclasses; see also KAFKA-12566.") public void testReplication() throws Exception { produceMessages(primaryProducer, "test-topic-1"); String backupTopic1 = remoteTopicName("test-topic-1", PRIMARY_CLUSTER_ALIAS); @@ -816,6 +818,7 @@ private Map partialOffsets(ConsumerRecords Date: Wed, 15 Jul 2026 16:46:18 +0300 Subject: [PATCH 3/3] fix(inkless:ci): make the flaky test job report-only so CI is trustable The flaky/quarantine matrix job runs only @Flaky-tagged tests (known-unreliable upstream tests, e.g. the MM2 config-sync integration tests that pass in isolation but flake under CI load). Previously junit.py failed the job on any non-zero Gradle exit code, so a flaked quarantined test turned the whole workflow red even though nothing at the Kafka coverage level was broken - defeating the point of quarantine and making the signal untrustworthy. Make junit.py treat a non-zero Gradle exit code as non-fatal when running the flaky job (RUN_FLAKY=true), while still failing on real infrastructure problems: timeouts (exit 124), thread dumps (hangs), and a missing exit code. Flaky failures are still reported in the job summary tables. The noflaky and new jobs are unaffected and remain the trustable gating signal. Net effect: the workflow is green unless a non-flaky test breaks or infra fails. Verified junit.py exit behavior locally: - flaky + test failures -> exit 0 (green) - noflaky + test failures -> exit 1 (red) - flaky + timeout (124) -> exit 1 (infra, still red) - clean / unset RUN_FLAKY -> unchanged --- .github/scripts/junit.py | 7 ++++++- .github/workflows/build.yml | 3 +++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/scripts/junit.py b/.github/scripts/junit.py index 550ea493511..e0d80581d5e 100644 --- a/.github/scripts/junit.py +++ b/.github/scripts/junit.py @@ -347,6 +347,11 @@ def split_report_path(base_path: str, report_path: str) -> Tuple[str, str]: exit_code = get_env("GRADLE_TEST_EXIT_CODE", int) junit_report_url = get_env("JUNIT_REPORT_URL") thread_dump_url = get_env("THREAD_DUMP_URL") + # Inkless: in the flaky/quarantine test job, tests fail by definition (they are @Flaky-tagged + # and known unreliable). Their failures must not fail the job, otherwise the workflow shows a + # red signal even when nothing at the Kafka coverage level is broken. Real infrastructure + # problems (timeout=124, thread dumps, missing exit code) still fail the job below. + run_flaky = (get_env("RUN_FLAKY") or "").lower() == "true" if exit_code is None: failure_messages.append("Missing required GRADLE_TEST_EXIT_CODE environment variable. Failing this script.") @@ -357,7 +362,7 @@ def split_report_path(base_path: str, report_path: str) -> Tuple[str, str]: # this script. If any task fails due to timeout, we want to fail the overall build since it will not # include all the test results failure_messages.append(f"Gradle task had a timeout. Failing this script. These are partial results!") - elif exit_code > 0: + elif exit_code > 0 and not run_flaky: failure_messages.append(f"Gradle task had a failure exit code. Failing this script.") if thread_dump_url: diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b67eca69017..210ea12e2aa 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -273,6 +273,9 @@ jobs: JUNIT_REPORT_URL: ${{ steps.archive-junit-html.outputs.artifact-url }} THREAD_DUMP_URL: ${{ steps.archive-thread-dump.outputs.artifact-url }} GRADLE_TEST_EXIT_CODE: ${{ steps.junit-test.outputs.gradle-exitcode }} + # Inkless: flaky-test failures in the flaky job are expected and must not fail the job, + # so the workflow only goes red when something at the Kafka coverage level actually breaks. + RUN_FLAKY: ${{ matrix.run-flaky }} run: | python .github/scripts/junit.py \ --path build/junit-xml >> $GITHUB_STEP_SUMMARY