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


##########
hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/TruncateRegionProcedure.java:
##########
@@ -0,0 +1,239 @@
+/*
+ * 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;
+
+  @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);
+  }
+
+  public TruncateRegionProcedure(final MasterProcedureEnv env, final 
RegionInfo region,
+    ProcedurePrepareLatch latch) throws HBaseIOException {
+    super(env, region, latch);
+    this.region = region;
+    preflightChecks(env, true);
+  }
+
+  @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 void deleteRegionsFromFileSystem(final MasterProcedureEnv env) 
throws IOException {
+    RegionStateNode regionNode =
+      env.getAssignmentManager().getRegionStates().getRegionStateNode(region);
+    try {
+      regionNode.lock();
+      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);
+    } finally {
+      regionNode.unlock();
+    }
+  }
+
+  @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;

Review Comment:
   Rollback is supported for state 
TruncateRegionState.TRUNCATE_REGION_MAKE_OFFLINE also, right?



##########
hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/TruncateRegionProcedure.java:
##########
@@ -0,0 +1,239 @@
+/*
+ * 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;
+
+  @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);
+  }
+
+  public TruncateRegionProcedure(final MasterProcedureEnv env, final 
RegionInfo region,
+    ProcedurePrepareLatch latch) throws HBaseIOException {
+    super(env, region, latch);
+    this.region = region;
+    preflightChecks(env, true);
+  }
+
+  @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 void deleteRegionsFromFileSystem(final MasterProcedureEnv env) 
throws IOException {

Review Comment:
   nit: *Regions* or *Region*?



##########
hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/TruncateRegionProcedure.java:
##########
@@ -0,0 +1,239 @@
+/*
+ * 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);
+

Review Comment:
   We don't need this variable right? Method getRegion() should give the 
variable initialized via super call in constructor. Please refer 
SplitTableRegionProcedure



##########
hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/TruncateRegionProcedure.java:
##########
@@ -0,0 +1,239 @@
+/*
+ * 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;
+
+  @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);
+  }
+
+  public TruncateRegionProcedure(final MasterProcedureEnv env, final 
RegionInfo region,
+    ProcedurePrepareLatch latch) throws HBaseIOException {
+    super(env, region, latch);
+    this.region = region;
+    preflightChecks(env, true);
+  }
+
+  @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 void deleteRegionsFromFileSystem(final MasterProcedureEnv env) 
throws IOException {
+    RegionStateNode regionNode =
+      env.getAssignmentManager().getRegionStates().getRegionStateNode(region);
+    try {
+      regionNode.lock();
+      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);
+    } finally {
+      regionNode.unlock();
+    }
+  }
+
+  @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));

Review Comment:
   if failure occurs in TRUNCATE_REGION_MAKE_OFFLINE wouldn’t unaasign be not 
even be completed? will we not call assign on maybe already online region.



##########
hbase-protocol-shaded/src/main/protobuf/server/master/MasterProcedure.proto:
##########
@@ -102,6 +102,19 @@ 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;

Review Comment:
   nit: could we rename this to region_info, same is being used at most places



##########
hbase-shell/src/main/ruby/hbase/admin.rb:
##########
@@ -196,6 +196,16 @@ def split(table_or_region_name, split_point = nil)
       end
     end
 
+    
#----------------------------------------------------------------------------------------------
+    # Requests a region truncate
+    def truncate_region(region_name)
+      begin
+        
org.apache.hadoop.hbase.util.FutureUtils.get(@admin.truncateRegionAsync(region_name.to_java_bytes))
+      rescue java.lang.IllegalArgumentException, 
org.apache.hadoop.hbase.UnknownRegionException
+        @admin.truncate_region(region_name.to_java_bytes)

Review Comment:
   Do you mean NoServerForRegionExceptio, in which case does this throw 
UnknownRegionException?



##########
hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/TruncateRegionProcedure.java:
##########
@@ -0,0 +1,239 @@
+/*
+ * 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;
+
+  @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);
+  }
+
+  public TruncateRegionProcedure(final MasterProcedureEnv env, final 
RegionInfo region,
+    ProcedurePrepareLatch latch) throws HBaseIOException {
+    super(env, region, latch);
+    this.region = region;
+    preflightChecks(env, true);
+  }
+
+  @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 void deleteRegionsFromFileSystem(final MasterProcedureEnv env) 
throws IOException {
+    RegionStateNode regionNode =
+      env.getAssignmentManager().getRegionStates().getRegionStateNode(region);
+    try {
+      regionNode.lock();
+      final MasterFileSystem mfs = 
env.getMasterServices().getMasterFileSystem();
+      final Path tableDir = CommonFSUtils.getTableDir(mfs.getRootDir(), 
getTableName());
+      final Configuration conf = env.getMasterConfiguration();

Review Comment:
   nit: variable seems redundant



##########
hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/TruncateRegionProcedure.java:
##########
@@ -0,0 +1,239 @@
+/*
+ * 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;
+
+  @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);
+  }
+
+  public TruncateRegionProcedure(final MasterProcedureEnv env, final 
RegionInfo region,
+    ProcedurePrepareLatch latch) throws HBaseIOException {
+    super(env, region, latch);
+    this.region = region;
+    preflightChecks(env, true);
+  }
+
+  @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);

Review Comment:
   this string is same as in TruncateTableProcedure, may confuse, better change 
it?



##########
hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/TruncateRegionProcedure.java:
##########
@@ -0,0 +1,239 @@
+/*
+ * 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;
+
+  @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);
+  }
+
+  public TruncateRegionProcedure(final MasterProcedureEnv env, final 
RegionInfo region,
+    ProcedurePrepareLatch latch) throws HBaseIOException {
+    super(env, region, latch);
+    this.region = region;
+    preflightChecks(env, true);
+  }
+
+  @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 void deleteRegionsFromFileSystem(final MasterProcedureEnv env) 
throws IOException {
+    RegionStateNode regionNode =
+      env.getAssignmentManager().getRegionStates().getRegionStateNode(region);
+    try {
+      regionNode.lock();
+      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);
+    } finally {
+      regionNode.unlock();
+    }
+  }
+
+  @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));

Review Comment:
   Should be updated if region variable is removed, should call setRegionInfo 
of builder. Also null check is redundant 



##########
hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestAsyncRegionAdminApi2.java:
##########
@@ -283,4 +285,69 @@ private void splitTest(TableName tableName, int rowCount, 
boolean isSplitRegion,
     }
     assertEquals(2, count);
   }
+
+  @Test
+  public void testTruncateRegion() throws Exception {
+    // Arrange - Create table, insert data, identify region to truncate.
+    final byte[][] splitKeys =
+      new byte[][] { Bytes.toBytes("30"), Bytes.toBytes("60"), 
Bytes.toBytes("90") };
+    String family1 = "f1";
+    String family2 = "f2";
+
+    final String[] sFamilies = new String[] { family1, family2 };
+    final byte[][] bFamilies = new byte[][] { Bytes.toBytes(family1), 
Bytes.toBytes(family2) };
+    createTableWithDefaultConf(tableName, splitKeys, bFamilies);
+
+    AsyncTable<AdvancedScanResultConsumer> metaTable = 
ASYNC_CONN.getTable(META_TABLE_NAME);
+    List<HRegionLocation> regionLocations =
+      ClientMetaTableAccessor.getTableHRegionLocations(metaTable, 
tableName).get();
+    RegionInfo regionToBeTruncated = regionLocations.get(0).getRegion();
+
+    assertEquals(4, regionLocations.size());
+
+    AssignmentTestingUtil.insertData(TEST_UTIL, tableName, 2, 21, sFamilies);
+    AssignmentTestingUtil.insertData(TEST_UTIL, tableName, 2, 31, sFamilies);
+    AssignmentTestingUtil.insertData(TEST_UTIL, tableName, 2, 61, sFamilies);
+    AssignmentTestingUtil.insertData(TEST_UTIL, tableName, 2, 91, sFamilies);
+    int rowCountBeforeTruncate = TEST_UTIL.countRows(tableName);
+
+    // Act - Truncate the first region
+    admin.truncateRegion(regionToBeTruncated.getRegionName()).get();
+
+    // Assert
+    int rowCountAfterTruncate = TEST_UTIL.countRows(tableName);
+    assertNotEquals(rowCountBeforeTruncate, rowCountAfterTruncate);
+    int expectedRowCount = rowCountBeforeTruncate - 2;// Since region with 2 
rows was truncated.
+    assertEquals(expectedRowCount, rowCountAfterTruncate);
+  }
+
+  @Test
+  public void testTruncateReplicaRegionSNotAllowed() throws Exception {

Review Comment:
   nit: typo camelcase RegionS?



##########
hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/TruncateRegionProcedure.java:
##########
@@ -0,0 +1,239 @@
+/*
+ * 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;
+
+  @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);
+  }
+
+  public TruncateRegionProcedure(final MasterProcedureEnv env, final 
RegionInfo region,
+    ProcedurePrepareLatch latch) throws HBaseIOException {
+    super(env, region, latch);
+    this.region = region;
+    preflightChecks(env, true);
+  }
+
+  @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 void deleteRegionsFromFileSystem(final MasterProcedureEnv env) 
throws IOException {
+    RegionStateNode regionNode =
+      env.getAssignmentManager().getRegionStates().getRegionStateNode(region);
+    try {
+      regionNode.lock();
+      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);
+    } finally {
+      regionNode.unlock();
+    }
+  }
+
+  @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));

Review Comment:
   if failure occurs in TRUNCATE_REGION_MAKE_OFFLINE wouldn’t unaasign be not 
even be completed? will we not call assign on maybe already online region.



##########
hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestAsyncRegionAdminApi2.java:
##########
@@ -283,4 +285,69 @@ private void splitTest(TableName tableName, int rowCount, 
boolean isSplitRegion,
     }
     assertEquals(2, count);
   }
+
+  @Test
+  public void testTruncateRegion() throws Exception {

Review Comment:
   Could we add another test for for pre check failure for truncate region of 
meta table?



##########
hbase-shell/src/test/ruby/hbase/admin_test.rb:
##########
@@ -168,6 +168,17 @@ def teardown
 
     
#-------------------------------------------------------------------------------
 
+    define_test "truncate region should work" do
+      @t_name = 'hbase_shell_truncate_region'
+      drop_test_table(@t_name)
+      admin.create(@t_name, 'a', NUMREGIONS => 10, SPLITALGO => 
'HexStringSplit')
+      r1 = command(:locate_region, @t_name, '1')
+      region1 = r1.getRegion.getRegionNameAsString
+      command(:truncate_region, region1)

Review Comment:
   Add some assert here? 



##########
hbase-shell/src/main/ruby/shell/commands/truncate_region.rb:
##########
@@ -0,0 +1,36 @@
+#
+#
+# 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.
+#
+
+module Shell
+  module Commands
+    class TruncateRegion < Command
+      def help
+        <<-EOF
+truncate individual region.

Review Comment:
   nit: Start with caps?



##########
hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/TruncateRegionProcedure.java:
##########
@@ -0,0 +1,239 @@
+/*
+ * 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;
+
+  @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);
+  }
+
+  public TruncateRegionProcedure(final MasterProcedureEnv env, final 
RegionInfo region,
+    ProcedurePrepareLatch latch) throws HBaseIOException {
+    super(env, region, latch);
+    this.region = region;
+    preflightChecks(env, true);
+  }
+
+  @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 void deleteRegionsFromFileSystem(final MasterProcedureEnv env) 
throws IOException {
+    RegionStateNode regionNode =
+      env.getAssignmentManager().getRegionStates().getRegionStateNode(region);
+    try {
+      regionNode.lock();
+      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);
+    } finally {
+      regionNode.unlock();
+    }
+  }
+
+  @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());

Review Comment:
   Should be updated if region variable is removed, should call setRegion of 
super class



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