bbeaudreault commented on code in PR #5037:
URL: https://github.com/apache/hbase/pull/5037#discussion_r1110042305


##########
hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncNonMetaRegionLocator.java:
##########
@@ -299,37 +299,80 @@ private RegionLocations addToCache(TableCache tableCache, 
RegionLocations locs)
     LOG.trace("Try adding {} to cache", locs);
     byte[] startKey = locs.getRegionLocation().getRegion().getStartKey();
     for (;;) {
-      RegionLocations oldLocs = tableCache.cache.putIfAbsent(startKey, locs);
-      if (oldLocs == null) {
-        return locs;
-      }
-      // check whether the regions are the same, this usually happens when 
table is split/merged, or
-      // deleted and recreated again.
-      RegionInfo region = locs.getRegionLocation().getRegion();
-      RegionInfo oldRegion = oldLocs.getRegionLocation().getRegion();
-      if (region.getEncodedName().equals(oldRegion.getEncodedName())) {
-        RegionLocations mergedLocs = oldLocs.mergeLocations(locs);
-        if (isEqual(mergedLocs, oldLocs)) {
-          // the merged one is the same with the old one, give up
-          LOG.trace("Will not add {} to cache because the old value {} "
-            + " is newer than us or has the same server name."
-            + " Maybe it is updated before we replace it", locs, oldLocs);
-          return oldLocs;
-        }
-        if (tableCache.cache.replace(startKey, oldLocs, mergedLocs)) {
-          return mergedLocs;
-        }
-      } else {
-        // the region is different, here we trust the one we fetched. This 
maybe wrong but finally
-        // the upper layer can detect this and trigger removal of the wrong 
locations
-        if (LOG.isDebugEnabled()) {
-          LOG.debug("The newnly fetch region {} is different from the old one 
{} for row '{}',"
-            + " try replaing the old one...", region, oldRegion, 
Bytes.toStringBinary(startKey));
-        }
-        if (tableCache.cache.replace(startKey, oldLocs, locs)) {
+      // synchronize here because we may need to make multiple modifications in
+      // cleanOverlappingRegions, and we want them to be atomic
+      synchronized (tableCache) {
+        RegionLocations oldLocs = tableCache.cache.putIfAbsent(startKey, locs);
+        if (oldLocs == null) {
+          cleanOverlappingRegions(locs, tableCache);
           return locs;
         }
+        // check whether the regions are the same, this usually happens when 
table is split/merged,
+        // or
+        // deleted and recreated again.
+        RegionInfo region = locs.getRegionLocation().getRegion();
+        RegionInfo oldRegion = oldLocs.getRegionLocation().getRegion();
+        if (region.getEncodedName().equals(oldRegion.getEncodedName())) {
+          RegionLocations mergedLocs = oldLocs.mergeLocations(locs);
+          if (isEqual(mergedLocs, oldLocs)) {
+            // the merged one is the same with the old one, give up
+            LOG.trace("Will not add {} to cache because the old value {} "
+              + " is newer than us or has the same server name."
+              + " Maybe it is updated before we replace it", locs, oldLocs);
+            return oldLocs;
+          }
+          if (tableCache.cache.replace(startKey, oldLocs, mergedLocs)) {
+            cleanOverlappingRegions(locs, tableCache);
+            return mergedLocs;
+          }
+        } else {
+          // the region is different, here we trust the one we fetched. This 
maybe wrong but finally
+          // the upper layer can detect this and trigger removal of the wrong 
locations
+          if (LOG.isDebugEnabled()) {
+            LOG.debug(
+              "The newly fetch region {} is different from the old one {} for 
row '{}',"
+                + " try replaying the old one...",
+              region, oldRegion, Bytes.toStringBinary(startKey));
+          }
+          if (tableCache.cache.replace(startKey, oldLocs, locs)) {
+            cleanOverlappingRegions(locs, tableCache);
+            return locs;
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   * When caching a location, the region may have been the result of a merge. 
Check to see if the
+   * region's boundaries overlap any other cached locations. Those would have 
been merge parents
+   * which no longer exist. We need to proactively clear them out to avoid a 
case where a merged
+   * region which receives no requests never gets cleared. This causes 
requests to other merged
+   * regions after it to see the wrong cached location. See HBASE-27650
+   * @param locations  the new location that was just cached
+   * @param tableCache the tableCache containing that and other locations for 
this table.
+   */
+  private void cleanOverlappingRegions(RegionLocations locations, TableCache 
tableCache) {
+    RegionInfo region = locations.getRegionLocation().getRegion();
+
+    boolean isLast = Bytes.equals(region.getEndKey(), 
HConstants.EMPTY_END_ROW);
+
+    while (true) {
+      Map.Entry<byte[], RegionLocations> overlap =

Review Comment:
   Another option here would have been to iterate a `subMap`. I felt this 
approach was better because merges are rare and in almost all cases we'll exit 
here after just 1 floorEntry/lastEntry call and 1 reference equality check. 
Using a subMap requires at least 2 comparator comparisons, to get the head and 
tail of the subMap.



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