You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@bookkeeper.apache.org by si...@apache.org on 2018/12/13 14:45:15 UTC

[bookkeeper] branch master updated: Add get method for rest endpoint gc

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

sijie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/bookkeeper.git


The following commit(s) were added to refs/heads/master by this push:
     new e05850a  Add get method for rest endpoint gc
e05850a is described below

commit e05850aad6d0cff0dcb143e90bbbedcc88b2caef
Author: Jia Zhai <ji...@users.noreply.github.com>
AuthorDate: Thu Dec 13 22:45:09 2018 +0800

    Add get method for rest endpoint gc
    
    Descriptions of the changes in this PR:
    
    Add get method for rest endpoint gc to get force triggered GC running status on Bookie.
    true -- force triggered GC is running on Bookie. false -- not running.
    
    ### Motivation
    
    This is base on PR #1838, and in the review comments sijie is suggested to add get methods.
    
    ### Changes
    
    Add get method for rest endpoint gc and unit test.
    
    
    
    
    
    Reviewers: Sijie Guo <si...@apache.org>
    
    This closes #1840 from jiazhai/rest_gc_get
---
 .../org/apache/bookkeeper/http/HttpRouter.java     |  2 +
 .../org/apache/bookkeeper/http/HttpServer.java     |  2 +
 .../bookkeeper/bookie/GarbageCollectionStatus.java | 47 +++++++++++++++++++
 .../bookkeeper/bookie/GarbageCollectorThread.java  | 24 ++++++++++
 .../bookie/InterleavedLedgerStorage.java           | 31 ++++++++-----
 .../apache/bookkeeper/bookie/LedgerStorage.java    | 21 +++++++++
 .../bookkeeper/bookie/SortedLedgerStorage.java     | 10 +++++
 .../bookie/storage/ldb/DbLedgerStorage.java        | 13 ++++++
 .../ldb/SingleDirectoryDbLedgerStorage.java        | 13 ++++++
 .../server/http/BKHttpServiceProvider.java         |  3 ++
 ...TriggerGCService.java => GCDetailsService.java} | 38 +++++++++++-----
 .../server/http/service/TriggerGCService.java      | 25 +++++++++--
 .../bookkeeper/server/http/TestHttpService.java    | 52 +++++++++++++++++++---
 site/docs/latest/admin/http.md                     | 39 ++++++++++++++++
 14 files changed, 290 insertions(+), 30 deletions(-)

