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.assignment; 019 020import java.io.IOException; 021import java.util.Collections; 022import java.util.List; 023import java.util.SortedMap; 024import java.util.TreeMap; 025 026import org.apache.hadoop.hbase.Cell; 027import org.apache.hadoop.hbase.CellBuilderFactory; 028import org.apache.hadoop.hbase.CellBuilderType; 029import org.apache.hadoop.hbase.HConstants; 030import org.apache.hadoop.hbase.HRegionLocation; 031import org.apache.hadoop.hbase.MetaTableAccessor; 032import org.apache.hadoop.hbase.RegionLocations; 033import org.apache.hadoop.hbase.ServerName; 034import org.apache.hadoop.hbase.TableName; 035import org.apache.hadoop.hbase.client.Put; 036import org.apache.hadoop.hbase.client.RegionInfo; 037import org.apache.hadoop.hbase.client.Result; 038import org.apache.hadoop.hbase.client.Table; 039import org.apache.hadoop.hbase.client.TableDescriptor; 040import org.apache.hadoop.hbase.master.MasterFileSystem; 041import org.apache.hadoop.hbase.master.MasterServices; 042import org.apache.hadoop.hbase.master.RegionState.State; 043import org.apache.hadoop.hbase.procedure2.Procedure; 044import org.apache.hadoop.hbase.procedure2.util.StringUtils; 045import org.apache.hadoop.hbase.util.Bytes; 046import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 047import org.apache.hadoop.hbase.wal.WALSplitUtil; 048import org.apache.hadoop.hbase.zookeeper.MetaTableLocator; 049import org.apache.yetus.audience.InterfaceAudience; 050import org.apache.zookeeper.KeeperException; 051import org.slf4j.Logger; 052import org.slf4j.LoggerFactory; 053 054import org.apache.hbase.thirdparty.com.google.common.base.Preconditions; 055 056/** 057 * Store Region State to hbase:meta table. 058 */ 059@InterfaceAudience.Private 060public class RegionStateStore { 061 private static final Logger LOG = LoggerFactory.getLogger(RegionStateStore.class); 062 063 /** The delimiter for meta columns for replicaIds > 0 */ 064 protected static final char META_REPLICA_ID_DELIMITER = '_'; 065 066 private final MasterServices master; 067 068 public RegionStateStore(final MasterServices master) { 069 this.master = master; 070 } 071 072 public interface RegionStateVisitor { 073 void visitRegionState(Result result, RegionInfo regionInfo, State state, 074 ServerName regionLocation, ServerName lastHost, long openSeqNum); 075 } 076 077 public void visitMeta(final RegionStateVisitor visitor) throws IOException { 078 MetaTableAccessor.fullScanRegions(master.getConnection(), new MetaTableAccessor.Visitor() { 079 final boolean isDebugEnabled = LOG.isDebugEnabled(); 080 081 @Override 082 public boolean visit(final Result r) throws IOException { 083 if (r != null && !r.isEmpty()) { 084 long st = 0; 085 if (LOG.isTraceEnabled()) { 086 st = System.currentTimeMillis(); 087 } 088 visitMetaEntry(visitor, r); 089 if (LOG.isTraceEnabled()) { 090 long et = System.currentTimeMillis(); 091 LOG.trace("[T] LOAD META PERF " + StringUtils.humanTimeDiff(et - st)); 092 } 093 } else if (isDebugEnabled) { 094 LOG.debug("NULL result from meta - ignoring but this is strange."); 095 } 096 return true; 097 } 098 }); 099 } 100 101 private void visitMetaEntry(final RegionStateVisitor visitor, final Result result) 102 throws IOException { 103 final RegionLocations rl = MetaTableAccessor.getRegionLocations(result); 104 if (rl == null) return; 105 106 final HRegionLocation[] locations = rl.getRegionLocations(); 107 if (locations == null) return; 108 109 for (int i = 0; i < locations.length; ++i) { 110 final HRegionLocation hrl = locations[i]; 111 if (hrl == null) continue; 112 113 final RegionInfo regionInfo = hrl.getRegion(); 114 if (regionInfo == null) continue; 115 116 final int replicaId = regionInfo.getReplicaId(); 117 final State state = getRegionState(result, regionInfo); 118 119 final ServerName lastHost = hrl.getServerName(); 120 final ServerName regionLocation = getRegionServer(result, replicaId); 121 final long openSeqNum = hrl.getSeqNum(); 122 123 // TODO: move under trace, now is visible for debugging 124 LOG.info( 125 "Load hbase:meta entry region={}, regionState={}, lastHost={}, " + 126 "regionLocation={}, openSeqNum={}", 127 regionInfo.getEncodedName(), state, lastHost, regionLocation, openSeqNum); 128 visitor.visitRegionState(result, regionInfo, state, regionLocation, lastHost, openSeqNum); 129 } 130 } 131 132 public void updateRegionLocation(RegionStateNode regionStateNode) 133 throws IOException { 134 if (regionStateNode.getRegionInfo().isMetaRegion()) { 135 updateMetaLocation(regionStateNode.getRegionInfo(), regionStateNode.getRegionLocation(), 136 regionStateNode.getState()); 137 } else { 138 long openSeqNum = regionStateNode.getState() == State.OPEN ? regionStateNode.getOpenSeqNum() 139 : HConstants.NO_SEQNUM; 140 updateUserRegionLocation(regionStateNode.getRegionInfo(), regionStateNode.getState(), 141 regionStateNode.getRegionLocation(), openSeqNum, 142 // The regionStateNode may have no procedure in a test scenario; allow for this. 143 regionStateNode.getProcedure() != null ? regionStateNode.getProcedure().getProcId() 144 : Procedure.NO_PROC_ID); 145 } 146 } 147 148 private void updateMetaLocation(RegionInfo regionInfo, ServerName serverName, State state) 149 throws IOException { 150 try { 151 MetaTableLocator.setMetaLocation(master.getZooKeeper(), serverName, regionInfo.getReplicaId(), 152 state); 153 } catch (KeeperException e) { 154 throw new IOException(e); 155 } 156 } 157 158 private void updateUserRegionLocation(RegionInfo regionInfo, State state, 159 ServerName regionLocation, long openSeqNum, 160 long pid) throws IOException { 161 long time = EnvironmentEdgeManager.currentTime(); 162 final int replicaId = regionInfo.getReplicaId(); 163 final Put put = new Put(MetaTableAccessor.getMetaKeyForRegion(regionInfo), time); 164 MetaTableAccessor.addRegionInfo(put, regionInfo); 165 final StringBuilder info = 166 new StringBuilder("pid=").append(pid).append(" updating hbase:meta row=") 167 .append(regionInfo.getEncodedName()).append(", regionState=").append(state); 168 if (openSeqNum >= 0) { 169 Preconditions.checkArgument(state == State.OPEN && regionLocation != null, 170 "Open region should be on a server"); 171 MetaTableAccessor.addLocation(put, regionLocation, openSeqNum, replicaId); 172 // only update replication barrier for default replica 173 if (regionInfo.getReplicaId() == RegionInfo.DEFAULT_REPLICA_ID && 174 hasGlobalReplicationScope(regionInfo.getTable())) { 175 MetaTableAccessor.addReplicationBarrier(put, openSeqNum); 176 info.append(", repBarrier=").append(openSeqNum); 177 } 178 info.append(", openSeqNum=").append(openSeqNum); 179 info.append(", regionLocation=").append(regionLocation); 180 } else if (regionLocation != null) { 181 // Ideally, if no regionLocation, write null to the hbase:meta but this will confuse clients 182 // currently; they want a server to hit. TODO: Make clients wait if no location. 183 put.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY) 184 .setRow(put.getRow()) 185 .setFamily(HConstants.CATALOG_FAMILY) 186 .setQualifier(getServerNameColumn(replicaId)) 187 .setTimestamp(put.getTimestamp()) 188 .setType(Cell.Type.Put) 189 .setValue(Bytes.toBytes(regionLocation.getServerName())) 190 .build()); 191 info.append(", regionLocation=").append(regionLocation); 192 } 193 put.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY) 194 .setRow(put.getRow()) 195 .setFamily(HConstants.CATALOG_FAMILY) 196 .setQualifier(getStateColumn(replicaId)) 197 .setTimestamp(put.getTimestamp()) 198 .setType(Cell.Type.Put) 199 .setValue(Bytes.toBytes(state.name())) 200 .build()); 201 LOG.info(info.toString()); 202 updateRegionLocation(regionInfo, state, put); 203 } 204 205 private void updateRegionLocation(RegionInfo regionInfo, State state, Put put) 206 throws IOException { 207 try (Table table = master.getConnection().getTable(TableName.META_TABLE_NAME)) { 208 table.put(put); 209 } catch (IOException e) { 210 // TODO: Revist!!!! Means that if a server is loaded, then we will abort our host! 211 // In tests we abort the Master! 212 String msg = String.format("FAILED persisting region=%s state=%s", 213 regionInfo.getShortNameToLog(), state); 214 LOG.error(msg, e); 215 master.abort(msg, e); 216 throw e; 217 } 218 } 219 220 private long getOpenSeqNumForParentRegion(RegionInfo region) throws IOException { 221 MasterFileSystem fs = master.getMasterFileSystem(); 222 long maxSeqId = WALSplitUtil.getMaxRegionSequenceId(master.getConfiguration(), region, 223 fs::getFileSystem, fs::getWALFileSystem); 224 return maxSeqId > 0 ? maxSeqId + 1 : HConstants.NO_SEQNUM; 225 } 226 227 // ============================================================================================ 228 // Update Region Splitting State helpers 229 // ============================================================================================ 230 public void splitRegion(RegionInfo parent, RegionInfo hriA, RegionInfo hriB, 231 ServerName serverName) throws IOException { 232 TableDescriptor htd = getTableDescriptor(parent.getTable()); 233 long parentOpenSeqNum = HConstants.NO_SEQNUM; 234 if (htd.hasGlobalReplicationScope()) { 235 parentOpenSeqNum = getOpenSeqNumForParentRegion(parent); 236 } 237 MetaTableAccessor.splitRegion(master.getConnection(), parent, parentOpenSeqNum, hriA, hriB, 238 serverName, getRegionReplication(htd)); 239 } 240 241 // ============================================================================================ 242 // Update Region Merging State helpers 243 // ============================================================================================ 244 public void mergeRegions(RegionInfo child, RegionInfo [] parents, ServerName serverName) 245 throws IOException { 246 TableDescriptor htd = getTableDescriptor(child.getTable()); 247 boolean globalScope = htd.hasGlobalReplicationScope(); 248 SortedMap<RegionInfo, Long> parentSeqNums = new TreeMap<>(); 249 for (RegionInfo ri: parents) { 250 parentSeqNums.put(ri, globalScope? getOpenSeqNumForParentRegion(ri): -1); 251 } 252 MetaTableAccessor.mergeRegions(master.getConnection(), child, parentSeqNums, 253 serverName, getRegionReplication(htd)); 254 } 255 256 // ============================================================================================ 257 // Delete Region State helpers 258 // ============================================================================================ 259 public void deleteRegion(final RegionInfo regionInfo) throws IOException { 260 deleteRegions(Collections.singletonList(regionInfo)); 261 } 262 263 public void deleteRegions(final List<RegionInfo> regions) throws IOException { 264 MetaTableAccessor.deleteRegionInfos(master.getConnection(), regions); 265 } 266 267 // ========================================================================== 268 // Table Descriptors helpers 269 // ========================================================================== 270 private boolean hasGlobalReplicationScope(TableName tableName) throws IOException { 271 return hasGlobalReplicationScope(getTableDescriptor(tableName)); 272 } 273 274 private boolean hasGlobalReplicationScope(TableDescriptor htd) { 275 return htd != null ? htd.hasGlobalReplicationScope() : false; 276 } 277 278 private int getRegionReplication(TableDescriptor htd) { 279 return htd != null ? htd.getRegionReplication() : 1; 280 } 281 282 private TableDescriptor getTableDescriptor(TableName tableName) throws IOException { 283 return master.getTableDescriptors().get(tableName); 284 } 285 286 // ========================================================================== 287 // Server Name 288 // ========================================================================== 289 290 /** 291 * Returns the {@link ServerName} from catalog table {@link Result} 292 * where the region is transitioning. It should be the same as 293 * {@link MetaTableAccessor#getServerName(Result,int)} if the server is at OPEN state. 294 * @param r Result to pull the transitioning server name from 295 * @return A ServerName instance or {@link MetaTableAccessor#getServerName(Result,int)} 296 * if necessary fields not found or empty. 297 */ 298 static ServerName getRegionServer(final Result r, int replicaId) { 299 final Cell cell = r.getColumnLatestCell(HConstants.CATALOG_FAMILY, 300 getServerNameColumn(replicaId)); 301 if (cell == null || cell.getValueLength() == 0) { 302 RegionLocations locations = MetaTableAccessor.getRegionLocations(r); 303 if (locations != null) { 304 HRegionLocation location = locations.getRegionLocation(replicaId); 305 if (location != null) { 306 return location.getServerName(); 307 } 308 } 309 return null; 310 } 311 return ServerName.parseServerName(Bytes.toString(cell.getValueArray(), 312 cell.getValueOffset(), cell.getValueLength())); 313 } 314 315 private static byte[] getServerNameColumn(int replicaId) { 316 return replicaId == 0 317 ? HConstants.SERVERNAME_QUALIFIER 318 : Bytes.toBytes(HConstants.SERVERNAME_QUALIFIER_STR + META_REPLICA_ID_DELIMITER 319 + String.format(RegionInfo.REPLICA_ID_FORMAT, replicaId)); 320 } 321 322 // ========================================================================== 323 // Region State 324 // ========================================================================== 325 326 /** 327 * Pull the region state from a catalog table {@link Result}. 328 * @return the region state, or null if unknown. 329 */ 330 public static State getRegionState(final Result r, RegionInfo regionInfo) { 331 Cell cell = r.getColumnLatestCell(HConstants.CATALOG_FAMILY, 332 getStateColumn(regionInfo.getReplicaId())); 333 if (cell == null || cell.getValueLength() == 0) { 334 return null; 335 } 336 337 String state = Bytes.toString(cell.getValueArray(), cell.getValueOffset(), 338 cell.getValueLength()); 339 try { 340 return State.valueOf(state); 341 } catch (IllegalArgumentException e) { 342 LOG.warn("BAD value {} in hbase:meta info:state column for region {} , " + 343 "Consider using HBCK2 setRegionState ENCODED_REGION_NAME STATE", 344 state, regionInfo.getEncodedName()); 345 return null; 346 } 347 } 348 349 private static byte[] getStateColumn(int replicaId) { 350 return replicaId == 0 351 ? HConstants.STATE_QUALIFIER 352 : Bytes.toBytes(HConstants.STATE_QUALIFIER_STR + META_REPLICA_ID_DELIMITER 353 + String.format(RegionInfo.REPLICA_ID_FORMAT, replicaId)); 354 } 355}