You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@accumulo.apache.org by kt...@apache.org on 2018/07/13 20:04:19 UTC

[accumulo] branch 1.9 updated: fixes #558 use copy to avoid deadlock in tserver (#559)

This is an automated email from the ASF dual-hosted git repository.

kturner pushed a commit to branch 1.9
in repository https://gitbox.apache.org/repos/asf/accumulo.git


The following commit(s) were added to refs/heads/1.9 by this push:
     new 8519514  fixes #558 use copy to avoid deadlock in tserver (#559)
8519514 is described below

commit 8519514e4798f4c7dc694f2b007a78f1b4adc3b1
Author: Keith Turner <ke...@deenlo.com>
AuthorDate: Fri Jul 13 16:04:17 2018 -0400

    fixes #558 use copy to avoid deadlock in tserver (#559)
---
 .../org/apache/accumulo/tserver/tablet/Tablet.java | 64 ++++++++++++++--------
 1 file changed, 42 insertions(+), 22 deletions(-)

diff --git a/server/tserver/src/main/java/org/apache/accumulo/tserver/tablet/Tablet.java b/server/tserver/src/main/java/org/apache/accumulo/tserver/tablet/Tablet.java
index 4590872..4dc19f2 100644
--- a/server/tserver/src/main/java/org/apache/accumulo/tserver/tablet/Tablet.java
+++ b/server/tserver/src/main/java/org/apache/accumulo/tserver/tablet/Tablet.java
@@ -154,8 +154,11 @@ import org.apache.zookeeper.KeeperException.NoNodeException;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
 import com.google.common.cache.Cache;
 import com.google.common.cache.CacheBuilder;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSet.Builder;
 
 /**
  *
@@ -495,6 +498,8 @@ public class Tablet implements TabletCommitter {
             logEntry.getColumnQualifier().toString()));
       }
 
+      rebuildReferencedLogs();
+
       log.info(
           "Write-Ahead Log recovery complete for " + this.extent + " (" + entriesUsedOnTablet.get()
               + " mutations applied, " + getTabletMemory().getNumEntries() + " entries created)");
@@ -950,6 +955,8 @@ public class Tablet implements TabletCommitter {
 
   private synchronized MinorCompactionTask prepareForMinC(long flushId,
       MinorCompactionReason mincReason) {
+    Preconditions.checkState(otherLogs.isEmpty());
+    Preconditions.checkState(referencedLogs.equals(currentLogs));
     CommitSession oldCommitSession = getTabletMemory().prepareForMinC();
     otherLogs = currentLogs;
     currentLogs = new HashSet<>();
@@ -1524,9 +1531,9 @@ public class Tablet implements TabletCommitter {
 
     }
 
-    if (otherLogs.size() != 0 || currentLogs.size() != 0) {
+    if (otherLogs.size() != 0 || currentLogs.size() != 0 || referencedLogs.size() != 0) {
       String msg = "Closed tablet " + extent + " has walog entries in memory currentLogs = "
-          + currentLogs + "  otherLogs = " + otherLogs;
+          + currentLogs + "  otherLogs = " + otherLogs + " refererncedLogs = " + referencedLogs;
       log.error(msg);
       throw new RuntimeException(msg);
     }
@@ -2457,19 +2464,25 @@ public class Tablet implements TabletCommitter {
   }
 
   private Set<DfsLogger> currentLogs = new HashSet<>();
+  private Set<DfsLogger> otherLogs = Collections.emptySet();
+
+  // An immutable copy of currentLogs + otherLogs. This exists so that removeInUseLogs() does not
+  // have to get the tablet lock. See #558
+  private volatile Set<DfsLogger> referencedLogs = Collections.emptySet();
+
+  private synchronized void rebuildReferencedLogs() {
+    Builder<DfsLogger> builder = ImmutableSet.builder();
+    builder.addAll(currentLogs);
+    builder.addAll(otherLogs);
+    referencedLogs = builder.build();
+  }
 
   public void removeInUseLogs(Set<DfsLogger> candidates) {
-    synchronized (this) {
-      // remove logs related to minor compacting data
-      candidates.removeAll(otherLogs);
-      // remove logs related to tablets in memory data
-      candidates.removeAll(currentLogs);
-      // remove logs related to minor compaction file being added to the metadata table
-      candidates.removeAll(doomedLogs);
-    }
+    candidates.removeAll(referencedLogs);
   }
 
   Set<String> beginClearingUnusedLogs() {
+    Set<String> unusedLogs = new HashSet<>();
 
     ArrayList<String> otherLogsCopy = new ArrayList<>();
     ArrayList<String> currentLogsCopy = new ArrayList<>();
@@ -2478,20 +2491,26 @@ public class Tablet implements TabletCommitter {
     logLock.lock();
 
     synchronized (this) {
-      if (doomedLogs.size() > 0)
+      if (removingLogs)
         throw new IllegalStateException("Attempted to clear logs when removal of logs in progress");
 
       for (DfsLogger logger : otherLogs) {
         otherLogsCopy.add(logger.toString());
-        doomedLogs.add(logger);
+        unusedLogs.add(logger.getMeta());
       }
 
       for (DfsLogger logger : currentLogs) {
         currentLogsCopy.add(logger.toString());
-        doomedLogs.remove(logger);
+        unusedLogs.remove(logger.getMeta());
       }
 
       otherLogs = Collections.emptySet();
+      // Do NOT call rebuildReferenedLogs() here as that could cause walogs to be GCed before the
+      // minc rfile is written to metadata table (see #539). The clearing of otherLogs is reflected
+      // in refererncedLogs when finishClearingUnusedLogs() calls rebuildReferenedLogs().
+
+      if (unusedLogs.size() > 0)
+        removingLogs = true;
     }
 
     // do debug logging outside tablet lock
@@ -2503,23 +2522,20 @@ public class Tablet implements TabletCommitter {
       log.debug("Logs for current memory: " + getExtent() + " " + logger);
     }
 
-    Set<String> doomed = new HashSet<>();
-    for (DfsLogger logger : doomedLogs) {
-      log.debug("Logs to be destroyed: " + getExtent() + " " + logger.getMeta());
-      doomed.add(logger.getMeta());
+    for (String logger : unusedLogs) {
+      log.debug("Logs to be destroyed: " + getExtent() + " " + logger);
     }
 
-    return doomed;
+    return unusedLogs;
   }
 
   synchronized void finishClearingUnusedLogs() {
-    doomedLogs.clear();
+    removingLogs = false;
+    rebuildReferencedLogs();
     logLock.unlock();
   }
 
-  private Set<DfsLogger> otherLogs = Collections.emptySet();
-  // logs related to minor compaction file being added to the metadata table
-  private Set<DfsLogger> doomedLogs = new HashSet<>();
+  private boolean removingLogs = false;
 
   // this lock is basically used to synchronize writing of log info to metadata
   private final ReentrantLock logLock = new ReentrantLock();
@@ -2583,6 +2599,10 @@ public class Tablet implements TabletCommitter {
             numContained++;
         }
 
+        if (numAdded > 0) {
+          rebuildReferencedLogs();
+        }
+
         if (numAdded > 0 && numAdded != 1) {
           // expect to add all or none
           throw new IllegalArgumentException(