diff --git a/bookkeeper-http/http-server/src/main/java/org/apache/bookkeeper/http/HttpRouter.java b/bookkeeper-http/http-server/src/main/java/org/apache/bookkeeper/http/HttpRouter.java
index 5384df6..b4251d7 100644
--- a/bookkeeper-http/http-server/src/main/java/org/apache/bookkeeper/http/HttpRouter.java
+++ b/bookkeeper-http/http-server/src/main/java/org/apache/bookkeeper/http/HttpRouter.java
@@ -46,6 +46,7 @@ public abstract class HttpRouter<Handler> {
     public static final String LIST_DISK_FILE               = "/api/v1/bookie/list_disk_file";
     public static final String EXPAND_STORAGE               = "/api/v1/bookie/expand_storage";
     public static final String GC                           = "/api/v1/bookie/gc";
+    public static final String GC_DETAILS                   = "/api/v1/bookie/gc_details";
     // autorecovery
     public static final String RECOVERY_BOOKIE              = "/api/v1/autorecovery/bookie";
     public static final String LIST_UNDER_REPLICATED_LEDGER = "/api/v1/autorecovery/list_under_replicated_ledger";
@@ -75,6 +76,7 @@ public abstract class HttpRouter<Handler> {
         this.endpointHandlers.put(LIST_DISK_FILE, handlerFactory.newHandler(HttpServer.ApiType.LIST_DISK_FILE));
         this.endpointHandlers.put(EXPAND_STORAGE, handlerFactory.newHandler(HttpServer.ApiType.EXPAND_STORAGE));
         this.endpointHandlers.put(GC, handlerFactory.newHandler(HttpServer.ApiType.GC));
+        this.endpointHandlers.put(GC_DETAILS, handlerFactory.newHandler(HttpServer.ApiType.GC_DETAILS));
 
         // autorecovery
         this.endpointHandlers.put(RECOVERY_BOOKIE, handlerFactory.newHandler(HttpServer.ApiType.RECOVERY_BOOKIE));
diff --git a/bookkeeper-http/http-server/src/main/java/org/apache/bookkeeper/http/HttpServer.java b/bookkeeper-http/http-server/src/main/java/org/apache/bookkeeper/http/HttpServer.java
index 5e9f509..0922b56 100644
--- a/bookkeeper-http/http-server/src/main/java/org/apache/bookkeeper/http/HttpServer.java
+++ b/bookkeeper-http/http-server/src/main/java/org/apache/bookkeeper/http/HttpServer.java
@@ -78,6 +78,8 @@ public interface HttpServer {
         LIST_DISK_FILE,
         EXPAND_STORAGE,
         GC,
+        GC_DETAILS,
+
         // autorecovery
         RECOVERY_BOOKIE,
         LIST_UNDER_REPLICATED_LEDGER,
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/GarbageCollectionStatus.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/GarbageCollectionStatus.java
new file mode 100644
index 0000000..e1728dd
--- /dev/null
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/GarbageCollectionStatus.java
@@ -0,0 +1,47 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.bookkeeper.bookie;
+
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * This is the garbage collection thread status.
+ * It includes what phase GarbageCollection (major/minor), gc counters, last gc time, etc.
+ */
+@Setter
+@Getter
+@Builder
+public class GarbageCollectionStatus {
+    // whether the GC thread is in force GC.
+    private boolean forceCompacting;
+    // whether the GC thread is in major compacting.
+    private boolean majorCompacting;
+    // whether the GC thread is in minor compacting.
+    private boolean minorCompacting;
+
+    private long lastMajorCompactionTime;
+    private long lastMinorCompactionTime;
+    private long majorCompactionCounter;
+    private long minorCompactionCounter;
+}
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/GarbageCollectorThread.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/GarbageCollectorThread.java
index 369883c..ca02651 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/GarbageCollectorThread.java
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/GarbageCollectorThread.java
@@ -108,6 +108,10 @@ public class GarbageCollectorThread extends SafeRunnable {
     // to reduce the risk getting entry log corrupted
     final AtomicBoolean compacting = new AtomicBoolean(false);
 
+    // use to get the compacting status
+    final AtomicBoolean minorCompacting = new AtomicBoolean(false);
+    final AtomicBoolean majorCompacting = new AtomicBoolean(false);
+
     volatile boolean running = true;
 
     // track the last scanned successfully log id
@@ -298,6 +302,10 @@ public class GarbageCollectorThread extends SafeRunnable {
             });
     }
 
+    public boolean isInForceGC() {
+        return forceGarbageCollection.get();
+    }
+
     public void suspendMajorGC() {
         if (suspendMajorCompaction.compareAndSet(false, true)) {
             LOG.info("Suspend Major Compaction triggered by thread: {}", Thread.currentThread().getName());
@@ -376,18 +384,22 @@ public class GarbageCollectorThread extends SafeRunnable {
             && (force || curTime - lastMajorCompactionTime > majorCompactionInterval)) {
             // enter major compaction
             LOG.info("Enter major compaction, suspendMajor {}", suspendMajor);
+            majorCompacting.set(true);
             doCompactEntryLogs(majorCompactionThreshold);
             lastMajorCompactionTime = System.currentTimeMillis();
             // and also move minor compaction time
             lastMinorCompactionTime = lastMajorCompactionTime;
             majorCompactionCounter.inc();
+            majorCompacting.set(false);
         } else if (enableMinorCompaction && (!suspendMinor)
             && (force || curTime - lastMinorCompactionTime > minorCompactionInterval)) {
             // enter minor compaction
             LOG.info("Enter minor compaction, suspendMinor {}", suspendMinor);
+            minorCompacting.set(true);
             doCompactEntryLogs(minorCompactionThreshold);
             lastMinorCompactionTime = System.currentTimeMillis();
             minorCompactionCounter.inc();
+            minorCompacting.set(false);
         }
 
         if (force) {
@@ -601,4 +613,16 @@ public class GarbageCollectorThread extends SafeRunnable {
     CompactableLedgerStorage getLedgerStorage() {
         return ledgerStorage;
     }
+
+    public GarbageCollectionStatus getGarbageCollectionStatus() {
+        return GarbageCollectionStatus.builder()
+            .forceCompacting(forceGarbageCollection.get())
+            .majorCompacting(majorCompacting.get())
+            .minorCompacting(minorCompacting.get())
+            .lastMajorCompactionTime(lastMajorCompactionTime)
+            .lastMinorCompactionTime(lastMinorCompactionTime)
+            .majorCompactionCounter(majorCompactionCounter.get())
+            .minorCompactionCounter(minorCompactionCounter.get())
+            .build();
+    }
 }
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/InterleavedLedgerStorage.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/InterleavedLedgerStorage.java
index 0a0e60d..6078d69 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/InterleavedLedgerStorage.java
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/InterleavedLedgerStorage.java
@@ -37,6 +37,7 @@ import io.netty.buffer.ByteBuf;
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.NavigableMap;
@@ -227,6 +228,11 @@ public class InterleavedLedgerStorage implements CompactableLedgerStorage, Entry
     }
 
     @Override
+    public boolean isInForceGC() {
+        return gcThread.isInForceGC();
+    }
+
+    @Override
     public void start() {
         gcThread.start();
     }
@@ -563,10 +569,10 @@ public class InterleavedLedgerStorage implements CompactableLedgerStorage, Entry
 
                         if (success.booleanValue()) {
                             pageScanStats.registerSuccessfulEvent(
-                                    MathUtils.elapsedNanos(start), TimeUnit.NANOSECONDS);
+                                MathUtils.elapsedNanos(start), TimeUnit.NANOSECONDS);
                         } else {
                             pageScanStats.registerFailedEvent(
-                                    MathUtils.elapsedNanos(start), TimeUnit.NANOSECONDS);
+                                MathUtils.elapsedNanos(start), TimeUnit.NANOSECONDS);
                         }
                     } while (retry.booleanValue());
                     checkedPages++;
@@ -584,15 +590,20 @@ public class InterleavedLedgerStorage implements CompactableLedgerStorage, Entry
             checkedLedgers++;
         }
         LOG.info(
-                "Finished localConsistencyCheck, took {}s to scan {} ledgers, {} pages, "
-                        + "{} entries with {} retries, {} errors",
-                TimeUnit.NANOSECONDS.toSeconds(MathUtils.elapsedNanos(checkStart)),
-                checkedLedgers,
-                checkedPages,
-                checkedEntries.longValue(),
-                pageRetries.longValue(),
-                errors.size());
+            "Finished localConsistencyCheck, took {}s to scan {} ledgers, {} pages, "
+                + "{} entries with {} retries, {} errors",
+            TimeUnit.NANOSECONDS.toSeconds(MathUtils.elapsedNanos(checkStart)),
+            checkedLedgers,
+            checkedPages,
+            checkedEntries.longValue(),
+            pageRetries.longValue(),
+            errors.size());
 
         return errors;
     }
