You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@spark.apache.org by ka...@apache.org on 2026/08/20 03:50:35 UTC
(spark) branch branch-4.x updated: [SPARK-49442][SS][FOLLOWUP] Use monotonic clock for Kafka partition cache TTL
This is an automated email from the ASF dual-hosted git repository.
HeartSaVioR pushed a commit to branch branch-4.x
in repository https://gitbox.apache.org/repos/asf/spark.git
The following commit(s) were added to refs/heads/branch-4.x by this push:
new 130cb0714b07 [SPARK-49442][SS][FOLLOWUP] Use monotonic clock for Kafka partition cache TTL
130cb0714b07 is described below
commit 130cb0714b0794112292cab310ae07b9ad4c60e5
Author: Wenchen Fan <we...@databricks.com>
AuthorDate: Thu Aug 20 12:50:09 2026 +0900
[SPARK-49442][SS][FOLLOWUP] Use monotonic clock for Kafka partition cache TTL
### What changes were proposed in this pull request?
Followup to https://github.com/apache/spark/pull/57351.
This PR measures the Kafka partition metadata cache TTL with `System.nanoTime` instead of
`System.currentTimeMillis`. It also represents the cached timestamp with `Option[Long]` rather than
using zero as a sentinel. Debug logging continues to report cache age in milliseconds.
### Why are the changes needed?
TTL measurement should use a monotonic time source. Wall-clock adjustments can otherwise make a
cache entry appear older or younger than it is, causing premature refreshes or extending its
lifetime. `System.nanoTime` measures elapsed time without that sensitivity.
### Does this PR introduce _any_ user-facing change?
No. The cache option and its behavior under a stable clock are unchanged.
### How was this patch tested?
Existing cache reuse and expiry coverage was run with Java 17:
```
build/sbt 'sql-kafka-0-10/testOnly org.apache.spark.sql.kafka010.KafkaOffsetReaderSuite'
```
All 22 tests passed. No new test was added because the change only substitutes the JVM's monotonic
elapsed-time source; reliably simulating host wall-clock changes is not practical in this suite.
### Was this patch authored or co-authored using generative AI tooling?
Generated-by: Codex (GPT-5)
Closes #57943 from cloud-fan/SPARK-49442-followup.
Authored-by: Wenchen Fan <we...@databricks.com>
Signed-off-by: Jungtaek Lim <ka...@gmail.com>
(cherry picked from commit a37e9eafc242aa7988ceebae94e09d209c610bb7)
Signed-off-by: Jungtaek Lim <ka...@gmail.com>
---
.../sql/kafka010/KafkaOffsetReaderAdmin.scala | 29 +++++++++++++---------
1 file changed, 17 insertions(+), 12 deletions(-)
diff --git a/connector/kafka-0-10-sql/src/main/scala/org/apache/spark/sql/kafka010/KafkaOffsetReaderAdmin.scala b/connector/kafka-0-10-sql/src/main/scala/org/apache/spark/sql/kafka010/KafkaOffsetReaderAdmin.scala
index f182e156c76e..dc22a46e17bc 100644
--- a/connector/kafka-0-10-sql/src/main/scala/org/apache/spark/sql/kafka010/KafkaOffsetReaderAdmin.scala
+++ b/connector/kafka-0-10-sql/src/main/scala/org/apache/spark/sql/kafka010/KafkaOffsetReaderAdmin.scala
@@ -19,6 +19,7 @@ package org.apache.spark.sql.kafka010
import java.{util => ju}
import java.util.Locale
+import java.util.concurrent.TimeUnit
import scala.collection.mutable.ArrayBuffer
import scala.jdk.CollectionConverters._
@@ -62,7 +63,7 @@ private[kafka010] class KafkaOffsetReaderAdmin(
// Protected by this.synchronized (always accessed inside withRetries, which holds the lock).
private var cachedPartitions: Set[TopicPartition] = Set.empty
- private var cacheTimestampMs: Long = 0L
+ private var cacheTimestampNanos: Option[Long] = None
/**
* An AdminClient used in the driver to query the latest Kafka offsets.
@@ -451,16 +452,20 @@ private[kafka010] class KafkaOffsetReaderAdmin(
if (partitionMetadataCacheTtlMs <= 0) {
return consumerStrategy.assignedTopicPartitions(admin)
}
- val now = System.currentTimeMillis()
- if (cacheTimestampMs > 0 && (now - cacheTimestampMs) < partitionMetadataCacheTtlMs) {
- logDebug(s"Reusing cached partitions (age ${now - cacheTimestampMs}ms < " +
- s"${partitionMetadataCacheTtlMs}ms TTL): $cachedPartitions")
- cachedPartitions
- } else {
- val fresh = consumerStrategy.assignedTopicPartitions(admin)
- cachedPartitions = fresh
- cacheTimestampMs = now
- fresh
+ val nowNanos = System.nanoTime()
+ cacheTimestampNanos match {
+ case Some(timestampNanos)
+ if nowNanos - timestampNanos <
+ TimeUnit.MILLISECONDS.toNanos(partitionMetadataCacheTtlMs) =>
+ val cacheAgeMs = TimeUnit.NANOSECONDS.toMillis(nowNanos - timestampNanos)
+ logDebug(s"Reusing cached partitions (age ${cacheAgeMs}ms < " +
+ s"${partitionMetadataCacheTtlMs}ms TTL): $cachedPartitions")
+ cachedPartitions
+ case _ =>
+ val fresh = consumerStrategy.assignedTopicPartitions(admin)
+ cachedPartitions = fresh
+ cacheTimestampNanos = Some(nowNanos)
+ fresh
}
}
@@ -519,6 +524,6 @@ private[kafka010] class KafkaOffsetReaderAdmin(
stopAdmin()
_admin = null // will automatically get reinitialized again
cachedPartitions = Set.empty
- cacheTimestampMs = 0L
+ cacheTimestampNanos = None
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@spark.apache.org
For additional commands, e-mail: commits-help@spark.apache.org