gemini-code-assist[bot] commented on code in PR #39285:
URL: https://github.com/apache/beam/pull/39285#discussion_r3560776001


##########
sdks/java/io/kafka/src/main/java/org/apache/beam/sdk/io/kafka/ReadFromKafkaDoFn.java:
##########
@@ -687,7 +634,7 @@ public ProcessContinuation processElement(
 
         final long estimatedBacklogBytes =
             (long)
-                (BigDecimal.valueOf(latestOffsetEstimator.estimate())
+                (BigDecimal.valueOf(latestOffsetEstimator.get())
                         .subtract(BigDecimal.valueOf(expectedOffset), 
MathContext.DECIMAL128)
                         .doubleValue()
                     * avgRecordSize.get());

Review Comment:
   ![high](https://www.gstatic.com/codereviewagent/high-priority.svg)
   
   If `latestOffsetEstimator` is still `Long.MIN_VALUE` (e.g., if the first 
poll has not completed or failed to update the estimator), calculating the 
backlog using `Long.MIN_VALUE` will result in a huge negative backlog value. 
This can severely disrupt the runner's autoscaling and progress tracking. A 
defensive check should be added to default the backlog to `0` if the estimate 
is unknown.
   
   ```java
           final long estimate = latestOffsetEstimator.get();
           final long estimatedBacklogBytes =
               estimate == Long.MIN_VALUE
                   ? 0L
                   : (long)
                       (BigDecimal.valueOf(estimate)
                               .subtract(BigDecimal.valueOf(expectedOffset), 
MathContext.DECIMAL128)
                               .doubleValue()
                           * avgRecordSize.get());
   ```



##########
sdks/java/io/kafka/src/main/java/org/apache/beam/sdk/io/kafka/ReadFromKafkaDoFn.java:
##########
@@ -510,9 +449,10 @@ public UnsplittableRestrictionTracker<OffsetRange, Long> 
restrictionTracker(
     // OffsetEstimators are cached for each topic-partition because they hold 
a stateful connection,
     // so we want to minimize the amount of connections that we start and 
track with Kafka. Another
     // point is that it has a memoized backlog, and this should make that more 
reusable estimations.
+    final AtomicLong latestOffsetEstimator =
+        latestOffsetEstimatorCache.getUnchecked(kafkaSourceDescriptor);
     return new UnsplittableRestrictionTracker<>(
-        new GrowableOffsetRangeTracker(
-            restriction.getFrom(), 
latestOffsetEstimatorCache.getUnchecked(kafkaSourceDescriptor)));
+        new GrowableOffsetRangeTracker(restriction.getFrom(), 
latestOffsetEstimator::get));

Review Comment:
   ![high](https://www.gstatic.com/codereviewagent/high-priority.svg)
   
   Since `latestOffsetEstimator` is initialized to `Long.MIN_VALUE`, the 
`GrowableOffsetRangeTracker` will receive `Long.MIN_VALUE` as the end offset 
estimate before the first poll completes. This causes `getProgress()` to cap 
the end offset to the current position and report `0` remaining work, which can 
incorrectly signal to the runner that the partition is fully processed. 
Returning `Long.MAX_VALUE` when the estimate is unknown (i.e., 
`Long.MIN_VALUE`) represents an infinite/unknown end offset, which is much 
safer and more appropriate for an unbounded source.
   
   ```java
       final AtomicLong latestOffsetEstimator =
           latestOffsetEstimatorCache.getUnchecked(kafkaSourceDescriptor);
       return new UnsplittableRestrictionTracker<>(
           new GrowableOffsetRangeTracker(
               restriction.getFrom(),
               () -> {
                 long estimate = latestOffsetEstimator.get();
                 return estimate == Long.MIN_VALUE ? Long.MAX_VALUE : estimate;
               }));
   ```



##########
runners/spark/src/test/java/org/apache/beam/runners/spark/translation/streaming/utils/EmbeddedKafkaCluster.java:
##########
@@ -114,15 +114,20 @@ public void startup() {
       properties.setProperty("offsets.topic.replication.factor", "1");
       properties.setProperty("log.flush.interval.messages", String.valueOf(1));
 
-      KafkaServerStartable broker = startBroker(properties);
+      KafkaServer broker = startBroker(properties);
 
       brokers.add(broker);
       logDirs.add(logDir);
     }
   }
 
-  private static KafkaServerStartable startBroker(Properties props) {
-    KafkaServerStartable server = new KafkaServerStartable(new 
KafkaConfig(props));
+  private static KafkaServer startBroker(Properties props) {
+    KafkaServer server =
+        new KafkaServer(
+            new KafkaConfig(props),
+            KafkaServer.$lessinit$greater$default$2(),
+            KafkaServer.$lessinit$greater$default$3(),
+            KafkaServer.$lessinit$greater$default$4());

Review Comment:
   ![medium](https://www.gstatic.com/codereviewagent/medium-priority.svg)
   
   Using Scala synthetic methods like `$lessinit$greater$default$X` for default 
arguments is extremely fragile and can break if the Scala version or compiler 
internals change. It is much more robust to pass explicit, standard arguments 
such as `Time.SYSTEM`, `Option.empty()`, and `Nil$.MODULE$`.
   
   ```suggestion
       KafkaServer server =
           new KafkaServer(
               new KafkaConfig(props),
               org.apache.kafka.common.utils.Time.SYSTEM,
               scala.Option.empty(),
               scala.collection.immutable.Nil$.MODULE$);
   ```



##########
sdks/java/io/kafka/src/main/java/org/apache/beam/sdk/io/kafka/ReadFromKafkaDoFn.java:
##########
@@ -580,6 +519,14 @@ public ProcessContinuation processElement(
         // Fetch the next records.
         final ConsumerRecords<byte[], byte[]> rawRecords = 
consumer.poll(remainingTimeout);
         final Duration elapsed = pollTimer.elapsed();
+        try {
+          final long position = consumer.position(topicPartition);
+          consumer
+              .currentLag(topicPartition)
+              .ifPresent(lag -> latestOffsetEstimator.lazySet(position + lag));
+        } catch (KafkaException e) {
+        }

Review Comment:
   ![medium](https://www.gstatic.com/codereviewagent/medium-priority.svg)
   
   Empty catch blocks violate coding standards (such as the Google Java Style 
Guide) and can hide important errors. It is highly recommended to at least log 
the exception at a debug level to aid in troubleshooting.
   
   ```suggestion
           } catch (KafkaException e) {
             LOG.debug("Failed to update end offset estimator using local 
consumer state", e);
           }
   ```
   
   <details>
   <summary>References</summary>
   
   1. Empty catch blocks are allowed only when the exception is expected and 
documented, or when it is ignored on purpose with a comment explaining why. 
<sup>([link](http://console.cloud.google.com/gemini-code-assist/agents-tools))</sup>
   </details>



##########
sdks/java/testing/kafka-service/src/test/java/org/apache/beam/sdk/testing/kafka/LocalKafka.java:
##########
@@ -20,18 +20,23 @@
 import java.nio.file.Files;
 import java.util.Properties;
 import kafka.server.KafkaConfig;
-import kafka.server.KafkaServerStartable;
+import kafka.server.KafkaServer;
 
 public class LocalKafka {
-  private final KafkaServerStartable server;
+  private final KafkaServer server;
 
   LocalKafka(int kafkaPort, int zookeeperPort) throws Exception {
     Properties kafkaProperties = new Properties();
     kafkaProperties.setProperty("port", String.valueOf(kafkaPort));
     kafkaProperties.setProperty("zookeeper.connect", 
String.format("localhost:%s", zookeeperPort));
     kafkaProperties.setProperty("offsets.topic.replication.factor", "1");
     kafkaProperties.setProperty("log.dir", 
Files.createTempDirectory("kafka-log-").toString());
-    server = new KafkaServerStartable(KafkaConfig.fromProps(kafkaProperties));
+    server =
+        new KafkaServer(
+            KafkaConfig.fromProps(kafkaProperties),
+            KafkaServer.$lessinit$greater$default$2(),
+            KafkaServer.$lessinit$greater$default$3(),
+            KafkaServer.$lessinit$greater$default$4());

Review Comment:
   ![medium](https://www.gstatic.com/codereviewagent/medium-priority.svg)
   
   Using Scala synthetic methods like `$lessinit$greater$default$X` for default 
arguments is extremely fragile and can break if the Scala version or compiler 
internals change. It is much more robust to pass explicit, standard arguments 
such as `Time.SYSTEM`, `Option.empty()`, and `Nil$.MODULE$`.
   
   ```suggestion
       server =
           new KafkaServer(
               KafkaConfig.fromProps(kafkaProperties),
               org.apache.kafka.common.utils.Time.SYSTEM,
               scala.Option.empty(),
               scala.collection.immutable.Nil$.MODULE$);
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to