+
+    @Override
+    public List<GarbageCollectionStatus> getGarbageCollectionStatus() {
+        return Collections.singletonList(gcThread.getGarbageCollectionStatus());
+    }
 }
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/LedgerStorage.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/LedgerStorage.java
index 7a98fc7..111b8c2 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/LedgerStorage.java
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/LedgerStorage.java
@@ -25,6 +25,7 @@ import com.google.common.util.concurrent.RateLimiter;
 import io.netty.buffer.ByteBuf;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Optional;
 import org.apache.bookkeeper.bookie.CheckpointSource.Checkpoint;
@@ -224,4 +225,24 @@ public interface LedgerStorage {
     default List<DetectedInconsistency> localConsistencyCheck(Optional<RateLimiter> rateLimiter) throws IOException {
         return new ArrayList<>();
     }
+
+    /**
+     * Whether force triggered Garbage Collection is running or not.
+     *
+     * @return
+     *      true  -- force triggered Garbage Collection is running,
+     *      false -- force triggered Garbage Collection is not running
+     */
+    default boolean isInForceGC() {
+        return false;
+    }
+
+
+    /**
+     * Get Garbage Collection status.
+     * Since DbLedgerStorage is a list of storage instances, we should return a list.
+     */
+    default List<GarbageCollectionStatus> getGarbageCollectionStatus() {
+        return Collections.emptyList();
+    }
 }
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/SortedLedgerStorage.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/SortedLedgerStorage.java
index dd07d75..5e4dbad 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/SortedLedgerStorage.java
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/SortedLedgerStorage.java
@@ -340,4 +340,14 @@ public class SortedLedgerStorage
     public List<DetectedInconsistency> localConsistencyCheck(Optional<RateLimiter> rateLimiter) throws IOException {
         return interleavedLedgerStorage.localConsistencyCheck(rateLimiter);
     }
