wchevreuil commented on code in PR #5462:
URL: https://github.com/apache/hbase/pull/5462#discussion_r1361864310


##########
hbase-client/src/main/java/org/apache/hadoop/hbase/client/RawAsyncHBaseAdmin.java:
##########
@@ -1623,6 +1623,61 @@ private CompletableFuture<Void> split(final RegionInfo 
hri, byte[] splitPoint) {
     return future;
   }
 
+  @Override
+  public CompletableFuture<Void> truncateRegion(byte[] regionName) {
+    CompletableFuture<Void> future = new CompletableFuture<>();
+    addListener(getRegionLocation(regionName), (location, err) -> {
+      if (err != null) {
+        future.completeExceptionally(err);
+        return;
+      }
+      RegionInfo regionInfo = location.getRegion();
+      if (regionInfo.getReplicaId() != RegionInfo.DEFAULT_REPLICA_ID) {
+        future.completeExceptionally(new IllegalArgumentException(
+          "Can't truncate replicas directly.Replicas are auto-truncated "
+            + "when their primary is truncated."));
+        return;
+      }
+      ServerName serverName = location.getServerName();
+      if (serverName == null) {
+        future
+          .completeExceptionally(new 
NoServerForRegionException(Bytes.toStringBinary(regionName)));
+        return;
+      }
+      addListener(truncate_region(regionInfo), (ret, err2) -> {
+        if (err2 != null) {
+          future.completeExceptionally(err2);
+        } else {
+          future.complete(ret);
+        }
+      });
+    });
+    return future;
+  }
+
+  private CompletableFuture<Void> truncate_region(final RegionInfo hri) {

Review Comment:
   Please use camel case naming pattern.



##########
hbase-client/src/main/java/org/apache/hadoop/hbase/shaded/protobuf/RequestConverter.java:
##########
@@ -989,6 +989,17 @@ public static SplitTableRegionRequest 
buildSplitTableRegionRequest(final RegionI
     return builder.build();
   }
 
+  public static MasterProtos.TruncateRegionRequest buildSTruncateRegionRequest(

Review Comment:
   nit: method name should be: buildTruncateRegionRequest?



##########
hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/TruncateRegionProcedure.java:
##########
@@ -0,0 +1,260 @@
+/*
+ * 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.hadoop.hbase.master.procedure;
+
+import java.io.IOException;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.hbase.HBaseIOException;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.client.RegionInfo;
+import org.apache.hadoop.hbase.master.MasterCoprocessorHost;
+import org.apache.hadoop.hbase.master.MasterFileSystem;
+import org.apache.hadoop.hbase.master.assignment.RegionStateNode;
+import org.apache.hadoop.hbase.master.assignment.TransitRegionStateProcedure;
+import org.apache.hadoop.hbase.procedure2.ProcedureStateSerializer;
+import org.apache.hadoop.hbase.regionserver.HRegionFileSystem;
+import org.apache.hadoop.hbase.util.CommonFSUtils;
+import org.apache.yetus.audience.InterfaceAudience;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
+import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos;
+import 
org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.TruncateRegionState;
+
[email protected]
+public class TruncateRegionProcedure
+  extends AbstractStateMachineRegionProcedure<TruncateRegionState> {
+  private static final Logger LOG = 
LoggerFactory.getLogger(TruncateRegionProcedure.class);
+
+  private RegionInfo region;
+  private RegionInfo newRegion;
+
+  @SuppressWarnings("unused")
+  public TruncateRegionProcedure() {
+    // Required by the Procedure framework to create the procedure on replay
+    super();
+  }
+
+  public TruncateRegionProcedure(final MasterProcedureEnv env, final 
RegionInfo hri)
+    throws HBaseIOException {
+    super(env, hri);
+    this.region = hri;
+    checkOnline(env, region);
+  }
+
+  @Override
+  protected Flow executeFromState(final MasterProcedureEnv env, 
TruncateRegionState state)
+    throws InterruptedException {
+    if (LOG.isTraceEnabled()) {
+      LOG.trace(this + " execute state=" + state);
+    }
+    try {
+      switch (state) {
+        case TRUNCATE_REGION_PRE_OPERATION:
+          if (!prepareTruncate()) {
+            assert isFailed() : "the truncate should have an exception here";
+            return Flow.NO_MORE_STATE;
+          }
+          checkOnline(env, region);
+          assert region.getReplicaId() == RegionInfo.DEFAULT_REPLICA_ID || 
isFailed()
+            : "Can't truncate replicas directly. "
+              + "Replicas are auto-truncated when their primary is truncated.";
+          preTruncate(env);
+          setNextState(TruncateRegionState.TRUNCATE_REGION_MAKE_OFFLINE);
+          break;
+        case TRUNCATE_REGION_MAKE_OFFLINE:
+          addChildProcedure(createUnAssignProcedures(env));
+          setNextState(TruncateRegionState.TRUNCATE_REGION_REMOVE);
+          break;
+        case TRUNCATE_REGION_REMOVE:
+          deleteRegionsFromFileSystem(env);
+          setNextState(TruncateRegionState.TRUNCATE_REGION_MAKE_ONLINE);
+          break;
+        case TRUNCATE_REGION_MAKE_ONLINE:
+          addChildProcedure(createAssignProcedures(env));
+          setNextState(TruncateRegionState.TRUNCATE_REGION_POST_OPERATION);
+          break;
+        case TRUNCATE_REGION_POST_OPERATION:
+          postTruncate(env);
+          LOG.debug("truncate '" + getTableName() + "' completed");
+          return Flow.NO_MORE_STATE;
+        default:
+          throw new UnsupportedOperationException("unhandled state=" + state);
+      }
+    } catch (IOException e) {
+      if (isRollbackSupported(state)) {
+        setFailure("master-truncate-table", e);
+      } else {
+        LOG.warn("Retriable error trying to truncate table=" + getTableName() 
+ " state=" + state,
+          e);
+      }
+    }
+    return Flow.HAS_MORE_STATE;
+  }
+
+  private TransitRegionStateProcedure 
createAssignProcedures(MasterProcedureEnv env)
+    throws IOException {
+    return createAssignProceduresForTruncateRegion(env, region);
+  }
+
+  private TransitRegionStateProcedure 
createUnAssignProcedures(MasterProcedureEnv env)
+    throws IOException {
+    return createUnassignProceduresForTruncateRegion(env, region);
+  }
+
+  private void deleteRegionsFromFileSystem(final MasterProcedureEnv env) 
throws IOException {
+    final MasterFileSystem mfs = env.getMasterServices().getMasterFileSystem();
+    final Path tableDir = CommonFSUtils.getTableDir(mfs.getRootDir(), 
getTableName());
+    final Configuration conf = env.getMasterConfiguration();
+    HRegionFileSystem.deleteRegionFromFileSystem(conf, mfs.getFileSystem(), 
tableDir, region);

Review Comment:
   This would delete the region dir entirely. I don't see where we recreate the 
region dir, later. Isn't this a problem?



##########
hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java:
##########
@@ -2275,6 +2275,29 @@ protected String getDescription() {
       });
   }
 
+  @Override
+  public long truncateRegion(final RegionInfo regionInfo, final long 
nonceGroup, final long nonce)

Review Comment:
   We don't need to call the CPs pre/post truncate methods here?
   
   Also, this method structure looks pretty similar to the truncateTable one. 
Couldn't we do some code reuse here?



##########
hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/TruncateRegionProcedure.java:
##########
@@ -0,0 +1,260 @@
+/*
+ * 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.hadoop.hbase.master.procedure;
+
+import java.io.IOException;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.hbase.HBaseIOException;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.client.RegionInfo;
+import org.apache.hadoop.hbase.master.MasterCoprocessorHost;
+import org.apache.hadoop.hbase.master.MasterFileSystem;
+import org.apache.hadoop.hbase.master.assignment.RegionStateNode;
+import org.apache.hadoop.hbase.master.assignment.TransitRegionStateProcedure;
+import org.apache.hadoop.hbase.procedure2.ProcedureStateSerializer;
+import org.apache.hadoop.hbase.regionserver.HRegionFileSystem;
+import org.apache.hadoop.hbase.util.CommonFSUtils;
+import org.apache.yetus.audience.InterfaceAudience;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
+import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos;
+import 
org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.TruncateRegionState;
+
[email protected]
+public class TruncateRegionProcedure
+  extends AbstractStateMachineRegionProcedure<TruncateRegionState> {
+  private static final Logger LOG = 
LoggerFactory.getLogger(TruncateRegionProcedure.class);
+
+  private RegionInfo region;
+  private RegionInfo newRegion;
+
+  @SuppressWarnings("unused")
+  public TruncateRegionProcedure() {
+    // Required by the Procedure framework to create the procedure on replay
+    super();
+  }
+
+  public TruncateRegionProcedure(final MasterProcedureEnv env, final 
RegionInfo hri)
+    throws HBaseIOException {
+    super(env, hri);
+    this.region = hri;
+    checkOnline(env, region);
+  }
+
+  @Override
+  protected Flow executeFromState(final MasterProcedureEnv env, 
TruncateRegionState state)
+    throws InterruptedException {
+    if (LOG.isTraceEnabled()) {
+      LOG.trace(this + " execute state=" + state);
+    }
+    try {
+      switch (state) {
+        case TRUNCATE_REGION_PRE_OPERATION:
+          if (!prepareTruncate()) {
+            assert isFailed() : "the truncate should have an exception here";
+            return Flow.NO_MORE_STATE;
+          }
+          checkOnline(env, region);
+          assert region.getReplicaId() == RegionInfo.DEFAULT_REPLICA_ID || 
isFailed()
+            : "Can't truncate replicas directly. "
+              + "Replicas are auto-truncated when their primary is truncated.";
+          preTruncate(env);
+          setNextState(TruncateRegionState.TRUNCATE_REGION_MAKE_OFFLINE);
+          break;
+        case TRUNCATE_REGION_MAKE_OFFLINE:
+          addChildProcedure(createUnAssignProcedures(env));
+          setNextState(TruncateRegionState.TRUNCATE_REGION_REMOVE);
+          break;
+        case TRUNCATE_REGION_REMOVE:
+          deleteRegionsFromFileSystem(env);
+          setNextState(TruncateRegionState.TRUNCATE_REGION_MAKE_ONLINE);
+          break;
+        case TRUNCATE_REGION_MAKE_ONLINE:
+          addChildProcedure(createAssignProcedures(env));
+          setNextState(TruncateRegionState.TRUNCATE_REGION_POST_OPERATION);
+          break;
+        case TRUNCATE_REGION_POST_OPERATION:
+          postTruncate(env);
+          LOG.debug("truncate '" + getTableName() + "' completed");
+          return Flow.NO_MORE_STATE;
+        default:
+          throw new UnsupportedOperationException("unhandled state=" + state);
+      }
+    } catch (IOException e) {
+      if (isRollbackSupported(state)) {
+        setFailure("master-truncate-table", e);
+      } else {
+        LOG.warn("Retriable error trying to truncate table=" + getTableName() 
+ " state=" + state,
+          e);
+      }
+    }
+    return Flow.HAS_MORE_STATE;
+  }
+
+  private TransitRegionStateProcedure 
createAssignProcedures(MasterProcedureEnv env)
+    throws IOException {
+    return createAssignProceduresForTruncateRegion(env, region);
+  }
+
+  private TransitRegionStateProcedure 
createUnAssignProcedures(MasterProcedureEnv env)
+    throws IOException {
+    return createUnassignProceduresForTruncateRegion(env, region);
+  }
+
+  private void deleteRegionsFromFileSystem(final MasterProcedureEnv env) 
throws IOException {
+    final MasterFileSystem mfs = env.getMasterServices().getMasterFileSystem();
+    final Path tableDir = CommonFSUtils.getTableDir(mfs.getRootDir(), 
getTableName());
+    final Configuration conf = env.getMasterConfiguration();
+    HRegionFileSystem.deleteRegionFromFileSystem(conf, mfs.getFileSystem(), 
tableDir, region);
+  }
+
+  @Override
+  protected void rollbackState(final MasterProcedureEnv env, final 
TruncateRegionState state)
+    throws IOException {
+    if (state == TruncateRegionState.TRUNCATE_REGION_PRE_OPERATION) {
+      // nothing to rollback, pre-truncate is just table-state checks.
+      return;
+    }
+    if (state == TruncateRegionState.TRUNCATE_REGION_MAKE_OFFLINE) {
+      addChildProcedure(createAssignProcedures(env));
+      return;
+    }
+    // The truncate doesn't have a rollback. The execution will succeed, at 
some point.
+    throw new UnsupportedOperationException("unhandled state=" + state);
+  }
+
+  @Override
+  protected void completionCleanup(final MasterProcedureEnv env) {
+    releaseSyncLatch();
+  }
+
+  @Override
+  protected boolean isRollbackSupported(final TruncateRegionState state) {
+    return state == TruncateRegionState.TRUNCATE_REGION_PRE_OPERATION;
+  }
+
+  @Override
+  protected TruncateRegionState getState(final int stateId) {
+    return TruncateRegionState.forNumber(stateId);
+  }
+
+  @Override
+  protected int getStateId(final TruncateRegionState state) {
+    return state.getNumber();
+  }
+
+  @Override
+  protected TruncateRegionState getInitialState() {
+    return TruncateRegionState.TRUNCATE_REGION_PRE_OPERATION;
+  }
+
+  @Override
+  public void toStringClassDetails(StringBuilder sb) {
+    sb.append(getClass().getSimpleName());
+    sb.append(" (region=");
+    sb.append(region.getRegionNameAsString());
+    sb.append(")");
+  }
+
+  @Override
+  protected void serializeStateData(ProcedureStateSerializer serializer) 
throws IOException {
+    super.serializeStateData(serializer);
+
+    MasterProcedureProtos.TruncateRegionStateData.Builder state =
+      MasterProcedureProtos.TruncateRegionStateData.newBuilder()
+        .setUserInfo(MasterProcedureUtil.toProtoUserInfo(getUser()));
+    if (region != null) {
+      state.setRegion(ProtobufUtil.toRegionInfo(region));
+    }
+    serializer.serialize(state.build());
+  }
+
+  @Override
+  protected void deserializeStateData(ProcedureStateSerializer serializer) 
throws IOException {
+    super.deserializeStateData(serializer);
+
+    MasterProcedureProtos.TruncateRegionStateData state =
+      
serializer.deserialize(MasterProcedureProtos.TruncateRegionStateData.class);
+    setUser(MasterProcedureUtil.toUserInfo(state.getUserInfo()));
+    if (state.getRegion() != null) {
+      region = ProtobufUtil.toRegionInfo(state.getRegion());
+    }
+    if (state.getNewRegion() != null) {
+      newRegion = ProtobufUtil.toRegionInfo(state.getNewRegion());
+    }
+  }
+
+  private boolean prepareTruncate() throws IOException {
+    if (getTableName().equals(TableName.META_TABLE_NAME)) {
+      throw new IOException("Can't truncate region in catalog tables");
+    }
+    return true;
+  }
+
+  private void preTruncate(final MasterProcedureEnv env) throws IOException {
+    final MasterCoprocessorHost cpHost = env.getMasterCoprocessorHost();
+    if (cpHost != null) {
+      cpHost.preTruncateRegionAction(region, getUser());
+    }
+  }
+
+  private void postTruncate(final MasterProcedureEnv env) throws IOException {
+    final MasterCoprocessorHost cpHost = env.getMasterCoprocessorHost();
+    if (cpHost != null) {
+      cpHost.postTruncateRegionAction(newRegion, getUser());
+    }
+  }
+
+  @Override
+  public TableOperationType getTableOperationType() {
+    return TableOperationType.REGION_TRUNCATE;
+  }
+
+  public static TransitRegionStateProcedure
+    createUnassignProceduresForTruncateRegion(MasterProcedureEnv env, 
RegionInfo region) {

Review Comment:
   Can't we reuse similar method from AssignmentManager?



##########
hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/AssignmentManager.java:
##########
@@ -1082,6 +1083,11 @@ public SplitTableRegionProcedure 
createSplitProcedure(final RegionInfo regionToS
     return new SplitTableRegionProcedure(getProcedureEnvironment(), 
regionToSplit, splitKey);
   }
 
+  public TruncateRegionProcedure createTruncateRegionProcedure(final 
RegionInfo regionToTruncate)
+    throws IOException {
+    return new TruncateRegionProcedure(getProcedureEnvironment(), 
regionToTruncate);
+  }
+

Review Comment:
   I don't think AM should care about this proc, we should follow same approach 
from TruncateTableProcedure. 



##########
hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/TruncateRegionProcedure.java:
##########
@@ -0,0 +1,260 @@
+/*
+ * 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.hadoop.hbase.master.procedure;
+
+import java.io.IOException;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.hbase.HBaseIOException;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.client.RegionInfo;
+import org.apache.hadoop.hbase.master.MasterCoprocessorHost;
+import org.apache.hadoop.hbase.master.MasterFileSystem;
+import org.apache.hadoop.hbase.master.assignment.RegionStateNode;
+import org.apache.hadoop.hbase.master.assignment.TransitRegionStateProcedure;
+import org.apache.hadoop.hbase.procedure2.ProcedureStateSerializer;
+import org.apache.hadoop.hbase.regionserver.HRegionFileSystem;
+import org.apache.hadoop.hbase.util.CommonFSUtils;
+import org.apache.yetus.audience.InterfaceAudience;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
+import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos;
+import 
org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.TruncateRegionState;
+
[email protected]
+public class TruncateRegionProcedure
+  extends AbstractStateMachineRegionProcedure<TruncateRegionState> {
+  private static final Logger LOG = 
LoggerFactory.getLogger(TruncateRegionProcedure.class);
+
+  private RegionInfo region;
+  private RegionInfo newRegion;
+
+  @SuppressWarnings("unused")
+  public TruncateRegionProcedure() {
+    // Required by the Procedure framework to create the procedure on replay
+    super();
+  }
+
+  public TruncateRegionProcedure(final MasterProcedureEnv env, final 
RegionInfo hri)
+    throws HBaseIOException {
+    super(env, hri);
+    this.region = hri;
+    checkOnline(env, region);
+  }
+
+  @Override
+  protected Flow executeFromState(final MasterProcedureEnv env, 
TruncateRegionState state)
+    throws InterruptedException {
+    if (LOG.isTraceEnabled()) {
+      LOG.trace(this + " execute state=" + state);
+    }
+    try {
+      switch (state) {
+        case TRUNCATE_REGION_PRE_OPERATION:
+          if (!prepareTruncate()) {
+            assert isFailed() : "the truncate should have an exception here";
+            return Flow.NO_MORE_STATE;
+          }
+          checkOnline(env, region);
+          assert region.getReplicaId() == RegionInfo.DEFAULT_REPLICA_ID || 
isFailed()
+            : "Can't truncate replicas directly. "
+              + "Replicas are auto-truncated when their primary is truncated.";
+          preTruncate(env);
+          setNextState(TruncateRegionState.TRUNCATE_REGION_MAKE_OFFLINE);
+          break;
+        case TRUNCATE_REGION_MAKE_OFFLINE:
+          addChildProcedure(createUnAssignProcedures(env));
+          setNextState(TruncateRegionState.TRUNCATE_REGION_REMOVE);
+          break;
+        case TRUNCATE_REGION_REMOVE:
+          deleteRegionsFromFileSystem(env);
+          setNextState(TruncateRegionState.TRUNCATE_REGION_MAKE_ONLINE);
+          break;
+        case TRUNCATE_REGION_MAKE_ONLINE:
+          addChildProcedure(createAssignProcedures(env));
+          setNextState(TruncateRegionState.TRUNCATE_REGION_POST_OPERATION);
+          break;
+        case TRUNCATE_REGION_POST_OPERATION:
+          postTruncate(env);
+          LOG.debug("truncate '" + getTableName() + "' completed");
+          return Flow.NO_MORE_STATE;
+        default:
+          throw new UnsupportedOperationException("unhandled state=" + state);
+      }
+    } catch (IOException e) {
+      if (isRollbackSupported(state)) {
+        setFailure("master-truncate-table", e);
+      } else {
+        LOG.warn("Retriable error trying to truncate table=" + getTableName() 
+ " state=" + state,
+          e);
+      }
+    }
+    return Flow.HAS_MORE_STATE;
+  }
+
+  private TransitRegionStateProcedure 
createAssignProcedures(MasterProcedureEnv env)
+    throws IOException {
+    return createAssignProceduresForTruncateRegion(env, region);
+  }
+
+  private TransitRegionStateProcedure 
createUnAssignProcedures(MasterProcedureEnv env)
+    throws IOException {
+    return createUnassignProceduresForTruncateRegion(env, region);
+  }
+
+  private void deleteRegionsFromFileSystem(final MasterProcedureEnv env) 
throws IOException {
+    final MasterFileSystem mfs = env.getMasterServices().getMasterFileSystem();
+    final Path tableDir = CommonFSUtils.getTableDir(mfs.getRootDir(), 
getTableName());
+    final Configuration conf = env.getMasterConfiguration();
+    HRegionFileSystem.deleteRegionFromFileSystem(conf, mfs.getFileSystem(), 
tableDir, region);
+  }
+
+  @Override
+  protected void rollbackState(final MasterProcedureEnv env, final 
TruncateRegionState state)
+    throws IOException {
+    if (state == TruncateRegionState.TRUNCATE_REGION_PRE_OPERATION) {
+      // nothing to rollback, pre-truncate is just table-state checks.
+      return;
+    }
+    if (state == TruncateRegionState.TRUNCATE_REGION_MAKE_OFFLINE) {
+      addChildProcedure(createAssignProcedures(env));
+      return;
+    }
+    // The truncate doesn't have a rollback. The execution will succeed, at 
some point.
+    throw new UnsupportedOperationException("unhandled state=" + state);
+  }
+
+  @Override
+  protected void completionCleanup(final MasterProcedureEnv env) {
+    releaseSyncLatch();
+  }
+
+  @Override
+  protected boolean isRollbackSupported(final TruncateRegionState state) {
+    return state == TruncateRegionState.TRUNCATE_REGION_PRE_OPERATION;
+  }
+
+  @Override
+  protected TruncateRegionState getState(final int stateId) {
+    return TruncateRegionState.forNumber(stateId);
+  }
+
+  @Override
+  protected int getStateId(final TruncateRegionState state) {
+    return state.getNumber();
+  }
+
+  @Override
+  protected TruncateRegionState getInitialState() {
+    return TruncateRegionState.TRUNCATE_REGION_PRE_OPERATION;
+  }
+
+  @Override
+  public void toStringClassDetails(StringBuilder sb) {
+    sb.append(getClass().getSimpleName());
+    sb.append(" (region=");
+    sb.append(region.getRegionNameAsString());
+    sb.append(")");
+  }
+
+  @Override
+  protected void serializeStateData(ProcedureStateSerializer serializer) 
throws IOException {
+    super.serializeStateData(serializer);
+
+    MasterProcedureProtos.TruncateRegionStateData.Builder state =
+      MasterProcedureProtos.TruncateRegionStateData.newBuilder()
+        .setUserInfo(MasterProcedureUtil.toProtoUserInfo(getUser()));
+    if (region != null) {
+      state.setRegion(ProtobufUtil.toRegionInfo(region));
+    }
+    serializer.serialize(state.build());
+  }
+
+  @Override
+  protected void deserializeStateData(ProcedureStateSerializer serializer) 
throws IOException {
+    super.deserializeStateData(serializer);
+
+    MasterProcedureProtos.TruncateRegionStateData state =
+      
serializer.deserialize(MasterProcedureProtos.TruncateRegionStateData.class);
+    setUser(MasterProcedureUtil.toUserInfo(state.getUserInfo()));
+    if (state.getRegion() != null) {
+      region = ProtobufUtil.toRegionInfo(state.getRegion());
+    }
+    if (state.getNewRegion() != null) {
+      newRegion = ProtobufUtil.toRegionInfo(state.getNewRegion());
+    }
+  }
+
+  private boolean prepareTruncate() throws IOException {
+    if (getTableName().equals(TableName.META_TABLE_NAME)) {
+      throw new IOException("Can't truncate region in catalog tables");
+    }
+    return true;
+  }
+
+  private void preTruncate(final MasterProcedureEnv env) throws IOException {
+    final MasterCoprocessorHost cpHost = env.getMasterCoprocessorHost();
+    if (cpHost != null) {
+      cpHost.preTruncateRegionAction(region, getUser());
+    }
+  }
+
+  private void postTruncate(final MasterProcedureEnv env) throws IOException {
+    final MasterCoprocessorHost cpHost = env.getMasterCoprocessorHost();
+    if (cpHost != null) {
+      cpHost.postTruncateRegionAction(newRegion, getUser());
+    }
+  }
+
+  @Override
+  public TableOperationType getTableOperationType() {
+    return TableOperationType.REGION_TRUNCATE;
+  }
+
+  public static TransitRegionStateProcedure
+    createUnassignProceduresForTruncateRegion(MasterProcedureEnv env, 
RegionInfo region) {
+    RegionStateNode regionNode =
+      env.getAssignmentManager().getRegionStates().getRegionStateNode(region);
+    try {
+      regionNode.lock();
+      TransitRegionStateProcedure proc =
+        TransitRegionStateProcedure.unassign(env, regionNode.getRegionInfo());
+      regionNode.setProcedure(proc);
+      return proc;
+    } finally {
+      regionNode.unlock();
+    }
+  }
+
+  public static TransitRegionStateProcedure

Review Comment:
   Same as comment above.



##########
hbase-protocol-shaded/src/main/protobuf/server/master/MasterProcedure.proto:
##########
@@ -102,6 +102,20 @@ message TruncateTableStateData {
   repeated RegionInfo region_info = 5;
 }
 
+enum TruncateRegionState {
+  TRUNCATE_REGION_PRE_OPERATION = 1;
+  TRUNCATE_REGION_MAKE_OFFLINE = 2;
+  TRUNCATE_REGION_REMOVE = 3;
+  TRUNCATE_REGION_MAKE_ONLINE = 4;
+  TRUNCATE_REGION_POST_OPERATION = 5;
+}
+
+message TruncateRegionStateData {
+  required UserInformation user_info = 1;
+  required RegionInfo region = 2;
+  optional RegionInfo new_region = 3;

Review Comment:
   Why a new_region field? We should only remove existing region data, no?



##########
hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/TruncateRegionProcedure.java:
##########
@@ -0,0 +1,260 @@
+/*
+ * 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.hadoop.hbase.master.procedure;
+
+import java.io.IOException;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.hbase.HBaseIOException;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.client.RegionInfo;
+import org.apache.hadoop.hbase.master.MasterCoprocessorHost;
+import org.apache.hadoop.hbase.master.MasterFileSystem;
+import org.apache.hadoop.hbase.master.assignment.RegionStateNode;
+import org.apache.hadoop.hbase.master.assignment.TransitRegionStateProcedure;
+import org.apache.hadoop.hbase.procedure2.ProcedureStateSerializer;
+import org.apache.hadoop.hbase.regionserver.HRegionFileSystem;
+import org.apache.hadoop.hbase.util.CommonFSUtils;
+import org.apache.yetus.audience.InterfaceAudience;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
+import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos;
+import 
org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.TruncateRegionState;
+
[email protected]
+public class TruncateRegionProcedure
+  extends AbstractStateMachineRegionProcedure<TruncateRegionState> {
+  private static final Logger LOG = 
LoggerFactory.getLogger(TruncateRegionProcedure.class);
+
+  private RegionInfo region;
+  private RegionInfo newRegion;
+
+  @SuppressWarnings("unused")
+  public TruncateRegionProcedure() {
+    // Required by the Procedure framework to create the procedure on replay
+    super();
+  }
+
+  public TruncateRegionProcedure(final MasterProcedureEnv env, final 
RegionInfo hri)
+    throws HBaseIOException {
+    super(env, hri);
+    this.region = hri;
+    checkOnline(env, region);
+  }
+
+  @Override
+  protected Flow executeFromState(final MasterProcedureEnv env, 
TruncateRegionState state)
+    throws InterruptedException {
+    if (LOG.isTraceEnabled()) {
+      LOG.trace(this + " execute state=" + state);
+    }
+    try {
+      switch (state) {
+        case TRUNCATE_REGION_PRE_OPERATION:
+          if (!prepareTruncate()) {
+            assert isFailed() : "the truncate should have an exception here";
+            return Flow.NO_MORE_STATE;
+          }
+          checkOnline(env, region);
+          assert region.getReplicaId() == RegionInfo.DEFAULT_REPLICA_ID || 
isFailed()
+            : "Can't truncate replicas directly. "
+              + "Replicas are auto-truncated when their primary is truncated.";
+          preTruncate(env);
+          setNextState(TruncateRegionState.TRUNCATE_REGION_MAKE_OFFLINE);
+          break;
+        case TRUNCATE_REGION_MAKE_OFFLINE:
+          addChildProcedure(createUnAssignProcedures(env));
+          setNextState(TruncateRegionState.TRUNCATE_REGION_REMOVE);
+          break;
+        case TRUNCATE_REGION_REMOVE:
+          deleteRegionsFromFileSystem(env);
+          setNextState(TruncateRegionState.TRUNCATE_REGION_MAKE_ONLINE);
+          break;
+        case TRUNCATE_REGION_MAKE_ONLINE:
+          addChildProcedure(createAssignProcedures(env));
+          setNextState(TruncateRegionState.TRUNCATE_REGION_POST_OPERATION);
+          break;
+        case TRUNCATE_REGION_POST_OPERATION:
+          postTruncate(env);
+          LOG.debug("truncate '" + getTableName() + "' completed");
+          return Flow.NO_MORE_STATE;
+        default:
+          throw new UnsupportedOperationException("unhandled state=" + state);
+      }
+    } catch (IOException e) {
+      if (isRollbackSupported(state)) {
+        setFailure("master-truncate-table", e);
+      } else {
+        LOG.warn("Retriable error trying to truncate table=" + getTableName() 
+ " state=" + state,
+          e);
+      }
+    }
+    return Flow.HAS_MORE_STATE;
+  }
+
+  private TransitRegionStateProcedure 
createAssignProcedures(MasterProcedureEnv env)
+    throws IOException {
+    return createAssignProceduresForTruncateRegion(env, region);
+  }
+
+  private TransitRegionStateProcedure 
createUnAssignProcedures(MasterProcedureEnv env)
+    throws IOException {
+    return createUnassignProceduresForTruncateRegion(env, region);
+  }
+
+  private void deleteRegionsFromFileSystem(final MasterProcedureEnv env) 
throws IOException {
+    final MasterFileSystem mfs = env.getMasterServices().getMasterFileSystem();
+    final Path tableDir = CommonFSUtils.getTableDir(mfs.getRootDir(), 
getTableName());
+    final Configuration conf = env.getMasterConfiguration();

Review Comment:
   Don't we need region locking here?



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