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