+
+    @Override
+    public boolean isInForceGC() {
+        return interleavedLedgerStorage.isInForceGC();
+    }
+
+    @Override
+    public List<GarbageCollectionStatus> getGarbageCollectionStatus() {
+        return interleavedLedgerStorage.getGarbageCollectionStatus();
+    }
 }
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/storage/ldb/DbLedgerStorage.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/storage/ldb/DbLedgerStorage.java
index 4c5fd33..0706565 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/storage/ldb/DbLedgerStorage.java
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/storage/ldb/DbLedgerStorage.java
@@ -39,12 +39,14 @@ import java.util.List;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 
+import java.util.stream.Collectors;
 import lombok.extern.slf4j.Slf4j;
 
 import org.apache.bookkeeper.bookie.BookieException;
 import org.apache.bookkeeper.bookie.CheckpointSource;
 import org.apache.bookkeeper.bookie.CheckpointSource.Checkpoint;
 import org.apache.bookkeeper.bookie.Checkpointer;
+import org.apache.bookkeeper.bookie.GarbageCollectionStatus;
 import org.apache.bookkeeper.bookie.LastAddConfirmedUpdateNotification;
 import org.apache.bookkeeper.bookie.LedgerCache;
 import org.apache.bookkeeper.bookie.LedgerDirsManager;
@@ -359,4 +361,15 @@ public class DbLedgerStorage implements LedgerStorage {
     public void forceGC() {
         ledgerStorageList.stream().forEach(SingleDirectoryDbLedgerStorage::forceGC);
     }
+
+    @Override
+    public boolean isInForceGC() {
+        return ledgerStorageList.stream().anyMatch(SingleDirectoryDbLedgerStorage::isInForceGC);
+    }
+
+    @Override
+    public List<GarbageCollectionStatus> getGarbageCollectionStatus() {
+        return ledgerStorageList.stream()
+            .map(single -> single.getGarbageCollectionStatus().get(0)).collect(Collectors.toList());
+    }
 }
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/storage/ldb/SingleDirectoryDbLedgerStorage.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/storage/ldb/SingleDirectoryDbLedgerStorage.java
index 2e3c556..58504ab 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/storage/ldb/SingleDirectoryDbLedgerStorage.java
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/storage/ldb/SingleDirectoryDbLedgerStorage.java
@@ -30,6 +30,8 @@ import io.netty.buffer.ByteBuf;
 import io.netty.util.concurrent.DefaultThreadFactory;
 
 import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -49,6 +51,7 @@ import org.apache.bookkeeper.bookie.Checkpointer;
 import org.apache.bookkeeper.bookie.CompactableLedgerStorage;
 import org.apache.bookkeeper.bookie.EntryLocation;
 import org.apache.bookkeeper.bookie.EntryLogger;
+import org.apache.bookkeeper.bookie.GarbageCollectionStatus;
 import org.apache.bookkeeper.bookie.GarbageCollectorThread;
 import org.apache.bookkeeper.bookie.LastAddConfirmedUpdateNotification;
 import org.apache.bookkeeper.bookie.LedgerCache;
@@ -270,6 +273,11 @@ public class SingleDirectoryDbLedgerStorage implements CompactableLedgerStorage
     }
 
     @Override
