/** * 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. */ package org.apache.hadoop.hbase.catalog; import java.io.IOException; import java.net.ConnectException; import java.util.ArrayList; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.hbase.HConstants; import org.apache.hadoop.hbase.HRegionInfo; import org.apache.hadoop.hbase.NotAllMetaRegionsOnlineException; import org.apache.hadoop.hbase.ServerName; import org.apache.hadoop.hbase.client.Delete; import org.apache.hadoop.hbase.client.HTable; import org.apache.hadoop.hbase.client.Put; import org.apache.hadoop.hbase.util.Bytes; /** * Writes region and assignment information to <code>.META.</code>. * TODO: Put MetaReader and MetaEditor together; doesn't make sense having * them distinct. see HBASE-3475. */ @InterfaceAudience.Private public class MetaEditor { // TODO: Strip CatalogTracker from this class. Its all over and in the end // its only used to get its Configuration so we can get associated // Connection. private static final Log LOG = LogFactory.getLog(MetaEditor.class); /** * Generates and returns a Put containing the region into for the catalog table */ public static Put makePutFromRegionInfo(HRegionInfo regionInfo) throws IOException { Put put = new Put(regionInfo.getRegionName()); addRegionInfo(put, regionInfo); return put; } /** * Adds split daughters to the Put */ public static Put addDaughtersToPut(Put put, HRegionInfo splitA, HRegionInfo splitB) { if (splitA != null) { put.add(HConstants.CATALOG_FAMILY, HConstants.SPLITA_QUALIFIER, splitA.toByteArray()); } if (splitB != null) { put.add(HConstants.CATALOG_FAMILY, HConstants.SPLITB_QUALIFIER, splitB.toByteArray()); } return put; } /** * Put the passed <code>p</code> to the <code>.META.</code> table. * @param ct CatalogTracker on whose back we will ride the edit. * @param p Put to add to .META. * @throws IOException */ static void putToMetaTable(final CatalogTracker ct, final Put p) throws IOException { put(MetaReader.getMetaHTable(ct), p); } /** * Put the passed <code>p</code> to the <code>.META.</code> table. * @param ct CatalogTracker on whose back we will ride the edit. * @param p Put to add to .META. * @throws IOException */ static void putToRootTable(final CatalogTracker ct, final Put p) throws IOException { put(MetaReader.getRootHTable(ct), p); } /** * Put the passed <code>p</code> to a catalog table. * @param ct CatalogTracker on whose back we will ride the edit. * @param p Put to add * @throws IOException */ static void putToCatalogTable(final CatalogTracker ct, final Put p) throws IOException { HTable t = MetaReader.getCatalogHTable(ct, p.getRow()); put(t, p); } /** * @param t Table to use (will be closed when done). * @param p * @throws IOException */ private static void put(final HTable t, final Put p) throws IOException { try { t.put(p); } finally { t.close(); } } /** * Put the passed <code>ps</code> to the <code>.META.</code> table. * @param ct CatalogTracker on whose back we will ride the edit. * @param ps Put to add to .META. * @throws IOException */ static void putsToMetaTable(final CatalogTracker ct, final List<Put> ps) throws IOException { HTable t = MetaReader.getMetaHTable(ct); try { t.put(ps); } finally { t.close(); } } /** * Delete the passed <code>d</code> from the <code>.META.</code> table. * @param ct CatalogTracker on whose back we will ride the edit. * @param d Delete to add to .META. * @throws IOException */ static void deleteMetaTable(final CatalogTracker ct, final Delete d) throws IOException { HTable t = MetaReader.getMetaHTable(ct); try { t.delete(d); } finally { t.close(); } } /** * Adds a META row for the specified new region. * @param regionInfo region information * @throws IOException if problem connecting or updating meta */ public static void addRegionToMeta(CatalogTracker catalogTracker, HRegionInfo regionInfo) throws IOException { putToMetaTable(catalogTracker, makePutFromRegionInfo(regionInfo)); LOG.info("Added region " + regionInfo.getRegionNameAsString() + " to META"); } /** * Adds a META row for the specified new region to the given catalog table. The * HTable is not flushed or closed. * @param meta the HTable for META * @param regionInfo region information * @throws IOException if problem connecting or updating meta */ public static void addRegionToMeta(HTable meta, HRegionInfo regionInfo) throws IOException { addRegionToMeta(meta, regionInfo, null, null); } /** * Adds a (single) META row for the specified new region and its daughters. Note that this does * not add its daughter's as different rows, but adds information about the daughters * in the same row as the parent. Use * {@link #offlineParentInMeta(CatalogTracker, HRegionInfo, HRegionInfo, HRegionInfo)} * and {@link #addDaughter(CatalogTracker, HRegionInfo, ServerName)} if you want to do that. * @param meta the HTable for META * @param regionInfo region information * @param splitA first split daughter of the parent regionInfo * @param splitB second split daughter of the parent regionInfo * @throws IOException if problem connecting or updating meta */ public static void addRegionToMeta(HTable meta, HRegionInfo regionInfo, HRegionInfo splitA, HRegionInfo splitB) throws IOException { Put put = makePutFromRegionInfo(regionInfo); addDaughtersToPut(put, splitA, splitB); meta.put(put); if (LOG.isDebugEnabled()) { LOG.debug("Added region " + regionInfo.getRegionNameAsString() + " to META"); } } /** * Adds a META row for each of the specified new regions. * @param catalogTracker CatalogTracker * @param regionInfos region information list * @throws IOException if problem connecting or updating meta */ public static void addRegionsToMeta(CatalogTracker catalogTracker, List<HRegionInfo> regionInfos) throws IOException { List<Put> puts = new ArrayList<Put>(); for (HRegionInfo regionInfo : regionInfos) { puts.add(makePutFromRegionInfo(regionInfo)); } putsToMetaTable(catalogTracker, puts); LOG.info("Added " + puts.size() + " regions in META"); } /** * Offline parent in meta. * Used when splitting. * @param catalogTracker * @param parent * @param a Split daughter region A * @param b Split daughter region B * @throws NotAllMetaRegionsOnlineException * @throws IOException */ public static void offlineParentInMeta(CatalogTracker catalogTracker, HRegionInfo parent, final HRegionInfo a, final HRegionInfo b) throws NotAllMetaRegionsOnlineException, IOException { HRegionInfo copyOfParent = new HRegionInfo(parent); copyOfParent.setOffline(true); copyOfParent.setSplit(true); HTable meta = MetaReader.getMetaHTable(catalogTracker); try { addRegionToMeta(meta, copyOfParent, a, b); LOG.info("Offlined parent region " + parent.getRegionNameAsString() + " in META"); } finally { meta.close(); } } public static void addDaughter(final CatalogTracker catalogTracker, final HRegionInfo regionInfo, final ServerName sn) throws NotAllMetaRegionsOnlineException, IOException { Put put = new Put(regionInfo.getRegionName()); addRegionInfo(put, regionInfo); if (sn != null) addLocation(put, sn); putToMetaTable(catalogTracker, put); LOG.info("Added daughter " + regionInfo.getRegionNameAsString() + (sn == null? ", serverName=null": ", serverName=" + sn.toString())); } /** * Updates the location of the specified META region in ROOT to be the * specified server hostname and startcode. * <p> * Uses passed catalog tracker to get a connection to the server hosting * ROOT and makes edits to that region. * * @param catalogTracker catalog tracker * @param regionInfo region to update location of * @param sn Server name * @throws IOException * @throws ConnectException Usually because the regionserver carrying .META. * is down. * @throws NullPointerException Because no -ROOT- server connection */ public static void updateMetaLocation(CatalogTracker catalogTracker, HRegionInfo regionInfo, ServerName sn) throws IOException, ConnectException { updateLocation(catalogTracker, regionInfo, sn); } /** * Updates the location of the specified region in META to be the specified * server hostname and startcode. * <p> * Uses passed catalog tracker to get a connection to the server hosting * META and makes edits to that region. * * @param catalogTracker catalog tracker * @param regionInfo region to update location of * @param sn Server name * @throws IOException */ public static void updateRegionLocation(CatalogTracker catalogTracker, HRegionInfo regionInfo, ServerName sn) throws IOException { updateLocation(catalogTracker, regionInfo, sn); } /** * Updates the location of the specified region to be the specified server. * <p> * Connects to the specified server which should be hosting the specified * catalog region name to perform the edit. * * @param catalogTracker * @param regionInfo region to update location of * @param sn Server name * @throws IOException In particular could throw {@link java.net.ConnectException} * if the server is down on other end. */ private static void updateLocation(final CatalogTracker catalogTracker, HRegionInfo regionInfo, ServerName sn) throws IOException { Put put = new Put(regionInfo.getRegionName()); addLocation(put, sn); putToCatalogTable(catalogTracker, put); LOG.info("Updated row " + regionInfo.getRegionNameAsString() + " with server=" + sn); } /** * Deletes the specified region from META. * @param catalogTracker * @param regionInfo region to be deleted from META * @throws IOException */ public static void deleteRegion(CatalogTracker catalogTracker, HRegionInfo regionInfo) throws IOException { Delete delete = new Delete(regionInfo.getRegionName()); deleteMetaTable(catalogTracker, delete); LOG.info("Deleted region " + regionInfo.getRegionNameAsString() + " from META"); } /** * Deletes daughters references in offlined split parent. * @param catalogTracker * @param parent Parent row we're to remove daughter reference from * @throws NotAllMetaRegionsOnlineException * @throws IOException */ public static void deleteDaughtersReferencesInParent(CatalogTracker catalogTracker, final HRegionInfo parent) throws NotAllMetaRegionsOnlineException, IOException { Delete delete = new Delete(parent.getRegionName()); delete.deleteColumns(HConstants.CATALOG_FAMILY, HConstants.SPLITA_QUALIFIER); delete.deleteColumns(HConstants.CATALOG_FAMILY, HConstants.SPLITB_QUALIFIER); deleteMetaTable(catalogTracker, delete); LOG.info("Deleted daughters references, qualifier=" + Bytes.toStringBinary(HConstants.SPLITA_QUALIFIER) + " and qualifier=" + Bytes.toStringBinary(HConstants.SPLITB_QUALIFIER) + ", from parent " + parent.getRegionNameAsString()); } private static Put addRegionInfo(final Put p, final HRegionInfo hri) throws IOException { p.add(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER, hri.toByteArray()); return p; } private static Put addLocation(final Put p, final ServerName sn) { p.add(HConstants.CATALOG_FAMILY, HConstants.SERVER_QUALIFIER, Bytes.toBytes(sn.getHostAndPort())); p.add(HConstants.CATALOG_FAMILY, HConstants.STARTCODE_QUALIFIER, Bytes.toBytes(sn.getStartcode())); return p; } }