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

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


The following commit(s) were added to refs/heads/2.1 by this push:
     new 92af41860e run minor compaction earlier in tablet close (#3685)
92af41860e is described below

commit 92af41860e29f448f9262b9d4d16406cafa31045
Author: Keith Turner <ktur...@apache.org>
AuthorDate: Fri Aug 11 14:13:05 2023 -0400

    run minor compaction earlier in tablet close (#3685)
    
    Closing a tablet does two minor compactions.  This commit moves the
    first minor compaction to run earlier.  The goal of this change is to
    avoid leaving tablets in a half closed state when minor compaction are
    failing.
---
 .../org/apache/accumulo/tserver/tablet/Tablet.java | 75 ++++++++++++++++------
 1 file changed, 55 insertions(+), 20 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 cfc36719e2..d0d71bf722 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
@@ -902,7 +902,62 @@ public class Tablet extends TabletBase {
     log.trace("initiateClose(saveState={}) {}", saveState, getExtent());
 
     MinorCompactionTask mct = null;
+    if (saveState) {
+      try {
+        synchronized (this) {
+          // Wait for any running minor compaction before trying to start 
another. This is done for
+          // the case where the current in memory map has a lot of data. So 
wait for the running
+          // minor compaction and then start compacting the current in memory 
map before closing.
+          getTabletMemory().waitForMinC();
+        }
+        mct = createMinorCompactionTask(getFlushID(), 
MinorCompactionReason.CLOSE);
+      } catch (NoNodeException e) {
+        throw new IllegalStateException("Exception on " + extent + " during 
prep for MinC", e);
+      }
+    }
+
+    if (mct != null) {
+      // Do an initial minor compaction that flushes any data in memory before 
marking that tablet
+      // as closed. Another minor compaction will be done once the tablet is 
marked as closed. There
+      // are two goals for this initial minor compaction.
+      //
+      // 1. Make the 2nd minor compaction that occurs after closing faster 
because it has less
+      // data. That is important because after the tablet is closed it can not 
be read or written
+      // to, so hopefully the 2nd compaction has little if any data because of 
this minor compaction
+      // that occurred before close.
+      //
+      // 2. Its possible a minor compaction may hang because of bad config or 
DFS problems. Taking
+      // this action before close can be less disruptive if it does hang. Also 
in the case where
+      // there is a bug that causes minor compaction to fail it will leave the 
tablet in a bad
+      // state. If that happens here before starting to close then it could 
leave the tablet in a
+      // more usable state than a failure that happens after the tablet starts 
to close.
+      //
+      // If 'mct' was null it means either a minor compaction was running, 
there was no data to
+      // minor compact, or the flush id was updating. In the case of flush id 
was updating, ideally
+      // this code would wait for flush id updates and then minor compact if 
needed, but that can
+      // not be done without setting the close state to closing to prevent 
flush id updates from
+      // starting. So if there is a flush id update going on it could cause no 
minor compaction
+      // here. There will still be a minor compaction after close.
+      //
+      // Its important to run the following minor compaction outside of any 
sync blocks as this
+      // could needlessly block scans. The resources needed for the minor 
compaction have already
+      // been reserved in a sync block.
+      mct.run();
+    }
+
     synchronized (this) {
+
+      if (saveState) {
+        // Wait for any running minc to finish before we start shutting things 
down in the tablet.
+        // It is possible that this function was unable to initiate a minor 
compaction above and
+        // something else did because of race conditions (because everything 
above happens before
+        // marking anything closed so normal actions could still start minor 
compactions). If
+        // something did start lets wait on it before marking things closed.
+        getTabletMemory().waitForMinC();
+      }
+
+      // This check is intentionally done later in the method because the 
check and change of the
+      // closeState variable need to be atomic, so both are done in the same 
sync block.
       if (isClosed() || isClosing()) {
         String msg = "Tablet " + getExtent() + " already " + closeState;
         throw new IllegalStateException(msg);
@@ -933,27 +988,7 @@ public class Tablet extends TabletBase {
       // calling this.wait() releases the lock, ensure things are as expected 
when the lock is
       // obtained again
       Preconditions.checkState(closeState == CloseState.CLOSING);
-
-      if (!saveState || getTabletMemory().getMemTable().getNumEntries() == 0) {
-        return;
-      }
-
-      getTabletMemory().waitForMinC();
-
-      // calling this.wait() in waitForMinC() releases the lock, ensure things 
are as expected when
-      // the lock is obtained again
-      Preconditions.checkState(closeState == CloseState.CLOSING);
-
-      try {
-        mct = prepareForMinC(getFlushID(), MinorCompactionReason.CLOSE);
-      } catch (NoNodeException e) {
-        throw new RuntimeException("Exception on " + extent + " during prep 
for MinC", e);
-      }
     }
-
-    // do minor compaction outside of synch block so that tablet can be read 
and written to while
-    // compaction runs
-    mct.run();
   }
 
   private boolean closeCompleting = false;

Reply via email to