+    public boolean isInForceGC() {
+        return gcThread.isInForceGC();
+    }
+
+    @Override
     public void shutdown() throws InterruptedException {
         try {
             flush();
@@ -922,6 +930,11 @@ public class SingleDirectoryDbLedgerStorage implements CompactableLedgerStorage
         return readCache.count();
     }
 
+    @Override
+    public List<GarbageCollectionStatus> getGarbageCollectionStatus() {
+        return Collections.singletonList(gcThread.getGarbageCollectionStatus());
+    }
+
     /**
      * Interface which process ledger logger.
      */
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/http/BKHttpServiceProvider.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/http/BKHttpServiceProvider.java
index 102b0eb..662777c 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/http/BKHttpServiceProvider.java
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/http/BKHttpServiceProvider.java
@@ -41,6 +41,7 @@ import org.apache.bookkeeper.server.http.service.ConfigurationService;
 import org.apache.bookkeeper.server.http.service.DecommissionService;
 import org.apache.bookkeeper.server.http.service.DeleteLedgerService;
 import org.apache.bookkeeper.server.http.service.ExpandStorageService;
+import org.apache.bookkeeper.server.http.service.GCDetailsService;
 import org.apache.bookkeeper.server.http.service.GetLastLogMarkService;
 import org.apache.bookkeeper.server.http.service.GetLedgerMetaService;
 import org.apache.bookkeeper.server.http.service.ListBookieInfoService;
@@ -211,6 +212,8 @@ public class BKHttpServiceProvider implements HttpServiceProvider {
                 return new ExpandStorageService(configuration);
             case GC:
                 return new TriggerGCService(configuration, bookieServer);
+            case GC_DETAILS:
+                return new GCDetailsService(configuration, bookieServer);
 
             // autorecovery
             case RECOVERY_BOOKIE:
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/http/service/TriggerGCService.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/http/service/GCDetailsService.java
similarity index 61%
copy from bookkeeper-server/src/main/java/org/apache/bookkeeper/server/http/service/TriggerGCService.java
copy to bookkeeper-server/src/main/java/org/apache/bookkeeper/server/http/service/GCDetailsService.java
index 185965a..29b59e2 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/http/service/TriggerGCService.java
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/http/service/GCDetailsService.java
@@ -20,6 +20,8 @@ package org.apache.bookkeeper.server.http.service;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.util.List;
+import org.apache.bookkeeper.bookie.GarbageCollectionStatus;
 import org.apache.bookkeeper.common.util.JsonUtil;
 import org.apache.bookkeeper.conf.ServerConfiguration;
 import org.apache.bookkeeper.http.HttpServer;
@@ -31,18 +33,27 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * HttpEndpointService that handle force trigger GC requests.
+ * HttpEndpointService that handle get garbage collection details service.
  *
- * <p>The PUT method will force trigger GC on current bookie, and make GC run at backend.
+ * <p>Get Garbage Collection status, the output would be like:
+ *        [ {
+ *           "forceCompacting" : false,
+ *           "majorCompacting" : false,
+ *           "minorCompacting" : false,
+ *           "lastMajorCompactionTime" : 1544578144944,
+ *           "lastMinorCompactionTime" : 1544578144944,
+ *           "majorCompactionCounter" : 1,
+ *           "minorCompactionCounter" : 0
+ *         } ]
  */
-public class TriggerGCService implements HttpEndpointService {
+public class GCDetailsService implements HttpEndpointService {
 
-    static final Logger LOG = LoggerFactory.getLogger(TriggerGCService.class);
+    static final Logger LOG = LoggerFactory.getLogger(GCDetailsService.class);
 
     protected ServerConfiguration conf;
     protected BookieServer bookieServer;
 
-    public TriggerGCService(ServerConfiguration conf, BookieServer bookieServer) {
+    public GCDetailsService(ServerConfiguration conf, BookieServer bookieServer) {
         checkNotNull(conf);
         checkNotNull(bookieServer);
         this.conf = conf;
@@ -52,19 +63,22 @@ public class TriggerGCService implements HttpEndpointService {
     @Override
     public HttpServiceResponse handle(HttpServiceRequest request) throws Exception {
         HttpServiceResponse response = new HttpServiceResponse();
-        // PUT
-        if (HttpServer.Method.PUT == request.getMethod()) {
-            bookieServer.getBookie().getLedgerStorage().forceGC();
 
-            String output = "Triggered GC on BookieServer: " + bookieServer.toString();
-            String jsonResponse = JsonUtil.toJson(output);
-            LOG.debug("output body:" + jsonResponse);
+        if (HttpServer.Method.GET == request.getMethod()) {
+            List<GarbageCollectionStatus> details = bookieServer.getBookie()
+                .getLedgerStorage().getGarbageCollectionStatus();
+
+            String jsonResponse = JsonUtil.toJson(details);
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("output body:" + jsonResponse);
+            }
             response.setBody(jsonResponse);
             response.setCode(HttpServer.StatusCode.OK);
             return response;
         } else {
             response.setCode(HttpServer.StatusCode.NOT_FOUND);
-            response.setBody("Not found method. Should be PUT method to trigger GC.");
+            response.setBody("Only support GET method to retrieve GC details."
+                + " If you want to trigger gc, send a POST to gc endpoint.");
             return response;
         }
     }
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/http/service/TriggerGCService.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/http/service/TriggerGCService.java
index 185965a..ebf9ea9 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/http/service/TriggerGCService.java
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/http/service/TriggerGCService.java
@@ -27,6 +27,7 @@ import org.apache.bookkeeper.http.service.HttpEndpointService;
 import org.apache.bookkeeper.http.service.HttpServiceRequest;
 import org.apache.bookkeeper.http.service.HttpServiceResponse;
 import org.apache.bookkeeper.proto.BookieServer;
+import org.apache.commons.lang3.tuple.Pair;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -34,6 +35,12 @@ import org.slf4j.LoggerFactory;
  * HttpEndpointService that handle force trigger GC requests.
  *
  * <p>The PUT method will force trigger GC on current bookie, and make GC run at backend.
+ *
+ * <p>The GET method will get the force triggered GC running or not.
+ * Output would be like:
+ *        {
+ *           "is_in_force_gc" : "false"
+ *        }
  */
 public class TriggerGCService implements HttpEndpointService {
 
@@ -52,19 +59,31 @@ public class TriggerGCService implements HttpEndpointService {
     @Override
     public HttpServiceResponse handle(HttpServiceRequest request) throws Exception {
         HttpServiceResponse response = new HttpServiceResponse();
-        // PUT
+
         if (HttpServer.Method.PUT == request.getMethod()) {
             bookieServer.getBookie().getLedgerStorage().forceGC();
 
             String output = "Triggered GC on BookieServer: " + bookieServer.toString();
             String jsonResponse = JsonUtil.toJson(output);
-            LOG.debug("output body:" + jsonResponse);
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("output body:" + jsonResponse);
+            }
+            response.setBody(jsonResponse);
+            response.setCode(HttpServer.StatusCode.OK);
+            return response;
+        } else if (HttpServer.Method.GET == request.getMethod()) {
+            Boolean isInForceGC = bookieServer.getBookie().getLedgerStorage().isInForceGC();
+            Pair<String, String> output = Pair.of("is_in_force_gc", isInForceGC.toString());
+            String jsonResponse = JsonUtil.toJson(output);
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("output body:" + jsonResponse);
+            }
             response.setBody(jsonResponse);
             response.setCode(HttpServer.StatusCode.OK);
             return response;
         } else {
             response.setCode(HttpServer.StatusCode.NOT_FOUND);
-            response.setBody("Not found method. Should be PUT method to trigger GC.");
+            response.setBody("Not found method. Should be PUT to trigger GC, Or GET to get Force GC state.");
             return response;
         }
     }
diff --git a/bookkeeper-server/src/test/java/org/apache/bookkeeper/server/http/TestHttpService.java b/bookkeeper-server/src/test/java/org/apache/bookkeeper/server/http/TestHttpService.java
index e88d29d..da25d14 100644
--- a/bookkeeper-server/src/test/java/org/apache/bookkeeper/server/http/TestHttpService.java
+++ b/bookkeeper-server/src/test/java/org/apache/bookkeeper/server/http/TestHttpService.java
@@ -743,9 +743,6 @@ public class TestHttpService extends BookKeeperClusterTestCase {
         stopAuditorElector();
     }
 
-    /**
-     * Create ledgers, then test Delete Ledger service.
-     */
     @Test
     public void testTriggerGCService() throws Exception {
         baseConf.setMetadataServiceUri(zkUtil.getMetadataServiceUri());
@@ -771,14 +768,59 @@ public class TestHttpService extends BookKeeperClusterTestCase {
         HttpEndpointService triggerGCService = bkHttpServiceProvider
             .provideHttpEndpointService(HttpServer.ApiType.GC);
 
-        //1,  GET, should return NOT_FOUND
+        //1,  GET, should return OK
         HttpServiceRequest request1 = new HttpServiceRequest(null, HttpServer.Method.GET, null);
         HttpServiceResponse response1 = triggerGCService.handle(request1);
-        assertEquals(HttpServer.StatusCode.NOT_FOUND.getValue(), response1.getStatusCode());
+        assertEquals(HttpServer.StatusCode.OK.getValue(), response1.getStatusCode());
+        assertTrue(response1.getBody().contains("\"is_in_force_gc\" : \"false\""));
 
         //2, PUT, should return OK
         HttpServiceRequest request2 = new HttpServiceRequest(null, HttpServer.Method.PUT, null);
         HttpServiceResponse response2 = triggerGCService.handle(request2);
         assertEquals(HttpServer.StatusCode.OK.getValue(), response2.getStatusCode());
     }
+
+    @Test
+    public void testGCDetailsService() throws Exception {
+        baseConf.setMetadataServiceUri(zkUtil.getMetadataServiceUri());
+        BookKeeper.DigestType digestType = BookKeeper.DigestType.CRC32;
+        int numLedgers = 4;
+        int numMsgs = 100;
+        LedgerHandle[] lh = new LedgerHandle[numLedgers];
+        // create ledgers
+        for (int i = 0; i < numLedgers; i++) {
+            lh[i] = bkc.createLedger(digestType, "".getBytes());
+        }
+        String content = "This is test for GC details service!";
+        // add entries
+        for (int i = 0; i < numMsgs; i++) {
+            for (int j = 0; j < numLedgers; j++) {
+                lh[j].addEntry(content.getBytes());
+            }
+        }
+        // close ledgers
+        for (int i = 0; i < numLedgers; i++) {
+            lh[i].close();
+        }
+        HttpEndpointService gcDetailsService = bkHttpServiceProvider
+            .provideHttpEndpointService(HttpServer.ApiType.GC_DETAILS);
+
+        // force trigger a GC
+        HttpEndpointService triggerGCService = bkHttpServiceProvider
+            .provideHttpEndpointService(HttpServer.ApiType.GC);
+        HttpServiceRequest request0 = new HttpServiceRequest(null, HttpServer.Method.PUT, null);
+        HttpServiceResponse response0 = triggerGCService.handle(request0);
+        assertEquals(HttpServer.StatusCode.OK.getValue(), response0.getStatusCode());
+
+        //1,  GET, should return OK
+        HttpServiceRequest request1 = new HttpServiceRequest(null, HttpServer.Method.GET, null);
+        HttpServiceResponse response1 = gcDetailsService.handle(request1);
+        assertEquals(HttpServer.StatusCode.OK.getValue(), response1.getStatusCode());
+        LOG.info("Get response: {}", response1.getBody());
+
+        //2, PUT, should return NOT_FOUND
+        HttpServiceRequest request3 = new HttpServiceRequest(null, HttpServer.Method.PUT, null);
+        HttpServiceResponse response3 = gcDetailsService.handle(request3);
+        assertEquals(HttpServer.StatusCode.NOT_FOUND.getValue(), response3.getStatusCode());
+    }
 }
diff --git a/site/docs/latest/admin/http.md b/site/docs/latest/admin/http.md
index 7c1abea..270dcb3 100644
--- a/site/docs/latest/admin/http.md
+++ b/site/docs/latest/admin/http.md
@@ -282,6 +282,45 @@ Currently all the HTTP endpoints could be divided into these 4 components:
         |403 | Permission denied |
         |404 | Not found |
 
+1. Method: GET
+    * Description:  whether force triggered Garbage Collection is running or not for this bookie. true for is running.
+    * Response:
+
+        | Code   | Description |
+        |:-------|:------------|
+        |200 | Successful operation |
+        |403 | Permission denied |
+        |404 | Not found |
+    * Body:
+       ```json
+       {
+          "is_in_force_gc" : "false"
+       }
+       ```
+
+### Endpoint: /api/v1/bookie/gc_details
+1. Method: GET
+    * Description:  get details of Garbage Collection Thread, like whether it is in compacting, last compaction time, compaction counter, etc.
+    * Response:
+
+        | Code   | Description |
+        |:-------|:------------|
+        |200 | Successful operation |
+        |403 | Permission denied |
+        |404 | Not found |
+    * Body:
+       ```json
+       [ {
+          "forceCompacting" : false,
+          "majorCompacting" : false,
+          "minorCompacting" : false,
+          "lastMajorCompactionTime" : 1544578144944,
+          "lastMinorCompactionTime" : 1544578144944,
+          "majorCompactionCounter" : 1,
+          "minorCompactionCounter" : 0
+        } ]
+       ```
+
 ## Auto recovery
 
 ### Endpoint: /api/v1/autorecovery/bookie/