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

chenhang 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 e169cbd3f5 Add new api resumeCompaction and suspendCompaction to 
support resume and suspend compaction through api (#3509)
e169cbd3f5 is described below

commit e169cbd3f5785d107beff115056ce2068e0e45ba
Author: gaozhangmin <[email protected]>
AuthorDate: Wed Oct 26 12:04:31 2022 +0800

    Add new api resumeCompaction and suspendCompaction to support resume and 
suspend compaction through api (#3509)
    
    ### Motivation
    Compaction would bring high disk io util, It would better allowing to 
control suspending and resuming compaction through api
    
    ### Changes
    Add two apis to control resuming and suspending compaction.
    `/api/v1/bookie/gc/suspend_compaction`
    `/api/v1/bookie/gc/resume_compaction`
---
 .../org/apache/bookkeeper/http/HttpRouter.java     |  4 +
 .../org/apache/bookkeeper/http/HttpServer.java     |  3 +-
 .../bookkeeper/bookie/GarbageCollectorThread.java  |  8 ++
 .../bookie/InterleavedLedgerStorage.java           | 24 ++++++
 .../apache/bookkeeper/bookie/LedgerStorage.java    | 24 ++++++
 .../bookkeeper/bookie/SortedLedgerStorage.java     | 30 +++++++
 .../bookie/storage/ldb/DbLedgerStorage.java        | 30 +++++++
 .../ldb/SingleDirectoryDbLedgerStorage.java        | 24 ++++++
 .../server/http/BKHttpServiceProvider.java         |  6 ++
 .../http/service/ResumeCompactionService.java      | 86 +++++++++++++++++++
 .../http/service/SuspendCompactionService.java     | 99 ++++++++++++++++++++++
 .../bookkeeper/bookie/MockLedgerStorage.java       | 24 ++++++
 .../bookkeeper/server/http/TestHttpService.java    | 71 ++++++++++++++++
 site3/website/docs/admin/http.md                   | 53 ++++++++++++
 14 files changed, 485 insertions(+), 1 deletion(-)

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 e4dbf299e0..1d1d562ff7 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
@@ -47,6 +47,8 @@ public abstract class HttpRouter<Handler> {
     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";
+    public static final String SUSPEND_GC_COMPACTION        = 
"/api/v1/bookie/gc/suspend_compaction";
+    public static final String RESUME_GC_COMPACTION         = 
"/api/v1/bookie/gc/resume_compaction";
     public static final String BOOKIE_STATE                 = 
"/api/v1/bookie/state";
     public static final String BOOKIE_STATE_READONLY        = 
"/api/v1/bookie/state/readonly";
     public static final String BOOKIE_IS_READY              = 
"/api/v1/bookie/is_ready";
@@ -87,6 +89,8 @@ public abstract class HttpRouter<Handler> {
                 
handlerFactory.newHandler(HttpServer.ApiType.BOOKIE_STATE_READONLY));
         this.endpointHandlers.put(BOOKIE_IS_READY, 
handlerFactory.newHandler(HttpServer.ApiType.BOOKIE_IS_READY));
         this.endpointHandlers.put(BOOKIE_INFO, 
handlerFactory.newHandler(HttpServer.ApiType.BOOKIE_INFO));
+        this.endpointHandlers.put(SUSPEND_GC_COMPACTION, 
handlerFactory.newHandler(HttpServer.ApiType.SUSPEND_GC_COMPACTION));
+        this.endpointHandlers.put(RESUME_GC_COMPACTION, 
handlerFactory.newHandler(HttpServer.ApiType.RESUME_GC_COMPACTION));
 
         // autorecovery
         this.endpointHandlers.put(AUTORECOVERY_STATUS, handlerFactory
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 902194a0ec..7bfd62e975 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
@@ -85,7 +85,8 @@ public interface HttpServer {
         BOOKIE_STATE_READONLY,
         BOOKIE_IS_READY,
         BOOKIE_INFO,
-
+        RESUME_GC_COMPACTION,
+        SUSPEND_GC_COMPACTION,
         // autorecovery
         AUTORECOVERY_STATUS,
         RECOVERY_BOOKIE,
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 2e153f7ab7..7502205ffa 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
@@ -333,6 +333,14 @@ public class GarbageCollectorThread extends SafeRunnable {
         return forceGarbageCollection.get();
     }
 
+    public boolean isMajorGcSuspend() {
+        return suspendMajorCompaction.get();
+    }
+
+    public boolean isMinorGcSuspend() {
+        return suspendMinorCompaction.get();
+    }
+
     public void suspendMajorGC() {
         if (suspendMajorCompaction.compareAndSet(false, true)) {
             LOG.info("Suspend Major Compaction triggered by thread: {}", 
Thread.currentThread().getName());
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 7597acc0a6..083a14f36e 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
@@ -273,6 +273,30 @@ public class InterleavedLedgerStorage implements 
CompactableLedgerStorage, Entry
         return gcThread.isInForceGC();
     }
 
+    public void suspendMinorGC() {
+        gcThread.suspendMinorGC();
+    }
+
+    public void suspendMajorGC() {
+        gcThread.suspendMajorGC();
+    }
+
+    public void resumeMinorGC() {
+        gcThread.resumeMinorGC();
+    }
+
+    public void resumeMajorGC() {
+        gcThread.resumeMajorGC();
+    }
+
+    public boolean isMajorGcSuspended() {
+        return gcThread.isMajorGcSuspend();
+    }
+
+    public boolean isMinorGcSuspended() {
+        return gcThread.isMinorGcSuspend();
+    }
+
     @Override
     public void start() {
         gcThread.start();
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 c409112eef..6b0b0fd37e 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
@@ -236,6 +236,30 @@ public interface LedgerStorage {
         return;
     }
 
+    default void suspendMinorGC() {
+        return;
+    }
+
+    default void suspendMajorGC() {
+        return;
+    }
+
+    default void resumeMinorGC() {
+        return;
+    }
+
+    default void resumeMajorGC() {
+        return;
+    }
+
+    default boolean isMajorGcSuspended() {
+        return false;
+    }
+
+    default boolean isMinorGcSuspended() {
+        return false;
+    }
+
     /**
      * Class for describing location of a generic inconsistency.  
Implementations should
      * ensure that detail is populated with an exception which adequately 
describes the
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 b9a4b7cfd8..d7984b1c2e 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
@@ -376,6 +376,36 @@ public class SortedLedgerStorage
         interleavedLedgerStorage.forceGC(forceMajor, forceMinor);
     }
 
+    @Override
+    public void suspendMinorGC() {
+        interleavedLedgerStorage.suspendMinorGC();
+    }
+
+    @Override
+    public void suspendMajorGC() {
+        interleavedLedgerStorage.suspendMajorGC();
+    }
+
+    @Override
+    public void resumeMinorGC() {
+        interleavedLedgerStorage.resumeMinorGC();
+    }
+
+    @Override
+    public void resumeMajorGC() {
+        interleavedLedgerStorage.resumeMajorGC();
+    }
+
+    @Override
+    public boolean isMajorGcSuspended() {
+        return interleavedLedgerStorage.isMajorGcSuspended();
+    }
+
+    @Override
+    public boolean isMinorGcSuspended() {
+        return interleavedLedgerStorage.isMinorGcSuspended();
+    }
+
     @Override
     public List<DetectedInconsistency> 
localConsistencyCheck(Optional<RateLimiter> rateLimiter) throws IOException {
         return interleavedLedgerStorage.localConsistencyCheck(rateLimiter);
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 d1404e0179..f5784423d6 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
@@ -524,6 +524,36 @@ public class DbLedgerStorage implements LedgerStorage {
         return 
ledgerStorageList.stream().anyMatch(SingleDirectoryDbLedgerStorage::isInForceGC);
     }
 
+    @Override
+    public void suspendMinorGC() {
+        
ledgerStorageList.stream().forEach(SingleDirectoryDbLedgerStorage::suspendMinorGC);
+    }
+
+    @Override
+    public void suspendMajorGC() {
+        
ledgerStorageList.stream().forEach(SingleDirectoryDbLedgerStorage::suspendMajorGC);
+    }
+
+    @Override
+    public void resumeMinorGC() {
+        
ledgerStorageList.stream().forEach(SingleDirectoryDbLedgerStorage::resumeMinorGC);
+    }
+
+    @Override
+    public void resumeMajorGC() {
+        
ledgerStorageList.stream().forEach(SingleDirectoryDbLedgerStorage::resumeMajorGC);
+    }
+
+    @Override
+    public boolean isMajorGcSuspended() {
+        return 
ledgerStorageList.stream().allMatch(SingleDirectoryDbLedgerStorage::isMajorGcSuspended);
+    }
+
+    @Override
+    public boolean isMinorGcSuspended() {
+        return 
ledgerStorageList.stream().allMatch(SingleDirectoryDbLedgerStorage::isMinorGcSuspended);
+    }
+
     @Override
     public List<GarbageCollectionStatus> getGarbageCollectionStatus() {
         return ledgerStorageList.stream()
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 d15eaf10c5..8b752b33ca 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
@@ -278,6 +278,30 @@ public class SingleDirectoryDbLedgerStorage implements 
CompactableLedgerStorage
         return gcThread.isInForceGC();
     }
 
+    public void suspendMinorGC() {
+        gcThread.suspendMinorGC();
+    }
+
+    public void suspendMajorGC() {
+        gcThread.suspendMajorGC();
+    }
+
+    public void resumeMinorGC() {
+        gcThread.resumeMinorGC();
+    }
+
+    public void resumeMajorGC() {
+        gcThread.resumeMajorGC();
+    }
+
+    public boolean isMajorGcSuspended() {
+        return gcThread.isMajorGcSuspend();
+    }
+
+    public boolean isMinorGcSuspended() {
+        return gcThread.isMinorGcSuspend();
+    }
+
     @Override
     public void shutdown() throws InterruptedException {
         try {
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 0b10f84ed8..8ebf29e831 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
@@ -58,6 +58,8 @@ import 
org.apache.bookkeeper.server.http.service.LostBookieRecoveryDelayService;
 import org.apache.bookkeeper.server.http.service.MetricsService;
 import org.apache.bookkeeper.server.http.service.ReadLedgerEntryService;
 import org.apache.bookkeeper.server.http.service.RecoveryBookieService;
+import org.apache.bookkeeper.server.http.service.ResumeCompactionService;
+import org.apache.bookkeeper.server.http.service.SuspendCompactionService;
 import org.apache.bookkeeper.server.http.service.TriggerAuditService;
 import org.apache.bookkeeper.server.http.service.TriggerGCService;
 import org.apache.bookkeeper.server.http.service.WhoIsAuditorService;
@@ -223,6 +225,10 @@ public class BKHttpServiceProvider implements 
HttpServiceProvider {
                 return new BookieIsReadyService(bookieServer.getBookie());
             case BOOKIE_INFO:
                 return new BookieInfoService(bookieServer.getBookie());
+            case SUSPEND_GC_COMPACTION:
+                return new SuspendCompactionService(bookieServer);
+            case RESUME_GC_COMPACTION:
+                return new ResumeCompactionService(bookieServer);
 
             // autorecovery
             case AUTORECOVERY_STATUS:
diff --git 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/http/service/ResumeCompactionService.java
 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/http/service/ResumeCompactionService.java
new file mode 100644
index 0000000000..92d66fbcf7
--- /dev/null
+++ 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/http/service/ResumeCompactionService.java
@@ -0,0 +1,86 @@
+/*
+ * 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.server.http.service;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.bookkeeper.common.util.JsonUtil;
+import org.apache.bookkeeper.http.HttpServer;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ResumeCompactionService implements HttpEndpointService {
+
+    static final Logger LOG = 
LoggerFactory.getLogger(ResumeCompactionService.class);
+
+    protected BookieServer bookieServer;
+
+    public ResumeCompactionService(BookieServer bookieServer) {
+        checkNotNull(bookieServer);
+        this.bookieServer = bookieServer;
+    }
+
+    @Override
+    public HttpServiceResponse handle(HttpServiceRequest request) throws 
Exception {
+        HttpServiceResponse response = new HttpServiceResponse();
+
+        if (HttpServer.Method.PUT == request.getMethod()) {
+            String requestBody = request.getBody();
+            if (null == requestBody) {
+                return new HttpServiceResponse("Empty request body", 
HttpServer.StatusCode.BAD_REQUEST);
+            } else {
+                @SuppressWarnings("unchecked")
+                Map<String, Object> configMap = JsonUtil.fromJson(requestBody, 
HashMap.class);
+                Boolean resumeMajor = (Boolean) configMap.get("resumeMajor");
+                Boolean resumeMinor = (Boolean) configMap.get("resumeMinor");
+                if (resumeMajor == null && resumeMinor == null) {
+                    return new HttpServiceResponse("No resumeMajor or 
resumeMinor params found",
+                            HttpServer.StatusCode.BAD_REQUEST);
+                }
+                String output = "";
+                if (resumeMajor != null  && resumeMajor) {
+                    output = "Resume majorGC on BookieServer: " + 
bookieServer.toString();
+                    
bookieServer.getBookie().getLedgerStorage().resumeMajorGC();
+                }
+                if (resumeMinor != null && resumeMinor) {
+                    output += ", Resume minorGC on BookieServer: " + 
bookieServer.toString();
+                    
bookieServer.getBookie().getLedgerStorage().resumeMinorGC();
+                }
+                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 to resume major 
or minor compaction, Or GET to get "
+                    + "compaction state.");
+            return response;
+        }
+    }
+}
diff --git 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/http/service/SuspendCompactionService.java
 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/http/service/SuspendCompactionService.java
new file mode 100644
index 0000000000..74e284ad96
--- /dev/null
+++ 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/http/service/SuspendCompactionService.java
@@ -0,0 +1,99 @@
+/*
+ * 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.server.http.service;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.bookkeeper.common.util.JsonUtil;
+import org.apache.bookkeeper.http.HttpServer;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SuspendCompactionService implements HttpEndpointService {
+
+    static final Logger LOG = 
LoggerFactory.getLogger(SuspendCompactionService.class);
+
+    protected BookieServer bookieServer;
+
+    public SuspendCompactionService(BookieServer bookieServer) {
+        checkNotNull(bookieServer);
+        this.bookieServer = bookieServer;
+    }
+
+    @Override
+    public HttpServiceResponse handle(HttpServiceRequest request) throws 
Exception {
+        HttpServiceResponse response = new HttpServiceResponse();
+
+        if (HttpServer.Method.PUT == request.getMethod()) {
+            String requestBody = request.getBody();
+            if (null == requestBody) {
+                return new HttpServiceResponse("Empty request body", 
HttpServer.StatusCode.BAD_REQUEST);
+            } else {
+                @SuppressWarnings("unchecked")
+                Map<String, Object> configMap = JsonUtil.fromJson(requestBody, 
HashMap.class);
+                Boolean suspendMajor = (Boolean) configMap.get("suspendMajor");
+                Boolean suspendMinor = (Boolean) configMap.get("suspendMinor");
+                if (suspendMajor == null && suspendMinor == null) {
+                    return new HttpServiceResponse("No suspendMajor or 
suspendMinor params found",
+                            HttpServer.StatusCode.BAD_REQUEST);
+                }
+                String output = "";
+                if (suspendMajor != null  && suspendMajor) {
+                    output = "Suspend majorGC on BookieServer: " + 
bookieServer.toString();
+                    
bookieServer.getBookie().getLedgerStorage().suspendMajorGC();
+                }
+                if (suspendMinor != null && suspendMinor) {
+                    output += ", Suspend minorGC on BookieServer: " + 
bookieServer.toString();
+                    
bookieServer.getBookie().getLedgerStorage().suspendMinorGC();
+                }
+                String jsonResponse = JsonUtil.toJson(output);
+                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 isMajorGcSuspend = 
bookieServer.getBookie().getLedgerStorage().isMajorGcSuspended();
+            boolean isMinorGcSuspend = 
bookieServer.getBookie().getLedgerStorage().isMinorGcSuspended();
+            Map<String, String> output = new HashMap<>();
+            output.put("isMajorGcSuspended", 
Boolean.toString(isMajorGcSuspend));
+            output.put("isMinorGcSuspended", 
Boolean.toString(isMinorGcSuspend));
+            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 to suspend major 
or minor compaction, "
+                    + "Or GET to get compaction state.");
+            return response;
+        }
+    }
+}
diff --git 
a/bookkeeper-server/src/test/java/org/apache/bookkeeper/bookie/MockLedgerStorage.java
 
b/bookkeeper-server/src/test/java/org/apache/bookkeeper/bookie/MockLedgerStorage.java
index f5b94e422c..33e18e90cd 100644
--- 
a/bookkeeper-server/src/test/java/org/apache/bookkeeper/bookie/MockLedgerStorage.java
+++ 
b/bookkeeper-server/src/test/java/org/apache/bookkeeper/bookie/MockLedgerStorage.java
@@ -280,6 +280,30 @@ public class MockLedgerStorage implements 
CompactableLedgerStorage {
         CompactableLedgerStorage.super.forceGC(forceMajor, forceMinor);
     }
 
+    public void suspendMinorGC() {
+        CompactableLedgerStorage.super.suspendMinorGC();
+    }
+
+    public void suspendMajorGC() {
+        CompactableLedgerStorage.super.suspendMajorGC();
+    }
+
+    public void resumeMinorGC() {
+        CompactableLedgerStorage.super.resumeMinorGC();
+    }
+
+    public void resumeMajorGC() {
+        CompactableLedgerStorage.super.suspendMajorGC();
+    }
+
+    public boolean isMajorGcSuspended() {
+        return CompactableLedgerStorage.super.isMajorGcSuspended();
+    }
+
+    public boolean isMinorGcSuspended() {
+        return CompactableLedgerStorage.super.isMinorGcSuspended();
+    }
+
     @Override
     public List<DetectedInconsistency> 
localConsistencyCheck(Optional<RateLimiter> rateLimiter) throws IOException {
         return 
CompactableLedgerStorage.super.localConsistencyCheck(rateLimiter);
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 1f718f4742..cdb9c4fdd4 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
@@ -964,4 +964,75 @@ public class TestHttpService extends 
BookKeeperClusterTestCase {
         readOnlyState = JsonUtil.fromJson(response.getBody(), 
ReadOnlyState.class);
         assertFalse(readOnlyState.isReadOnly());
     }
+
+    @Test
+    public void testSuspendCompaction() throws Exception {
+        HttpEndpointService suspendCompactionService = bkHttpServiceProvider
+                
.provideHttpEndpointService(HttpServer.ApiType.SUSPEND_GC_COMPACTION);
+
+        HttpEndpointService resumeCompactionService = bkHttpServiceProvider
+                
.provideHttpEndpointService(HttpServer.ApiType.RESUME_GC_COMPACTION);
+
+        //1,  PUT with null body, should return error
+        HttpServiceRequest request1 = new HttpServiceRequest(null, 
HttpServer.Method.PUT, null);
+        HttpServiceResponse response1 = 
suspendCompactionService.handle(request1);
+        assertEquals(HttpServer.StatusCode.BAD_REQUEST.getValue(), 
response1.getStatusCode());
+
+        //2,  PUT with null, should return error, because should contains 
"suspendMajor" or "suspendMinor"
+        String putBody2 = "{}";
+        HttpServiceRequest request2 = new HttpServiceRequest(putBody2, 
HttpServer.Method.PUT, null);
+        HttpServiceResponse response2 = 
suspendCompactionService.handle(request2);
+        assertEquals(HttpServer.StatusCode.BAD_REQUEST.getValue(), 
response2.getStatusCode());
+
+
+        //3,  GET before suspend, should success
+        HttpServiceRequest request3 = new HttpServiceRequest(null, 
HttpServer.Method.GET, null);
+        HttpServiceResponse response3 = 
suspendCompactionService.handle(request3);
+        assertEquals(HttpServer.StatusCode.OK.getValue(), 
response3.getStatusCode());
+
+        Map responseMap = JsonUtil.fromJson(
+                response3.getBody(),
+                Map.class
+        );
+        assertEquals(responseMap.get("isMajorGcSuspended"), "false");
+        assertEquals(responseMap.get("isMinorGcSuspended"), "false");
+
+
+        //2, PUT, with body, should success
+        String putBody4 = "{\"suspendMajor\": true, \"suspendMinor\": true}";
+        HttpServiceRequest request4 = new HttpServiceRequest(putBody4, 
HttpServer.Method.PUT, null);
+        HttpServiceResponse response4 = 
suspendCompactionService.handle(request4);
+        assertEquals(HttpServer.StatusCode.OK.getValue(), 
response4.getStatusCode());
+
+        //3,  GET after suspend, should success
+        HttpServiceRequest request5 = new HttpServiceRequest(null, 
HttpServer.Method.GET, null);
+        HttpServiceResponse response5 = 
suspendCompactionService.handle(request5);
+        assertEquals(HttpServer.StatusCode.OK.getValue(), 
response5.getStatusCode());
+
+        Map responseMap5 = JsonUtil.fromJson(
+                response5.getBody(),
+                Map.class
+        );
+        assertEquals(responseMap5.get("isMajorGcSuspended"), "true");
+        assertEquals(responseMap5.get("isMinorGcSuspended"), "true");
+
+
+        //2, PUT, with body, should success
+        String putBody6 = "{\"resumeMajor\": true, \"resumeMinor\": true}";
+        HttpServiceRequest request6 = new HttpServiceRequest(putBody6, 
HttpServer.Method.PUT, null);
+        HttpServiceResponse response6 = 
resumeCompactionService.handle(request6);
+        assertEquals(HttpServer.StatusCode.OK.getValue(), 
response6.getStatusCode());
+
+        //3,  GET after suspend, should success
+        HttpServiceRequest request7 = new HttpServiceRequest(null, 
HttpServer.Method.GET, null);
+        HttpServiceResponse response7 = 
suspendCompactionService.handle(request7);
+        assertEquals(HttpServer.StatusCode.OK.getValue(), 
response7.getStatusCode());
+
+        Map responseMap7 = JsonUtil.fromJson(
+                response7.getBody(),
+                Map.class
+        );
+        assertEquals(responseMap7.get("isMajorGcSuspended"), "false");
+        assertEquals(responseMap7.get("isMinorGcSuspended"), "false");
+    }
 }
diff --git a/site3/website/docs/admin/http.md b/site3/website/docs/admin/http.md
index d827720856..6c71c09740 100644
--- a/site3/website/docs/admin/http.md
+++ b/site3/website/docs/admin/http.md
@@ -339,6 +339,59 @@ Currently all the HTTP endpoints could be divided into 
these 5 components:
           "minorCompactionCounter" : 0
         } ]
        ```
+### Endpoint: /api/v1/bookie/gc/suspend_compaction
+1. Method: PUT
+    * Description:  suspend the next compaction stage for this bookie.
+    * Body:
+         ```json
+         {
+            "suspendMajor": "true",
+            "suspendMinor": "true"
+         }
+         ```
+    * Response:
+
+      | Code   | Description |
+              |:-------|:------------|
+      |200 | Successful operation |
+      |403 | Permission denied |
+      |404 | Not found |
+
+2. Method: GET
+    * Description:  whether major or minor compaction  is suspending or not 
for this bookie. true for is running.
+    * Response:
+
+      | Code   | Description |
+              |:-------|:------------|
+      |200 | Successful operation |
+      |403 | Permission denied |
+      |404 | Not found |
+    * Body:
+       ```json
+       {
+          "isMajorGcSuspended" : "true",
+          "isMinorGcSuspended" : "true"
+      
+       }
+       ```
+
+### Endpoint: /api/v1/bookie/gc/resume_compaction
+1. Method: PUT
+    * Description:  resume the suspended compaction for this bookie.
+    * Body:
+         ```json
+         {
+            "resumeMajor": "true",
+            "resumeMinor": "true"
+         }
+         ```
+    * Response:
+
+      | Code   | Description |
+                    |:-------|:------------|
+      |200 | Successful operation |
+      |403 | Permission denied |
+      |404 | Not found |
 
 ### Endpoint: /api/v1/bookie/state
 1. Method: GET

Reply via email to