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; 019 020import java.io.IOException; 021import java.util.Arrays; 022import java.util.List; 023 024import org.apache.hadoop.conf.Configuration; 025import org.apache.hadoop.hbase.HConstants; 026import org.apache.hadoop.hbase.MetaTableAccessor; 027import org.apache.hadoop.hbase.TableName; 028import org.apache.hadoop.hbase.client.Put; 029import org.apache.hadoop.hbase.client.RegionInfo; 030import org.apache.hadoop.hbase.client.RegionInfoBuilder; 031import org.apache.hadoop.hbase.regionserver.HRegion; 032import org.apache.hadoop.hbase.util.Bytes; 033import org.apache.hadoop.hbase.util.FSUtils; 034import org.apache.hadoop.hbase.util.Pair; 035import org.apache.yetus.audience.InterfaceAudience; 036import org.slf4j.Logger; 037import org.slf4j.LoggerFactory; 038 039/** 040 * Server-side fixing of bad or inconsistent state in hbase:meta. 041 * Distinct from MetaTableAccessor because {@link MetaTableAccessor} is about low-level 042 * manipulations driven by the Master. This class MetaFixer is 043 * employed by the Master and it 'knows' about holes and orphan 044 * and encapsulates their fixing on behalf of the Master. 045 */ 046@InterfaceAudience.Private 047class MetaFixer { 048 private static final Logger LOG = LoggerFactory.getLogger(MetaFixer.class); 049 private final MasterServices masterServices; 050 051 MetaFixer(MasterServices masterServices) { 052 this.masterServices = masterServices; 053 } 054 055 void fix() throws IOException { 056 CatalogJanitor.Report report = this.masterServices.getCatalogJanitor().getLastReport(); 057 if (report == null) { 058 LOG.info("CatalogJanitor has not generated a report yet; run 'catalogjanitor_run' in " + 059 "shell or wait until CatalogJanitor chore runs."); 060 return; 061 } 062 fixHoles(report); 063 fixOverlaps(report); 064 } 065 066 /** 067 * If hole, it papers it over by adding a region in the filesystem and to hbase:meta. 068 * Does not assign. 069 * @return True if we fixed any 'holes'. 070 */ 071 boolean fixHoles(CatalogJanitor.Report report) throws IOException { 072 boolean result = false; 073 List<Pair<RegionInfo, RegionInfo>> holes = report.getHoles(); 074 if (holes.isEmpty()) { 075 LOG.debug("No holes."); 076 return result; 077 } 078 for (Pair<RegionInfo, RegionInfo> p: holes) { 079 RegionInfo ri = getHoleCover(p); 080 if (ri == null) { 081 continue; 082 } 083 Configuration configuration = this.masterServices.getConfiguration(); 084 HRegion.createRegionDir(configuration, ri, FSUtils.getRootDir(configuration)); 085 // If an error here, then we'll have a region in the filesystem but not 086 // in hbase:meta (if the below fails). Should be able to rerun the fix. 087 // The second call to createRegionDir will just go through. Idempotent. 088 Put put = MetaTableAccessor.makePutFromRegionInfo(ri, HConstants.LATEST_TIMESTAMP); 089 MetaTableAccessor.putsToMetaTable(this.masterServices.getConnection(), Arrays.asList(put)); 090 LOG.info("Fixed hole by adding {}; region is NOT assigned (assign to online).", ri); 091 result = true; 092 } 093 return result; 094 } 095 096 /** 097 * @return Calculated RegionInfo that covers the hole <code>hole</code> 098 */ 099 private RegionInfo getHoleCover(Pair<RegionInfo, RegionInfo> hole) { 100 RegionInfo holeCover = null; 101 RegionInfo left = hole.getFirst(); 102 RegionInfo right = hole.getSecond(); 103 if (left.getTable().equals(right.getTable())) { 104 // Simple case. 105 if (Bytes.compareTo(left.getEndKey(), right.getStartKey()) >= 0) { 106 LOG.warn("Skipping hole fix; left-side endKey is not less than right-side startKey; " + 107 "left=<{}>, right=<{}>", left, right); 108 return holeCover; 109 } 110 holeCover = buildRegionInfo(left.getTable(), left.getEndKey(), right.getStartKey()); 111 } else { 112 boolean leftUndefined = left.equals(RegionInfo.UNDEFINED); 113 boolean rightUnefined = right.equals(RegionInfo.UNDEFINED); 114 boolean last = left.isLast(); 115 boolean first = right.isFirst(); 116 if (leftUndefined && rightUnefined) { 117 LOG.warn("Skipping hole fix; both the hole left-side and right-side RegionInfos are " + 118 "UNDEFINED; left=<{}>, right=<{}>", left, right); 119 return holeCover; 120 } 121 if (leftUndefined || last) { 122 holeCover = buildRegionInfo(right.getTable(), HConstants.EMPTY_START_ROW, 123 right.getStartKey()); 124 } else if (rightUnefined || first) { 125 holeCover = buildRegionInfo(left.getTable(), left.getEndKey(), HConstants.EMPTY_END_ROW); 126 } else { 127 LOG.warn("Skipping hole fix; don't know what to do with left=<{}>, right=<{}>", 128 left, right); 129 return holeCover; 130 } 131 } 132 return holeCover; 133 } 134 135 private RegionInfo buildRegionInfo(TableName tn, byte [] start, byte [] end) { 136 return RegionInfoBuilder.newBuilder(tn).setStartKey(start).setEndKey(end).build(); 137 } 138 139 boolean fixOverlaps(CatalogJanitor.Report report) throws IOException { 140 boolean result = false; 141 List<Pair<RegionInfo, RegionInfo>> overlaps = report.getOverlaps(); 142 if (overlaps.isEmpty()) { 143 LOG.debug("No overlaps."); 144 return result; 145 } 146 for (Pair<RegionInfo, RegionInfo> p: overlaps) { 147 RegionInfo ri = getHoleCover(p); 148 if (ri == null) { 149 continue; 150 } 151 Configuration configuration = this.masterServices.getConfiguration(); 152 HRegion.createRegionDir(configuration, ri, FSUtils.getRootDir(configuration)); 153 // If an error here, then we'll have a region in the filesystem but not 154 // in hbase:meta (if the below fails). Should be able to rerun the fix. 155 // The second call to createRegionDir will just go through. Idempotent. 156 Put put = MetaTableAccessor.makePutFromRegionInfo(ri, HConstants.LATEST_TIMESTAMP); 157 MetaTableAccessor.putsToMetaTable(this.masterServices.getConnection(), Arrays.asList(put)); 158 LOG.info("Fixed hole by adding {}; region is NOT assigned (assign to online).", ri); 159 result = true; 160 } 161 return result; 162 } 163}