waterWang opened a new pull request, #19584:
URL: https://github.com/apache/hudi/pull/19584

   ### Bug
   
   Under concurrent multi-table writes from a single Spark driver JVM with 
`hoodie.metrics.on=true`, `LocalRegistry.getCounter()` can return null when a 
racing `clear()` lands between the `containsKey` check and the `get` call, 
causing a `NullPointerException` mid-commit (inside `DataFileWriter.close()`). 
The exception leaves a **truncated completed-instant metadata file** that 
permanently breaks the table.
   
   ### Root Cause
   
   `LocalRegistry.getCounter()` uses `containsKey` + `put` + `get` — a 
non-atomic check-then-act. `clear()` is unsynchronized and can race with 
`getCounter()`:
   
   ```java
   private synchronized Counter getCounter(String name) {
       if (!counters.containsKey(name)) {  // Thread A: passes
           counters.put(name, new Counter());
       }
       return counters.get(name);  // Thread A: clear() from Thread B hit 
between check and get → null
   }
   ```
   
   ### Fix
   
   Replace the check-then-act with `ConcurrentHashMap.computeIfAbsent()`, which 
is atomic and never returns null:
   
   ```java
   private Counter getCounter(String name) {
       return counters.computeIfAbsent(name, k -> new Counter());
   }
   ```
   
   This also removes the `synchronized` lock on the hot path (every FS write in 
the JVM calls `getCounter()`), since `computeIfAbsent` handles concurrency 
natively.
   
   ### Stack trace
   
   ```
   java.lang.NullPointerException: Cannot invoke 
"org.apache.hudi.common.metrics.Counter.add(long)"
     because the return value of 
"org.apache.hudi.common.metrics.LocalRegistry.getCounter(String)" is null
        at 
org.apache.hudi.common.metrics.LocalRegistry.add(LocalRegistry.java:48)
        at 
org.apache.hudi.hadoop.fs.HoodieWrapperFileSystem.executeFuncWithTimeAndByteMetrics(HoodieWrapperFileSystem.java:133)
        at 
org.apache.hudi.hadoop.fs.SizeAwareFSDataOutputStream.write(SizeAwareFSDataOutputStream.java:59)
        at 
org.apache.avro.file.DataFileWriter$BufferedFileOutputStream$PositionFilter.write(DataFileWriter.java:485)
        at org.apache.avro.file.DataFileWriter.close(DataFileWriter.java:469)
        at 
org.apache.hudi.common.table.timeline.CommitMetadataSerDe.lambda$getInstantWriter$0(CommitMetadataSerDe.java:52)
        at 
org.apache.hudi.storage.HoodieStorage.createImmutableFileInPath(HoodieStorage.java:349)
   ```
   
   Fixes #19570


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