linliu-code commented on code in PR #19998:
URL: https://github.com/apache/hudi/pull/19998#discussion_r4051101710


##########
hudi-common/src/main/java/org/apache/hudi/common/util/collection/RocksDBDAO.java:
##########
@@ -115,73 +119,95 @@ private void init() {
       managedDescriptorMap = new ConcurrentHashMap<>();
 
       // If already present, loads the existing column-family handles
-      final DBOptions dbOptions = new 
DBOptions().setCreateIfMissing(true).setCreateMissingColumnFamilies(true)
+      this.dbOptions = new 
DBOptions().setCreateIfMissing(true).setCreateMissingColumnFamilies(true)
           .setWalDir(rocksDBBasePath).setStatsDumpPeriodSec(300);
       this.statistics = new Statistics();
       dbOptions.setStatistics(statistics);
-      dbOptions.setLogger(new org.rocksdb.Logger(dbOptions) {
-        @Override
-        protected void log(InfoLogLevel infoLogLevel, String logMsg) {
-          switch (infoLogLevel) {
-            case DEBUG_LEVEL:
-              log.debug("From Rocks DB : {}", logMsg);
-              break;
-            case WARN_LEVEL:
-              log.warn("From Rocks DB : {}", logMsg);
-              break;
-            case ERROR_LEVEL:
-            case FATAL_LEVEL:
-              log.error("From Rocks DB : {}", logMsg);
-              break;
-            case HEADER_LEVEL:
-            case NUM_INFO_LOG_LEVELS:
-            case INFO_LEVEL:
-            default:
-              log.info("From Rocks DB : {}", logMsg);
-              break;
-          }
-        }
-      });
+      this.logger = new RocksDBLogger(dbOptions);
+      dbOptions.setLogger(logger);
       final List<ColumnFamilyDescriptor> managedColumnFamilies = 
loadManagedColumnFamilies(dbOptions);
       final List<ColumnFamilyHandle> managedHandles = new 
ArrayList<>(managedColumnFamilies.size());
       FileIOUtils.mkdir(new File(rocksDBBasePath));
       rocksDB = RocksDB.open(dbOptions, rocksDBBasePath, 
managedColumnFamilies, managedHandles);
       defaultWriteOptions = new 
WriteOptions().setDisableWAL(disableWALForWrites);
 
-      ValidationUtils.checkArgument(managedHandles.size() == 
managedColumnFamilies.size(),
-          "Unexpected number of handles are returned");
-      for (int index = 0; index < managedHandles.size(); index++) {
-        ColumnFamilyHandle handle = managedHandles.get(index);
-        ColumnFamilyDescriptor descriptor = managedColumnFamilies.get(index);
-        String familyNameFromHandle = fromUTF8Bytes(handle.getName());
-        String familyNameFromDescriptor = fromUTF8Bytes(descriptor.getName());
-
-        
ValidationUtils.checkArgument(familyNameFromDescriptor.equals(familyNameFromHandle),
-            "Family Handles not in order with descriptors");
-        managedHandlesMap.put(familyNameFromHandle, handle);
-        managedDescriptorMap.put(familyNameFromDescriptor, descriptor);
-      }
+      registerColumnFamilies(managedColumnFamilies, managedHandles);
     } catch (RocksDBException | IOException re) {
       log.error("Got exception opening Rocks DB instance ", re);
+      closeOnInitFailure();
       throw new HoodieException(re);
+    } catch (RuntimeException re) {
+      // The validation in registerColumnFamilies runs after RocksDB.open(), 
so this path can have
+      // an open DB to release as well.
+      closeOnInitFailure();
+      throw re;
+    }
+  }
+
+  /**
+   * Validates the handles RocksDB returned against the descriptors asked for, 
and registers them.
+   */
+  void registerColumnFamilies(List<ColumnFamilyDescriptor> 
managedColumnFamilies,
+                              List<ColumnFamilyHandle> managedHandles) throws 
RocksDBException {
+    ValidationUtils.checkArgument(managedHandles.size() == 
managedColumnFamilies.size(),
+        "Unexpected number of handles are returned");
+    for (int index = 0; index < managedHandles.size(); index++) {
+      ColumnFamilyHandle handle = managedHandles.get(index);
+      ColumnFamilyDescriptor descriptor = managedColumnFamilies.get(index);
+      String familyNameFromHandle = fromUTF8Bytes(handle.getName());
+      String familyNameFromDescriptor = fromUTF8Bytes(descriptor.getName());
+
+      
ValidationUtils.checkArgument(familyNameFromDescriptor.equals(familyNameFromHandle),
+          "Family Handles not in order with descriptors");
+      managedHandlesMap.put(familyNameFromHandle, handle);
+      managedDescriptorMap.put(familyNameFromDescriptor, descriptor);
     }
   }
 
+  /**
+   * init() runs from the constructor, so a throw leaves no reference for any 
caller to close():
+   * everything opened so far has to be released here or it outlives the 
failed DAO.
+   */
+  private void closeOnInitFailure() {
+    if (managedHandlesMap != null) {
+      
managedHandlesMap.values().forEach(AbstractImmutableNativeReference::close);
+      managedHandlesMap.clear();
+    }
+    if (managedDescriptorMap != null) {
+      managedDescriptorMap.clear();

Review Comment:
   Good catch — and already addressed in the current head (`86922bc3`); your 
comment is against `e2d1cf1f`, the revision before it.
   
   `descriptor.getOptions().close()` now runs on all three paths — `close()`, 
the init-failure cleanup, and `dropColumnFamily`, which discarded the 
descriptor while closing only the handle. Measured before the fix: `2 of 2` 
descriptor `ColumnFamilyOptions` still open after `close()`.
   
   Covered by `testCloseReleasesColumnFamilyDescriptorOptions`, 
`testDropColumnFamilyReleasesDescriptorOptions`, and a general 
`testCloseReleasesEveryReachableNativeHandle` that walks the DAO object graph 
before `close()` and requires every native handle to be released after — it 
reports the path, so this class of miss surfaces without anyone having to think 
of it:
   
   ```
   native handles still open after close(): 
[RocksDBDAO.managedDescriptorMap[default].columnFamilyOptions_ 
(ColumnFamilyOptions)]
   ```
   
   All three fail without the change.



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