Java tutorial
/** * 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.cloudata.core.client; import java.io.IOException; import java.net.SocketTimeoutException; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.zookeeper.ZooKeeper; import org.cloudata.core.client.scanner.ScannerFactory; import org.cloudata.core.client.scanner.TableScanner; import org.cloudata.core.common.Constants; import org.cloudata.core.common.CloudataLock; import org.cloudata.core.common.conf.CloudataConf; import org.cloudata.core.common.ipc.RPCInterruptedException; import org.cloudata.core.tablet.ColumnValue; import org.cloudata.core.tablet.TableSchema; import org.cloudata.core.tablet.TableSchemaMap; import org.cloudata.core.tablet.TabletInfo; import org.cloudata.core.tabletserver.DataServiceProtocol; import org.cloudata.core.tabletserver.Tablet; /** * @author jindolk * */ public class TabletLocationCache { private static final Log LOG = LogFactory.getLog(TabletLocationCache.class.getName()); private static TabletLocationCache instance; private CloudataConf conf; private TabletInfo rootTablet; private DataServiceProtocol rootTabletServer; //TableName -> TableInfo private TableSchemaMap tableSchemas; protected TreeMap<Row.Key, TabletInfo> rootCache = new TreeMap<Row.Key, TabletInfo>(); protected TreeMap<Row.Key, TabletInfo> metaCache = new TreeMap<Row.Key, TabletInfo>(); private final CloudataLock lock = new CloudataLock(); private final CloudataLock cacheLock = new CloudataLock(); private ZooKeeper zk; public synchronized static TabletLocationCache getInstance(CloudataConf conf) throws IOException { if (instance == null) { instance = new TabletLocationCache(conf); try { instance.initRootTablet(); //instance.preLoadFromMetaTablet(); } catch (IOException e) { LOG.warn("cannot load ROOT tablet or META tablet info:" + e.getMessage(), e); } } return instance; } private TabletLocationCache(CloudataConf conf) throws IOException { this.conf = conf; this.zk = CTableManager.getZooKeeper(conf); tableSchemas = new TableSchemaMap(conf, zk); } public TableSchemaMap getTableInfos() { return tableSchemas; } protected TableSchema getTableSchema(String tableName) throws IOException { TableSchema tableSchema = null; if ((tableSchema = tableSchemas.get(tableName)) != null) { return tableSchema; } tableSchema = TableSchema.loadTableSchema(conf, zk, tableName); if (tableSchema == null) { return null; } if (!tableSchemas.putIfAbsent(tableName, tableSchema)) { return tableSchemas.get(tableName); } return tableSchema; } public void clearAllCache() { cacheLock.obtainWriteLock(); try { rootTablet = null; rootCache.clear(); metaCache.clear(); } finally { cacheLock.releaseWriteLock(); } tableSchemas.clear(); } public void clearTabletCache(String tableName, TabletInfo tabletInfo) { if (tabletInfo == null) { return; } cacheLock.obtainWriteLock(); try { if (Constants.TABLE_NAME_ROOT.equals(tableName)) { rootTablet = null; } else if (Constants.TABLE_NAME_META.equals(tableName)) { rootCache.clear(); metaCache.clear(); } else { TreeMap<Row.Key, TabletInfo> tempMetaCache = new TreeMap<Row.Key, TabletInfo>(); tempMetaCache.putAll(metaCache); for (Map.Entry<Row.Key, TabletInfo> entry : tempMetaCache.entrySet()) { Row.Key rowKey = entry.getKey(); TabletInfo entryTabletInfo = entry.getValue(); if (entryTabletInfo.equals(tabletInfo)) { metaCache.remove(rowKey); } } } } finally { cacheLock.releaseWriteLock(); } } public void clearTabletCache(String tableName, Row.Key rowKey, TabletInfo tabletInfo) { cacheLock.obtainWriteLock(); try { if (Constants.TABLE_NAME_ROOT.equals(tableName)) { rootTablet = null; } else if (Constants.TABLE_NAME_META.equals(tableName)) { if (tabletInfo == null) { //LOG.debug("clear all root cache"); rootCache.clear(); } else { removeFromCache(rootCache, Tablet.generateMetaRowKey(Constants.TABLE_NAME_META, rowKey), tabletInfo); } } else { if (tabletInfo == null) { //LOG.debug("clear all meta cache"); metaCache.clear(); } else { removeFromCache(metaCache, Tablet.generateMetaRowKey(tableName, rowKey), tabletInfo); } } } finally { cacheLock.releaseWriteLock(); } } public void removeTableSchemaCache(String tableName) { tableSchemas.remove(tableName); } protected TableSchema reloadTableSchema(String tableName) throws IOException { TableSchema tableSchema = TableSchema.loadTableSchema(conf, zk, tableName); tableSchemas.override(tableName, tableSchema); return tableSchema; } protected TabletInfo lookupRootTablet() throws IOException { initRootTablet(); return rootTablet; } /** * ROOT Tablet? META Tablet . * @param tableName * @param rowKey * @return * @throws IOException */ protected TabletInfo lookupMetaTablet(String tableName, Row.Key rowKey) throws IOException { Row.Key rootTableRowKey = Tablet.generateMetaRowKey(Constants.TABLE_NAME_META, rowKey); //cache? . TabletInfo tabletInfo = findFromCache(tableName, rootCache, rootTableRowKey, rowKey); if (tabletInfo != null) { //Cache? return tabletInfo; } // ? TabletServer if (tabletInfo == null) { //LOG.debug("Root Cache miss:" + tableName + "," + rowKey); initRootTablet(); if (rootTabletServer == null) { clearAllCache(); return null; } try { //ROOT Tablet META Tablet . ColumnValue columnValue = rootTabletServer.getCloestMetaData(rootTablet.getTabletName(), rootTableRowKey); if (columnValue == null) { clearTabletCache(rootTablet.getTableName(), rootTableRowKey, rootTablet); return null; } tabletInfo = new TabletInfo(); tabletInfo.readFields(columnValue.getValue()); addCache(rootCache, Tablet.generateMetaRowKey(Constants.TABLE_NAME_META, tabletInfo.getEndRowKey()), tabletInfo); } catch (Exception e) { //ROOT TabletServer? ? NULL? //null? ? ? retry . //clearTabletCache(rootTablet.getTableName(), rootTableRowKey, rootTablet); clearAllCache(); return null; } } return tabletInfo; } protected TabletInfo lookupUserTablet(String tableName, Row.Key rowKey) throws IOException { Row.Key metaTableRowKey = Tablet.generateMetaRowKey(tableName, rowKey); TabletInfo tabletInfo = findFromCache(tableName, metaCache, metaTableRowKey, rowKey); if (tabletInfo == null) { //LOG.debug("Cache missed:" + tableName + "," + rowKey + "," + metaTableRowKey); //META Tablet? . TabletInfo metaTablet = lookupMetaTablet(Constants.TABLE_NAME_META, metaTableRowKey); if (metaTablet == null) { //root cache clear //clearTabletCache(Constants.TABLE_NAME_META, metaTableRowKey, null); clearAllCache(); LOG.warn("Can't find meta tablet in looking up user tablet"); return null; } //UserTablet? . try { DataServiceProtocol metaTabletServer = CTableManager.connectTabletServer(metaTablet, conf); ColumnValue columnValue = metaTabletServer.getCloestMetaData(metaTablet.getTabletName(), metaTableRowKey); if (columnValue == null) { clearTabletCache(Constants.TABLE_NAME_META, metaTableRowKey, metaTablet); return null; } tabletInfo = new TabletInfo(); tabletInfo.readFields(columnValue.getValue()); if (tableName.equals(tabletInfo.getTableName())) { //System.out.println("Add Cache:" + rowKey + "," + tabletInfo); addCache(metaCache, Tablet.generateMetaRowKey(tableName, tabletInfo.getEndRowKey()), tabletInfo); return tabletInfo; } else { clearTabletCache(Constants.TABLE_NAME_META, metaTableRowKey, metaTablet); return null; } } catch (Exception e) { LOG.warn("Exception in reading meta:" + e.getMessage()); clearAllCache(); return null; } } else { //LOG.debug("Cache hits:" + tableName + "," + rowKey + "," + tabletInfo); return tabletInfo; } } private void addCache(TreeMap<Row.Key, TabletInfo> cache, Row.Key cacheRowKey, TabletInfo tabletInfo) { cacheLock.obtainWriteLock(); try { cache.put(cacheRowKey, tabletInfo); } finally { cacheLock.releaseWriteLock(); } } private void initRootTablet() throws IOException { cacheLock.obtainWriteLock(); try { if (rootTablet == null) { rootTablet = Tablet.getRootTabletInfo(conf, zk); if (rootTablet == null || rootTablet.getAssignedHostName() == null) { throw new IOException("Root Tablet is : " + rootTablet); } rootTabletServer = CTableManager.connectTabletServer(rootTablet, conf); if (!rootTabletServer.isServicedTablet(rootTablet.getTabletName())) { LOG.info("Invalid ROOT tablet server:" + rootTablet); rootTablet = null; rootTabletServer = null; return; } } } catch (Exception e) { rootTablet = null; rootTabletServer = null; LOG.debug("Error initRootTablet:" + e.getMessage(), e); throw new IOException("Error initRootTablet:" + e.getMessage(), e); } finally { cacheLock.releaseWriteLock(); } preLoadFromRootTablet(); } /** * ROOT Tablet? META Tablet . * @throws IOException */ private void preLoadFromRootTablet() throws IOException { TableScanner scanner = null; try { scanner = ScannerFactory.openScanner(conf, rootTablet, Constants.META_COLUMN_NAME_TABLETINFO); ScanCell scanCell = null; int count = 0; while ((scanCell = scanner.next()) != null) { TabletInfo tabletInfo = new TabletInfo(); tabletInfo.readFields(scanCell.getBytes()); Row.Key metaFormatRowKey = Tablet.generateMetaRowKey(Constants.TABLE_NAME_META, tabletInfo.getEndRowKey()); addCache(rootCache, metaFormatRowKey, tabletInfo); count++; if (count > 1000) { break; } } } finally { if (scanner != null) { scanner.close(); } } } /** * ??? ? ? META? ? ( 10000 ?) . * @throws IOException */ private void preLoadFromMetaTablet() throws IOException { List<TabletInfo> rootTablets = new ArrayList<TabletInfo>(); cacheLock.obtainReadLock(); try { rootTablets.addAll(rootCache.values()); } finally { cacheLock.releaseReadLock(); } int count = 0; for (TabletInfo eachRootTablet : rootTablets) { TableScanner scanner = null; try { scanner = ScannerFactory.openScanner(conf, eachRootTablet, Constants.META_COLUMN_NAME_TABLETINFO); ScanCell scanCell = null; while ((scanCell = scanner.next()) != null) { TabletInfo tabletInfo = new TabletInfo(); tabletInfo.readFields(scanCell.getBytes()); Row.Key metaFormatRowKey = Tablet.generateMetaRowKey(tabletInfo.getTableName(), tabletInfo.getEndRowKey()); addCache(metaCache, metaFormatRowKey, tabletInfo); count++; if (count > 10000) { break; } } } finally { if (scanner != null) { scanner.close(); } } } } private void removeFromCache(TreeMap<Row.Key, TabletInfo> cache, Row.Key cacheRowKey, TabletInfo removeTablet) { if (cache.containsKey(cacheRowKey)) { cache.remove(cacheRowKey); } SortedMap<Row.Key, TabletInfo> tailMap = cache.tailMap(cacheRowKey); if (tailMap.isEmpty()) { return; } Row.Key tailFirst = tailMap.firstKey(); TabletInfo tabletInfo = tailMap.get(tailFirst); if (tabletInfo.equals(removeTablet)) { cache.remove(tailFirst); } } protected TabletInfo findFromCache(String tableName, TreeMap<Row.Key, TabletInfo> cache, Row.Key cacheRowKey, Row.Key dataRowKey) { cacheLock.obtainReadLock(); try { if (cache.containsKey(cacheRowKey)) { TabletInfo tabletInfo = cache.get(cacheRowKey); if (tabletInfo.belongRowRange(cacheRowKey)) { return tabletInfo; } else { return null; } } SortedMap<Row.Key, TabletInfo> tailMap = cache.tailMap(cacheRowKey); if (tailMap.isEmpty()) { return null; } Row.Key tailFirst = tailMap.firstKey(); TabletInfo tabletInfo = tailMap.get(tailFirst); if (tableName.equals(tabletInfo.getTableName()) && tabletInfo.belongRowRange(dataRowKey)) { return tabletInfo; } else { return null; } } finally { cacheLock.releaseReadLock(); } } }