Copilot commented on code in PR #8599:
URL: https://github.com/apache/hbase/pull/8599#discussion_r4029735272


##########
hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/AssignmentManagerUtil.java:
##########
@@ -294,10 +303,72 @@ static void removeNonDefaultReplicas(MasterProcedureEnv 
env, Stream<RegionInfo>
   }
 
   static void checkClosedRegion(MasterProcedureEnv env, RegionInfo regionInfo) 
throws IOException {
-    if (WALSplitUtil.hasRecoveredEdits(env.getMasterConfiguration(), 
regionInfo)) {
-      throw new IOException("Recovered.edits are found in Region: " + 
regionInfo
-        + ", abort split/merge to prevent data loss");
+    if (!WALSplitUtil.hasRecoveredEdits(env.getMasterConfiguration(), 
regionInfo)) {
+      return;
     }
+    // Robustness: corner cases can leave behind recovered.edits whose max 
seqid is already
+    // covered by the region's durable seqid. Drop those and proceed instead 
of aborting.
+    if (tryDropStaleRecoveredEdits(env, regionInfo)) {
+      return;
+    }
+    throw new IOException("Recovered.edits are found in Region: " + regionInfo
+      + ", abort split/merge to prevent data loss");
+  }
+
+  /**
+   * Try to remove recovered.edits files that are provably below the region's 
last flushed seqid.
+   * @return true if, after cleanup, no recovered.edits remain for the region
+   */
+  private static boolean tryDropStaleRecoveredEdits(MasterProcedureEnv env, 
RegionInfo regionInfo) {
+    long durableSeqId = env.getMasterServices().getServerManager()
+      
.getLastFlushedSequenceId(regionInfo.getEncodedNameAsBytes()).getLastFlushedSequenceId();
+    if (durableSeqId <= 0L) {
+      // No authoritative durability info at the master; play safe and let the 
caller abort.
+      return false;
+    }
+    try {
+      Configuration conf = env.getMasterConfiguration();
+      Path regionWALDir =
+        CommonFSUtils.getWALRegionDir(conf, regionInfo.getTable(), 
regionInfo.getEncodedName());
+      Path regionDir = 
FSUtils.getRegionDirFromRootDir(CommonFSUtils.getRootDir(conf), regionInfo);
+      Path wrongRegionWALDir = CommonFSUtils.getWrongWALRegionDir(conf, 
regionInfo.getTable(),
+        regionInfo.getEncodedName());
+      FileSystem walFs = CommonFSUtils.getWALFileSystem(conf);
+      FileSystem rootFs = CommonFSUtils.getRootDirFileSystem(conf);
+      return dropStaleEditsUnder(walFs, regionWALDir, durableSeqId, regionInfo)
+        && dropStaleEditsUnder(rootFs, regionDir, durableSeqId, regionInfo)
+        && dropStaleEditsUnder(walFs, wrongRegionWALDir, durableSeqId, 
regionInfo);
+    } catch (IOException e) {
+      LOG.warn("Failed to inspect recovered.edits for {}; falling back to 
abort", regionInfo, e);
+      return false;
+    }
+  }
+
+  private static boolean dropStaleEditsUnder(FileSystem fs, Path regionDir, 
long durableSeqId,
+    RegionInfo regionInfo) throws IOException {
+    NavigableSet<Path> files = WALSplitUtil.getSplitEditFilesSorted(fs, 
regionDir);
+    if (files.isEmpty()) {
+      return true;
+    }
+    for (Path p : files) {
+      // getSplitEditFilesSorted restricts filenames to 
WALSplitUtil.EDITFILES_NAME_PATTERN
+      // (`-?[0-9]+`), so parseLong cannot throw here.
+      long fileMaxSeqId = Long.parseLong(p.getName());

Review Comment:
   `EDITFILES_NAME_PATTERN` only restricts the filename to digits; it does not 
guarantee that the value fits in a `long`. An oversized numeric filename 
therefore reaches this line and throws an unchecked `NumberFormatException`, 
bypassing the intended safe fallback and failing the split/merge procedure 
unexpectedly. Treat an unparseable sequence ID as non-stale and return `false`.



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