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.hbase.NamespaceDescriptor; 022import org.apache.hadoop.hbase.NamespaceNotFoundException; 023import org.apache.hadoop.hbase.constraint.ConstraintException; 024import org.apache.hadoop.hbase.master.TableNamespaceManager; 025import org.apache.hadoop.hbase.procedure2.ProcedureStateSerializer; 026import org.apache.yetus.audience.InterfaceAudience; 027import org.slf4j.Logger; 028import org.slf4j.LoggerFactory; 029 030import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 031import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos; 032import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.ModifyNamespaceState; 033 034/** 035 * The procedure to add a namespace to an existing table. 036 */ 037@InterfaceAudience.Private 038public class ModifyNamespaceProcedure 039 extends AbstractStateMachineNamespaceProcedure<ModifyNamespaceState> { 040 private static final Logger LOG = LoggerFactory.getLogger(ModifyNamespaceProcedure.class); 041 042 private NamespaceDescriptor oldNsDescriptor; 043 private NamespaceDescriptor newNsDescriptor; 044 private Boolean traceEnabled; 045 046 public ModifyNamespaceProcedure() { 047 this.oldNsDescriptor = null; 048 this.traceEnabled = null; 049 } 050 051 public ModifyNamespaceProcedure(final MasterProcedureEnv env, 052 final NamespaceDescriptor newNsDescriptor) { 053 this(env, newNsDescriptor, null); 054 } 055 056 public ModifyNamespaceProcedure(final MasterProcedureEnv env, 057 final NamespaceDescriptor newNsDescriptor, final ProcedurePrepareLatch latch) { 058 super(env, latch); 059 this.oldNsDescriptor = null; 060 this.newNsDescriptor = newNsDescriptor; 061 this.traceEnabled = null; 062 } 063 064 @Override 065 protected Flow executeFromState(final MasterProcedureEnv env, final ModifyNamespaceState state) 066 throws InterruptedException { 067 if (isTraceEnabled()) { 068 LOG.trace(this + " execute state=" + state); 069 } 070 071 try { 072 switch (state) { 073 case MODIFY_NAMESPACE_PREPARE: 074 boolean success = prepareModify(env); 075 releaseSyncLatch(); 076 if (!success) { 077 assert isFailed() : "Modify namespace should have an exception here"; 078 return Flow.NO_MORE_STATE; 079 } 080 setNextState(ModifyNamespaceState.MODIFY_NAMESPACE_UPDATE_NS_TABLE); 081 break; 082 case MODIFY_NAMESPACE_UPDATE_NS_TABLE: 083 insertIntoNSTable(env); 084 setNextState(ModifyNamespaceState.MODIFY_NAMESPACE_UPDATE_ZK); 085 break; 086 case MODIFY_NAMESPACE_UPDATE_ZK: 087 updateZKNamespaceManager(env); 088 return Flow.NO_MORE_STATE; 089 default: 090 throw new UnsupportedOperationException(this + " unhandled state=" + state); 091 } 092 } catch (IOException e) { 093 if (isRollbackSupported(state)) { 094 setFailure("master-modify-namespace", e); 095 } else { 096 LOG.warn("Retriable error trying to modify namespace=" + newNsDescriptor.getName() 097 + " (in state=" + state + ")", e); 098 } 099 } 100 return Flow.HAS_MORE_STATE; 101 } 102 103 @Override 104 protected void rollbackState(final MasterProcedureEnv env, final ModifyNamespaceState state) 105 throws IOException { 106 if (state == ModifyNamespaceState.MODIFY_NAMESPACE_PREPARE) { 107 // nothing to rollback, pre-modify is just checks. 108 // TODO: coprocessor rollback semantic is still undefined. 109 releaseSyncLatch(); 110 return; 111 } 112 113 // The procedure doesn't have a rollback. The execution will succeed, at some point. 114 throw new UnsupportedOperationException("unhandled state=" + state); 115 } 116 117 @Override 118 protected boolean isRollbackSupported(final ModifyNamespaceState state) { 119 switch (state) { 120 case MODIFY_NAMESPACE_PREPARE: 121 return true; 122 default: 123 return false; 124 } 125 } 126 127 @Override 128 protected ModifyNamespaceState getState(final int stateId) { 129 return ModifyNamespaceState.valueOf(stateId); 130 } 131 132 @Override 133 protected int getStateId(final ModifyNamespaceState state) { 134 return state.getNumber(); 135 } 136 137 @Override 138 protected ModifyNamespaceState getInitialState() { 139 return ModifyNamespaceState.MODIFY_NAMESPACE_PREPARE; 140 } 141 142 @Override 143 protected void serializeStateData(ProcedureStateSerializer serializer) throws IOException { 144 super.serializeStateData(serializer); 145 146 MasterProcedureProtos.ModifyNamespaceStateData.Builder modifyNamespaceMsg = 147 MasterProcedureProtos.ModifyNamespaceStateData.newBuilder() 148 .setNamespaceDescriptor(ProtobufUtil.toProtoNamespaceDescriptor(this.newNsDescriptor)); 149 if (this.oldNsDescriptor != null) { 150 modifyNamespaceMsg.setUnmodifiedNamespaceDescriptor( 151 ProtobufUtil.toProtoNamespaceDescriptor(this.oldNsDescriptor)); 152 } 153 serializer.serialize(modifyNamespaceMsg.build()); 154 } 155 156 @Override 157 protected void deserializeStateData(ProcedureStateSerializer serializer) throws IOException { 158 super.deserializeStateData(serializer); 159 160 MasterProcedureProtos.ModifyNamespaceStateData modifyNamespaceMsg = 161 serializer.deserialize(MasterProcedureProtos.ModifyNamespaceStateData.class); 162 newNsDescriptor = 163 ProtobufUtil.toNamespaceDescriptor(modifyNamespaceMsg.getNamespaceDescriptor()); 164 if (modifyNamespaceMsg.hasUnmodifiedNamespaceDescriptor()) { 165 oldNsDescriptor = 166 ProtobufUtil.toNamespaceDescriptor(modifyNamespaceMsg.getUnmodifiedNamespaceDescriptor()); 167 } 168 } 169 170 @Override 171 public TableOperationType getTableOperationType() { 172 return TableOperationType.EDIT; 173 } 174 175 @Override 176 protected String getNamespaceName() { 177 return newNsDescriptor.getName(); 178 } 179 180 /** 181 * Action before any real action of adding namespace. 182 * @param env MasterProcedureEnv 183 */ 184 private boolean prepareModify(final MasterProcedureEnv env) throws IOException { 185 if (getTableNamespaceManager(env).doesNamespaceExist(newNsDescriptor.getName()) == false) { 186 setFailure("master-modify-namespace", 187 new NamespaceNotFoundException(newNsDescriptor.getName())); 188 return false; 189 } 190 try { 191 getTableNamespaceManager(env).validateTableAndRegionCount(newNsDescriptor); 192 } catch (ConstraintException e) { 193 setFailure("master-modify-namespace", e); 194 return false; 195 } 196 197 // This is used for rollback 198 oldNsDescriptor = getTableNamespaceManager(env).get(newNsDescriptor.getName()); 199 return true; 200 } 201 202 /** 203 * Insert/update the row into namespace table 204 * @param env MasterProcedureEnv 205 */ 206 private void insertIntoNSTable(final MasterProcedureEnv env) throws IOException { 207 getTableNamespaceManager(env).insertIntoNSTable(newNsDescriptor); 208 } 209 210 /** 211 * Update ZooKeeper. 212 * @param env MasterProcedureEnv 213 */ 214 private void updateZKNamespaceManager(final MasterProcedureEnv env) throws IOException { 215 getTableNamespaceManager(env).updateZKNamespaceManager(newNsDescriptor); 216 } 217 218 private TableNamespaceManager getTableNamespaceManager(final MasterProcedureEnv env) { 219 return env.getMasterServices().getClusterSchema().getTableNamespaceManager(); 220 } 221 222 /** 223 * The procedure could be restarted from a different machine. If the variable is null, we need to 224 * retrieve it. 225 */ 226 private Boolean isTraceEnabled() { 227 if (traceEnabled == null) { 228 traceEnabled = LOG.isTraceEnabled(); 229 } 230 return traceEnabled; 231 } 232}