001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.master.procedure; 019 020import java.io.IOException; 021import org.apache.hadoop.fs.Path; 022import org.apache.hadoop.hbase.HBaseIOException; 023import org.apache.hadoop.hbase.MetaTableAccessor; 024import org.apache.hadoop.hbase.TableName; 025import org.apache.hadoop.hbase.TableNotDisabledException; 026import org.apache.hadoop.hbase.TableNotEnabledException; 027import org.apache.hadoop.hbase.TableNotFoundException; 028import org.apache.hadoop.hbase.UnknownRegionException; 029import org.apache.hadoop.hbase.client.DoNotRetryRegionException; 030import org.apache.hadoop.hbase.client.RegionInfo; 031import org.apache.hadoop.hbase.client.RegionOfflineException; 032import org.apache.hadoop.hbase.client.TableState; 033import org.apache.hadoop.hbase.master.MasterServices; 034import org.apache.hadoop.hbase.master.RegionState; 035import org.apache.hadoop.hbase.master.TableStateManager; 036import org.apache.hadoop.hbase.master.assignment.RegionStates; 037import org.apache.hadoop.hbase.procedure2.StateMachineProcedure; 038import org.apache.hadoop.hbase.security.User; 039import org.apache.hadoop.hbase.util.FSUtils; 040import org.apache.yetus.audience.InterfaceAudience; 041 042/** 043 * Base class for all the Table procedures that want to use a StateMachineProcedure. 044 * It provides helpers like basic locking, sync latch, and toStringClassDetails(). 045 */ 046@InterfaceAudience.Private 047public abstract class AbstractStateMachineTableProcedure<TState> 048 extends StateMachineProcedure<MasterProcedureEnv, TState> 049 implements TableProcedureInterface { 050 051 // used for compatibility with old clients 052 private final ProcedurePrepareLatch syncLatch; 053 054 private User user; 055 056 protected AbstractStateMachineTableProcedure() { 057 // Required by the Procedure framework to create the procedure on replay 058 syncLatch = null; 059 } 060 061 protected AbstractStateMachineTableProcedure(final MasterProcedureEnv env) { 062 this(env, null); 063 } 064 065 /** 066 * @param env Uses this to set Procedure Owner at least. 067 */ 068 protected AbstractStateMachineTableProcedure(final MasterProcedureEnv env, 069 final ProcedurePrepareLatch latch) { 070 if (env != null) { 071 this.user = env.getRequestUser(); 072 this.setOwner(user); 073 } 074 // used for compatibility with clients without procedures 075 // they need a sync TableExistsException, TableNotFoundException, TableNotDisabledException, ... 076 this.syncLatch = latch; 077 } 078 079 @Override 080 public abstract TableName getTableName(); 081 082 @Override 083 public abstract TableOperationType getTableOperationType(); 084 085 @Override 086 public void toStringClassDetails(final StringBuilder sb) { 087 sb.append(getClass().getSimpleName()); 088 sb.append(" table="); 089 sb.append(getTableName()); 090 } 091 092 @Override 093 protected boolean waitInitialized(MasterProcedureEnv env) { 094 return env.waitInitialized(this); 095 } 096 097 @Override 098 protected LockState acquireLock(final MasterProcedureEnv env) { 099 if (env.getProcedureScheduler().waitTableExclusiveLock(this, getTableName())) { 100 return LockState.LOCK_EVENT_WAIT; 101 } 102 return LockState.LOCK_ACQUIRED; 103 } 104 105 @Override 106 protected void releaseLock(final MasterProcedureEnv env) { 107 env.getProcedureScheduler().wakeTableExclusiveLock(this, getTableName()); 108 } 109 110 protected User getUser() { 111 return user; 112 } 113 114 protected void setUser(final User user) { 115 this.user = user; 116 } 117 118 protected void releaseSyncLatch() { 119 ProcedurePrepareLatch.releaseLatch(syncLatch, this); 120 } 121 122 /** 123 * Check whether a table is modifiable - exists and either offline or online with config set 124 * @param env MasterProcedureEnv 125 * @throws IOException 126 */ 127 protected void checkTableModifiable(final MasterProcedureEnv env) throws IOException { 128 // Checks whether the table exists 129 if (!MetaTableAccessor.tableExists(env.getMasterServices().getConnection(), getTableName())) { 130 throw new TableNotFoundException(getTableName()); 131 } 132 } 133 134 protected final Path getWALRegionDir(MasterProcedureEnv env, RegionInfo region) 135 throws IOException { 136 return FSUtils.getWALRegionDir(env.getMasterConfiguration(), 137 region.getTable(), region.getEncodedName()); 138 } 139 140 /** 141 * Check that cluster is up and master is running. Check table is modifiable. 142 * If <code>enabled</code>, check table is enabled else check it is disabled. 143 * Call in Procedure constructor so can pass any exception to caller. 144 * @param enabled If true, check table is enabled and throw exception if not. If false, do the 145 * inverse. If null, do no table checks. 146 */ 147 protected void preflightChecks(MasterProcedureEnv env, Boolean enabled) throws HBaseIOException { 148 MasterServices master = env.getMasterServices(); 149 if (!master.isClusterUp()) { 150 throw new HBaseIOException("Cluster not up!"); 151 } 152 if (master.isStopping() || master.isStopped()) { 153 throw new HBaseIOException("Master stopping=" + master.isStopping() + 154 ", stopped=" + master.isStopped()); 155 } 156 if (enabled == null) { 157 // Don't do any table checks. 158 return; 159 } 160 try { 161 // Checks table exists and is modifiable. 162 checkTableModifiable(env); 163 TableName tn = getTableName(); 164 TableStateManager tsm = master.getTableStateManager(); 165 TableState ts = tsm.getTableState(tn); 166 if (enabled) { 167 if (!ts.isEnabledOrEnabling()) { 168 throw new TableNotEnabledException(tn); 169 } 170 } else { 171 if (!ts.isDisabledOrDisabling()) { 172 throw new TableNotDisabledException(tn); 173 } 174 } 175 } catch (IOException ioe) { 176 if (ioe instanceof HBaseIOException) { 177 throw (HBaseIOException)ioe; 178 } 179 throw new HBaseIOException(ioe); 180 } 181 } 182 183 /** 184 * Check region is online. 185 */ 186 protected static void checkOnline(MasterProcedureEnv env, final RegionInfo ri) 187 throws DoNotRetryRegionException { 188 RegionStates regionStates = env.getAssignmentManager().getRegionStates(); 189 RegionState rs = regionStates.getRegionState(ri); 190 if (rs == null) { 191 throw new UnknownRegionException("No RegionState found for " + ri.getEncodedName()); 192 } 193 if (!rs.isOpened()) { 194 throw new DoNotRetryRegionException(ri.getEncodedName() + " is not OPEN; regionState=" + rs); 195 } 196 if (ri.isSplitParent()) { 197 throw new DoNotRetryRegionException(ri.getEncodedName() + 198 " is not online (splitParent=true)"); 199 } 200 if (ri.isSplit()) { 201 throw new DoNotRetryRegionException(ri.getEncodedName() + " has split=true"); 202 } 203 if (ri.isOffline()) { 204 // RegionOfflineException is not instance of DNRIOE so wrap it. 205 throw new DoNotRetryRegionException(new RegionOfflineException(ri.getEncodedName())); 206 } 207 } 208}