# Script looks at hbase .META. table verifying its content is coherent. # # To see usage for this script, run: # # ${HBASE_HOME}/bin/hbase org.jruby.Main check_meta.rb --help # # Copyright 2010 The Apache Software Foundation # # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # include Java import org.apache.commons.logging.LogFactory import org.apache.hadoop.hbase.util.VersionInfo import org.apache.hadoop.hbase.HBaseConfiguration import org.apache.hadoop.fs.FileSystem import org.apache.hadoop.fs.Path import org.apache.hadoop.hbase.HConstants import org.apache.hadoop.hbase.util.FSUtils import org.apache.hadoop.hbase.client.HTable import org.apache.hadoop.hbase.client.Scan import org.apache.hadoop.hbase.util.Writables import org.apache.hadoop.hbase.HRegionInfo import org.apache.hadoop.hbase.util.Bytes import org.apache.hadoop.hbase.HTableDescriptor import org.apache.hadoop.hbase.client.Put # Name of this script NAME = 'check_meta' # Print usage for this script def usage puts 'Usage: %s.rb [--fix]' % NAME puts ' fix Try to fixup meta issues' puts 'Script checks consistency of the .META. table. It reports if .META. has missing entries.' puts 'If you pass "--fix", it will try looking in the filesystem for the dropped region and if it' puts 'finds a likely candidate, it will try pluggin the .META. hole.' exit! end def isFixup # Are we to do fixup during this run usage if ARGV.size > 1 fixup = nil if ARGV.size == 1 usage unless ARGV[0].downcase.match('--fix.*') fixup = 1 end return fixup end def getConfiguration hbase_twenty = VersionInfo.getVersion().match('0\.20\..*') # Get configuration to use. if hbase_twenty c = HBaseConfiguration.new() else c = HBaseConfiguration.create() end # Set hadoop filesystem configuration using the hbase.rootdir. # Otherwise, we'll always use localhost though the hbase.rootdir # might be pointing at hdfs location. Do old and new key for fs. c.set("fs.default.name", c.get(HConstants::HBASE_DIR)) c.set("fs.defaultFS", c.get(HConstants::HBASE_DIR)) return c end def fixup(leftEdge, rightEdge, metatable, fs, rootdir) plugged = nil # Try and fix the passed holes in meta. tabledir = HTableDescriptor::getTableDir(rootdir, leftEdge.getTableDesc().getName()) statuses = fs.listStatus(tabledir) for status in statuses next unless status.isDir() next if status.getPath().getName() == "compaction.dir" regioninfofile = Path.new(status.getPath(), ".regioninfo") unless fs.exists(regioninfofile) LOG.warn("Missing .regioninfo: " + regioninfofile.toString()) next end is = fs.open(regioninfofile) hri = HRegionInfo.new() hri.readFields(is) is.close() next unless Bytes.equals(leftEdge.getEndKey(), hri.getStartKey()) # TODO: Check against right edge to make sure this addition does not overflow right edge. # TODO: Check that the schema matches both left and right edges schemas. p = Put.new(hri.getRegionName()) p.add(HConstants::CATALOG_FAMILY, HConstants::REGIONINFO_QUALIFIER, Writables.getBytes(hri)) metatable.put(p) LOG.info("Plugged hole in .META. at: " + hri.toString()) plugged = true end return plugged end fixup = isFixup() # Get configuration conf = getConfiguration() # Filesystem fs = FileSystem.get(conf) # Rootdir rootdir = FSUtils.getRootDir(conf) # Get a logger and a metautils instance. LOG = LogFactory.getLog(NAME) # Scan the .META. looking for holes metatable = HTable.new(conf, HConstants::META_TABLE_NAME) scan = Scan.new() scanner = metatable.getScanner(scan) oldHRI = nil bad = nil while (result = scanner.next()) rowid = Bytes.toString(result.getRow()) rowidStr = java.lang.String.new(rowid) bytes = result.getValue(HConstants::CATALOG_FAMILY, HConstants::REGIONINFO_QUALIFIER) hri = Writables.getHRegionInfo(bytes) if oldHRI if oldHRI.isOffline() && Bytes.equals(oldHRI.getStartKey(), hri.getStartKey()) # Presume offlined parent elsif Bytes.equals(oldHRI.getEndKey(), hri.getStartKey()) # Start key of next matches end key of previous else LOG.warn("hole after " + oldHRI.toString()) if fixup bad = 1 unless fixup(oldHRI, hri, metatable, fs, rootdir) else bad = 1 end end end oldHRI = hri end scanner.close() if bad LOG.info(".META. has holes") else LOG.info(".META. is healthy") end # Return 0 if meta is good, else non-zero. exit bad