org.cloudata.core.tabletserver.DiskSSTable.java Source code

Java tutorial

Introduction

Here is the source code for org.cloudata.core.tabletserver.DiskSSTable.java

Source

/**
 * 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.tabletserver;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.cloudata.core.client.Cell;
import org.cloudata.core.client.CellFilter;
import org.cloudata.core.client.Row;
import org.cloudata.core.common.IdGenerator;
import org.cloudata.core.common.CStopWatch;
import org.cloudata.core.common.CloudataLock;
import org.cloudata.core.common.conf.CloudataConf;
import org.cloudata.core.common.exception.TestCaseTabletServerKillException;
import org.cloudata.core.fs.CloudataFileSystem;
import org.cloudata.core.fs.GPath;
import org.cloudata.core.tablet.ColumnValue;
import org.cloudata.core.tablet.TableSchema;
import org.cloudata.core.tablet.TabletInfo;
import org.cloudata.core.tablet.TableSchema.ColumnInfo;
import org.cloudata.core.tabletserver.TabletMapFile.MapFileReader;
import org.cloudata.core.tabletserver.TabletMapFile.MapFileWriter;

public class DiskSSTable {
    public static final Log LOG = LogFactory.getLog(DiskSSTable.class.getName());

    protected TabletInfo tabletInfo;

    //columnName, MapFiles
    protected Map<String, List<TabletMapFile>> mapFiles = Collections
            .synchronizedMap(new HashMap<String, List<TabletMapFile>>());

    //columnName, CacheTable
    protected Map<String, ColumnMemoryCache> columnMemoryCaches = Collections
            .synchronizedMap(new HashMap<String, ColumnMemoryCache>());

    protected Set<String> notUseColumnMemoryCache = Collections.synchronizedSet(new HashSet<String>());

    int maxMapFileCount = 0;

    long totalMapFileSize = 0;

    protected final CloudataLock lock = new CloudataLock();

    protected CloudataFileSystem fs;

    protected CloudataConf conf;

    protected TabletServer tabletServer;

    private int numOfVersion;

    private Tablet tablet;

    public void init(TabletServer tabletServer, CloudataConf conf, Tablet tablet, int numOfVersion) {
        this.tablet = tablet;
        this.tabletInfo = tablet.getTabletInfo();
        this.conf = conf;
        this.fs = CloudataFileSystem.get(conf);
        this.tabletServer = tabletServer;
        this.numOfVersion = numOfVersion;
    }

    public void clearAllMemory() {
        lock.obtainWriteLock();
        try {
            mapFiles.clear();
            columnMemoryCaches.clear();
            maxMapFileCount = 0;
            totalMapFileSize = 0;
        } finally {
            lock.releaseWriteLock();
        }
    }

    public TabletInfo getTabletInfo() {
        return tabletInfo;
    }

    //  public Map<String, List<TabletMapFile>> getMapFiles() {
    //    return mapFiles;
    //  }

    public Scanner[] getMapFileScanners(String columnName, Row.Key startRowKey, Row.Key endRowKey,
            CellFilter cellFilter) throws IOException {
        lock.obtainReadLock();
        try {
            List<TabletMapFile> tabletMapFiles = mapFiles.get(columnName);
            if (tabletMapFiles == null) {
                return null;
            }
            Scanner[] scanners = new Scanner[tabletMapFiles.size()];
            int index = 0;
            for (TabletMapFile eachMapFile : tabletMapFiles) {
                scanners[index] = eachMapFile.getMapFileReader(startRowKey, endRowKey, cellFilter);
                index++;
            }
            return scanners;
        } finally {
            lock.releaseReadLock();
        }
    }

    public void truncateColumn(String columnName) throws IOException {
        lock.obtainWriteLock();
        try {
            List<TabletMapFile> mapFileList = mapFiles.get(columnName);
            if (mapFileList != null) {
                for (TabletMapFile eachMapFile : mapFileList) {
                    eachMapFile.delete();
                }
            }
            mapFiles.remove(columnName);
            calculateMaxMapFileCount();
            calculateTotalMapFileSize();
        } finally {
            lock.releaseWriteLock();
        }
    }

    public SortedSet<MapFileIndexRecord> getMapFileIndex() {
        lock.obtainReadLock();
        try {
            SortedSet<MapFileIndexRecord> result = new TreeSet<MapFileIndexRecord>();
            for (List<TabletMapFile> eachMapFiles : mapFiles.values()) {
                for (TabletMapFile tabletMapFile : eachMapFiles) {
                    SortedSet<MapFileIndexRecord> indexRecords = tabletMapFile.getMapFileIndexRecords();
                    result.addAll(indexRecords);
                }
            }

            return result;
        } finally {
            lock.releaseReadLock();
        }
    }

    public void addTabletMapFile(String columnName, TabletMapFile mapFile) throws IOException {
        lock.obtainWriteLock();
        try {
            List<TabletMapFile> mapFileList = mapFiles.get(columnName);
            if (mapFileList == null) {
                mapFileList = Collections.synchronizedList(new ArrayList<TabletMapFile>());
                mapFiles.put(columnName, mapFileList);
            }

            mapFileList.add(mapFile);
            if (mapFileList.size() > this.maxMapFileCount) {
                this.maxMapFileCount = mapFileList.size();
            }
            calculateTotalMapFileSize();
        } finally {
            lock.releaseWriteLock();
        }
    }

    public void removeTabletMapFile(String columnName, TabletMapFile mapFile) throws IOException {
        lock.obtainWriteLock();
        try {
            List<TabletMapFile> mapFileList = mapFiles.get(columnName);
            if (mapFileList != null) {
                mapFileList.remove(mapFile);
            }
            calculateMaxMapFileCount();
            calculateTotalMapFileSize();
        } finally {
            lock.releaseWriteLock();
        }
    }

    public void load() throws IOException {
        try {
            if (fs.exists(Tablet.getTabletMajorCompactionTempPath(conf, tabletInfo))) {
                fs.delete(Tablet.getTabletMajorCompactionTempPath(conf, tabletInfo), true);
            }
            if (fs.exists(Tablet.getTabletMinorCompactionTempPath(conf, tabletInfo))) {
                fs.delete(Tablet.getTabletMinorCompactionTempPath(conf, tabletInfo), true);
            }
            if (fs.exists(Tablet.getTabletSplitTempPath(conf, tabletInfo))) {
                fs.delete(Tablet.getTabletSplitTempPath(conf, tabletInfo), true);
            }
        } catch (Exception e) {
            LOG.error("Can't delete temp file while loding:" + tabletInfo, e);
        }

        deleteUnusedFiles();

        GPath[] columnPaths = fs.list(Tablet.getTabletPath(conf, tabletInfo));

        if (columnPaths == null || columnPaths.length == 0) {
            return;
        }
        for (GPath eachColumnPath : columnPaths) {
            String pathName = eachColumnPath.getName();
            GPath[] mapFilePaths = fs.list(eachColumnPath);

            if (mapFilePaths == null || mapFilePaths.length == 0) {
                continue;
            }

            for (GPath eachMapFilePath : mapFilePaths) {
                TabletMapFile tabletMapFile = new TabletMapFile(conf, tabletInfo, pathName,
                        eachMapFilePath.getName(), numOfVersion);
                if (tabletMapFile.loadIndex()) {
                    addTabletMapFile(pathName, tabletMapFile);
                }
            }
        }

        //loadToMemoryCache();
    }

    public ColumnValue search(Row.Key rowKey, String columnName, Cell.Key columnKey) throws IOException {
        lock.obtainReadLock();
        try {
            List<TabletMapFile> tabletMapFiles = mapFiles.get(columnName);
            if (tabletMapFiles == null || tabletMapFiles.isEmpty()) {
                return null;
            }

            ValueCollection valueCollection = new ValueCollection();
            for (TabletMapFile tabletMapFile : tabletMapFiles) {
                valueCollection.addAll(tabletMapFile.scan(rowKey, columnKey), numOfVersion);
            }
            return valueCollection.get();
        } finally {
            lock.releaseReadLock();
        }
    }

    public ValueCollection searchAllVersion(Row.Key rowKey, String columnName, Cell.Key columnKey)
            throws IOException {
        lock.obtainReadLock();
        try {
            List<TabletMapFile> tabletMapFiles = mapFiles.get(columnName);
            if (tabletMapFiles == null || tabletMapFiles.isEmpty()) {
                return null;
            }

            ValueCollection valueCollection = new ValueCollection();
            for (TabletMapFile tabletMapFile : tabletMapFiles) {
                valueCollection.addAll(tabletMapFile.scan(rowKey, columnKey), numOfVersion);
            }
            if (valueCollection.isEmpty()) {
                return null;
            } else {
                return valueCollection;
            }
        } finally {
            lock.releaseReadLock();
        }
    }

    public Collection<ColumnValue> search(Row.Key rowKey, String columnName) throws IOException {
        //? Row.Key?   Cell.Key?  value .
        //??? Map ?? ? ?  ?  Map??   merge .
        lock.obtainReadLock();
        try {
            List<TabletMapFile> tabletMapFiles = mapFiles.get(columnName);

            if (tabletMapFiles == null || tabletMapFiles.isEmpty()) {
                return new ArrayList<ColumnValue>();
            }

            if (tabletMapFiles.size() == 1) {
                return tabletMapFiles.get(0).scan(rowKey);
            }

            MapFileReader[] mapFileReaders = new MapFileReader[tabletMapFiles.size()];
            int index = 0;
            for (TabletMapFile tabletMapFile : tabletMapFiles) {
                MapFileReader reader = tabletMapFile.getMapFileReader(rowKey, rowKey);
                mapFileReaders[index++] = reader;
            }

            //merge
            return mergeColumnValueFromMapFile(mapFileReaders);
        } finally {
            lock.releaseReadLock();
        }
    }

    private List<ColumnValue> mergeColumnValueFromMapFile(MapFileReader[] mapFileReaders) throws IOException {
        List<ColumnValueEntry> workPlace = new ArrayList<ColumnValueEntry>(mapFileReaders.length);

        //
        for (int i = 0; i < mapFileReaders.length; i++) {
            if (mapFileReaders[i] != null) {
                ColumnValue columnValue = mapFileReaders[i].next();
                if (columnValue != null) {
                    workPlace.add(new ColumnValueEntry(columnValue, i));
                } else {
                    mapFileReaders[i].close();
                    mapFileReaders[i] = null;
                }
            }
        }

        List<ColumnValue> result = new ArrayList<ColumnValue>();

        boolean end = false;
        while (!end) {
            int size = workPlace.size();
            if (size == 0) {
                break;
            }

            Collections.sort(workPlace);

            ColumnValueEntry winner = workPlace.remove(0);
            result.add(winner.columnValue);

            ColumnValue columnValue = mapFileReaders[winner.index].next();

            if (columnValue != null) {
                workPlace.add(new ColumnValueEntry(columnValue, winner.index));
            } else {
                mapFileReaders[winner.index].close();
                mapFileReaders[winner.index] = null;

                end = true;
                for (int i = 0; i < mapFileReaders.length; i++) {
                    if (mapFileReaders[i] != null) {
                        columnValue = mapFileReaders[i].next();
                        if (columnValue != null) {
                            workPlace.add(new ColumnValueEntry(columnValue, i));
                            end = false;
                            break;
                        } else {
                            mapFileReaders[i].close();
                            mapFileReaders[i] = null;
                        }
                    }
                }
            }
        } //end of while

        return result;
    }

    static class ColumnValueEntry implements Comparable<ColumnValueEntry> {
        ColumnValue columnValue;
        int index;

        public ColumnValueEntry(ColumnValue columnValue, int index) {
            this.columnValue = columnValue;
            this.index = index;
        }

        public int compareTo(ColumnValueEntry entry) {
            return columnValue.compareTo(entry.columnValue);
        }

        public boolean equals(ColumnValueEntry obj) {
            if (!(obj instanceof ColumnValueEntry)) {
                return false;
            }

            return compareTo((ColumnValueEntry) obj) == 0;
        }
    }

    /**
     * minor Compaction ?  ??  ? ? ?    
     * @throws IOException
     */
    public void cancelCompaction(String fileId) throws IOException {
        lock.obtainWriteLock();
        try {
            for (Map.Entry<String, List<TabletMapFile>> entry : mapFiles.entrySet()) {
                List<TabletMapFile> columnMapFiles = entry.getValue();
                List<TabletMapFile> tempColumnMapFiles = new ArrayList<TabletMapFile>();
                tempColumnMapFiles.addAll(columnMapFiles);
                for (TabletMapFile mapFile : tempColumnMapFiles) {
                    if (fileId.equals(mapFile.getFileId())) {
                        //1. ?  
                        columnMapFiles.remove(mapFile);
                        //2.?
                        mapFile.delete();
                    }
                }
            }

            calculateMaxMapFileCount();
            calculateTotalMapFileSize();
        } finally {
            lock.releaseWriteLock();
        }
    }

    /**
     * 
     * @throws IOException
     */
    public void majorCompaction() throws IOException {
        //deleteUnusedFiles();

        Map<String, List<TabletMapFile>> oldMapFiles = new HashMap<String, List<TabletMapFile>>();

        //merge target
        CStopWatch watch = new CStopWatch();
        lock.obtainReadLock();

        try {
            for (Map.Entry<String, List<TabletMapFile>> entry : mapFiles.entrySet()) {
                String columnName = entry.getKey();
                List<TabletMapFile> eachMapFiles = entry.getValue();
                if (eachMapFiles.size() > 1) {
                    List<TabletMapFile> cloneMapFiles = new ArrayList<TabletMapFile>(10);
                    cloneMapFiles.addAll(eachMapFiles);

                    oldMapFiles.put(columnName, cloneMapFiles);
                }
            }
        } finally {
            lock.releaseReadLock();
        }

        // ? Map ??  ID
        String fileId = IdGenerator.getId();

        //merge
        Map<String, TabletMapFile> mergedMapFiles = merge(oldMapFiles, fileId);

        //move new
        moveMergedFile(mergedMapFiles);

        //add new map file list and remove old file
        try {
            addMergedFiles(oldMapFiles, mergedMapFiles);
        } catch (Exception e) {
            //  ? TabletServer shutdown ( ?  ? ?)
            LOG.fatal("TabletServer shutdowned cause :" + e.getMessage(), e);
            tabletServer.shutdown();
            return;
        }

        //delete old file
        // ? kill?  ? ????

        watch.start("delete old files in major compaction", 1000);
        for (Map.Entry<String, TabletMapFile> entry : mergedMapFiles.entrySet()) {
            String columnName = entry.getKey();
            for (TabletMapFile oldMapFile : oldMapFiles.get(columnName)) {
                try {
                    boolean deleteResult = oldMapFile.delete();
                    if (!deleteResult) {
                        LOG.warn("Fail deleting Old MapFile while MajorCompaction: " + oldMapFile);
                    }
                } catch (Exception e) {
                    //delete  ??? ? majorCompaction  ? ?? ?? merge?.
                    LOG.error("merge:delete old file:" + oldMapFile, e);
                }
            }
            watch.stopAndReportIfExceed(LOG);
        }

        //delete temp dir
        fs.delete(Tablet.getTabletMajorCompactionTempPath(conf, tabletInfo), true);
    }

    public void addMergedFiles(Map<String, List<TabletMapFile>> targetMapFiles,
            Map<String, TabletMapFile> mergedMapFiles) throws IOException {
        CStopWatch watch = new CStopWatch();
        watch.start("addMergedFiles lock.obtainWriteLock", 50);
        lock.obtainWriteLock();
        watch.stopAndReportIfExceed(LOG);
        try {
            for (Map.Entry<String, TabletMapFile> entry : mergedMapFiles.entrySet()) {
                String columnName = entry.getKey();
                TabletMapFile mergedMapFile = entry.getValue();

                // ?? map ? 
                List<TabletMapFile> runningMapFiles = mapFiles.get(columnName);
                if (runningMapFiles != null) {
                    runningMapFiles.add(mergedMapFile);
                }

                // map file ? 
                for (TabletMapFile mergeTargetMapFile : targetMapFiles.get(columnName)) {
                    runningMapFiles.remove(mergeTargetMapFile);
                }
            }

            calculateMaxMapFileCount();
            calculateTotalMapFileSize();
        } finally {
            lock.releaseWriteLock();
        }
    }

    public void moveMergedFile(Map<String, TabletMapFile> mergedMapFiles) throws IOException {
        try {
            for (Map.Entry<String, TabletMapFile> entry : mergedMapFiles.entrySet()) {
                String columnName = entry.getKey();
                TabletMapFile mergedMapFile = entry.getValue();
                //FIXME  move? exception ?? ? kill? ?  ?.
                moveMajorTempToTablet(columnName, mergedMapFile);
            }
        } catch (TestCaseTabletServerKillException err) {
            LOG.error("merge error:move:" + tabletInfo.getTabletName(), err);
            throw new IOException(err.getMessage());
        } catch (Exception e) {
            LOG.error("merge error:move:" + tabletInfo.getTabletName(), e);
            // ?  ???  ? .
            for (Map.Entry<String, TabletMapFile> entry : mergedMapFiles.entrySet()) {
                TabletMapFile mergedMapFile = entry.getValue();
                try {
                    if (mergedMapFile.delete()) {
                        LOG.error("merge error:can't delete temp file cause error:" + mergedMapFile.getFilePath());
                    }
                } catch (Exception e1) {
                    LOG.error("merge error:delete temp file cause error", e1);
                }
            }

            //temp? ? .
            fs.delete(Tablet.getTabletMajorCompactionTempPath(conf, tabletInfo), true);
            throw new IOException(e.getMessage());
        }
    }

    public Map<String, TabletMapFile> merge(Map<String, List<TabletMapFile>> targetMapFiles, String fileId)
            throws IOException {
        Map<String, TabletMapFile> mergedMapFiles = new HashMap<String, TabletMapFile>(5);
        try {
            for (Map.Entry<String, List<TabletMapFile>> entry : targetMapFiles.entrySet()) {
                String columnName = entry.getKey();
                List<TabletMapFile> entryMapFiles = entry.getValue();
                long totalDataFileSize = 0;

                for (TabletMapFile mapFile : entryMapFiles) {
                    totalDataFileSize += mapFile.getDataFileSize();
                }

                long startTime = System.currentTimeMillis();
                TabletMapFile resultFile = mergeMapFiles(fileId, columnName, entryMapFiles);
                //LOG.debug("merge:" + tabletInfo.getTabletName() + ", time=" + (System.currentTimeMillis() - startTime) + ", mapfile.count=" + entryMapFiles.size() + ", mapfile.size=" + totalDataFileSize);
                mergedMapFiles.put(columnName, resultFile);
                //targetMapFiles.put(columnName, mapFiles);
            }
        } catch (Exception e) {
            // ? major compaciton temp?    
            LOG.error("merge error:merge:" + tabletInfo.getTabletName(), e);
            fs.delete(Tablet.getTabletMajorCompactionTempPath(conf, tabletInfo), true);
            throw new IOException(e.getMessage());
        }
        return mergedMapFiles;
    }

    public TabletMapFile mergeMapFiles(String fileId, String columnName, List<TabletMapFile> mergeMapFiles)
            throws IOException {
        GPath resultPath = new GPath(Tablet.getTabletMajorCompactionTempPath(conf, tabletInfo),
                columnName + "/" + fileId + "/");
        return mergeColumnMapFiles(conf, CloudataFileSystem.get(conf), resultPath, tabletInfo, fileId, columnName,
                mergeMapFiles, numOfVersion);
    }

    public static TabletMapFile mergeColumnMapFiles(CloudataConf conf, CloudataFileSystem resultFs,
            GPath resultPath, TabletInfo tabletInfo, String fileId, String columnName,
            List<TabletMapFile> mergeMapFiles, int numOfVersion) throws IOException {
        FileMerger merger = new FileMerger();

        TabletMapFile resultFile = new TabletMapFile(conf, resultFs, tabletInfo, columnName, fileId, resultPath,
                numOfVersion);

        merger.merge(resultFile, mergeMapFiles.toArray(new TabletMapFile[mergeMapFiles.size()]), false,
                numOfVersion);
        return resultFile;
    }

    /**
     * ??? tabletserver? ? ? mapfile   ? ?  ??   
     * @throws IOException
     */
    private void deleteUnusedFiles() throws IOException {
        //FIXME MinorCompation? ?  ?? deleteUb=nusedFiles? ? ??

        //MajonCompaction  compaciton   ?? ? ?  ? ? ?   
        //MajorCompaction  ? MapFile ??  ?? .     
        //LOG.debug("execute deleteUnusedFiles");
        GPath tabletPath = Tablet.getTabletPath(conf, tabletInfo);
        GPath[] columnPaths = fs.list(tabletPath);

        if (columnPaths == null) {
            return;
        }

        for (int i = 0; i < columnPaths.length; i++) {
            try {
                if (!fs.isDirectory(columnPaths[i])) {
                    continue;
                }
                String columnName = columnPaths[i].getName();
                Set<String> validColumnMapFilesSet = new HashSet<String>();

                lock.obtainReadLock();
                try {
                    List<TabletMapFile> columnMapFiles = mapFiles.get(columnName);

                    if (columnMapFiles != null) {
                        for (TabletMapFile eachMapFile : columnMapFiles) {
                            validColumnMapFilesSet.add(eachMapFile.getFilePath().getName());
                        }
                    }
                } finally {
                    lock.releaseReadLock();
                }

                // ? ??  ?.
                GPath[] mapFilePaths = fs.list(columnPaths[i]);
                if (mapFilePaths == null) {
                    continue;
                }
                GPath tempPath = new GPath(Tablet.getTabletPath(conf, tabletInfo) + "/temp");
                for (int j = 0; j < mapFilePaths.length; j++) {
                    if (validColumnMapFilesSet.size() > 0) {
                        //Minor, Major Compacition  ??  
                        if (!validColumnMapFilesSet.contains(mapFilePaths[j].getName())
                                && !mapFilePaths[j].toString().startsWith(tempPath.toString())) {
                            LOG.info("deleteUnusedFiles1: delete file: " + mapFilePaths[j]);
                            fs.delete(mapFilePaths[j], true);
                        }
                    } else {
                        //Tablet    (map file, idx file ?    )
                        if (mapFilePaths[j].toString().startsWith(tempPath.toString())) {
                            continue;
                        }
                        if (!fs.exists(new GPath(mapFilePaths[j] + "/" + TabletMapFile.DATA_FILE))
                                || !fs.exists(new GPath(mapFilePaths[j] + "/" + TabletMapFile.DATA_FILE))) {
                            LOG.info("deleteUnusedFiles2: delete file: " + mapFilePaths[j]);
                            fs.delete(mapFilePaths[j], true);
                        }
                    }
                }
            } catch (Exception e) {
                LOG.error("deleteUnusedFiles error: " + columnPaths[i] + ": " + e.getMessage());
            }
        }

        for (int i = 0; i < columnPaths.length; i++) {
            GPath[] childPaths = fs.list(columnPaths[i]);
            if (childPaths == null || childPaths.length == 0) {
                fs.delete(columnPaths[i]);
            }
        }

        calculateMaxMapFileCount();
        calculateTotalMapFileSize();
    }

    public void moveMajorTempToTablet(String columnName, TabletMapFile tabletMapFile) throws IOException {
        GPath targetPath = new GPath(Tablet.getTabletPath(conf, tabletInfo),
                columnName + "/" + tabletMapFile.getFileId() + "/");
        tabletMapFile.moveTo(targetPath, true);
    }

    private void calculateMaxMapFileCount() {
        maxMapFileCount = 0;
        try {
            for (List<TabletMapFile> mapFile : mapFiles.values()) {
                if (mapFile.size() > maxMapFileCount) {
                    maxMapFileCount = mapFile.size();
                }
            }
        } catch (Exception e) {
            LOG.warn("Error in calculating max map file count", e);
        }
    }

    public int getMaxMapFileCount() {
        return maxMapFileCount;
    }

    public ColumnValue findClosestMeta(Row.Key rowKey, String columnName, boolean great) throws IOException {
        lock.obtainReadLock();
        try {
            if (columnMemoryCaches.containsKey(columnName)) {
                ColumnMemoryCache cache = columnMemoryCaches.get(columnName);
                return cache.findClosest(rowKey);
            }
            List<TabletMapFile> tabletMapFiles = mapFiles.get(columnName);
            if (tabletMapFiles == null || tabletMapFiles.isEmpty()) {
                return null;
            }

            MapFileReader[] readers = new MapFileReader[tabletMapFiles.size()];

            TreeSet<MetaValue> metaValues = new TreeSet<MetaValue>();

            TreeSet<ColumnValue> workPlace = new TreeSet<ColumnValue>();

            try {
                //init
                CellFilter cellFilter = new CellFilter(columnName);
                int index = 0;
                for (TabletMapFile tabletMapFile : tabletMapFiles) {
                    MapFileReader reader = tabletMapFile.getMapFileReader(rowKey, Row.Key.MAX_KEY, cellFilter);
                    ColumnValue columnValue = null;
                    while ((columnValue = reader.next()) != null) {
                        if (great) {
                            if (columnValue.getRowKey().compareTo(rowKey) < 0) {
                                continue;
                            }
                        } else {
                            if (columnValue.getRowKey().compareTo(rowKey) <= 0) {
                                continue;
                            }
                        }
                        break;
                    }
                    if (columnValue != null) {
                        workPlace.add(columnValue);
                        readers[index] = reader;
                    } else {
                        reader.close();
                        readers[index] = null;
                    }
                    index++;
                }

                //findClosestMeta
                while (true) {
                    if (workPlace.isEmpty()) {
                        return null;
                    }
                    ColumnValue winnerColumnValue = workPlace.first();
                    metaValues.add(new MetaValue(winnerColumnValue));
                    workPlace.remove(winnerColumnValue);
                    Row.Key winnerRowKey = winnerColumnValue.getRowKey();

                    List<ColumnValue> tempWorkPlace = new ArrayList<ColumnValue>();
                    tempWorkPlace.addAll(workPlace);
                    for (ColumnValue eachColumnValue : tempWorkPlace) {
                        if (winnerRowKey.equals(eachColumnValue.getRowKey())) {
                            metaValues.add(new MetaValue(eachColumnValue));
                            workPlace.remove(eachColumnValue);
                        }
                    }

                    for (int i = 0; i < readers.length; i++) {
                        if (readers[i] == null) {
                            continue;
                        }
                        ColumnValue columnValue = null;
                        while ((columnValue = readers[i].next()) != null) {
                            if (winnerRowKey.equals(columnValue.getRowKey())) {

                                metaValues.add(new MetaValue(columnValue));
                            } else {
                                workPlace.add(columnValue);
                                break;
                            }
                        }
                        if (columnValue == null) {
                            readers[i].close();
                            readers[i] = null;
                        }
                    }

                    if (metaValues.size() > 0) {
                        MetaValue firstValue = metaValues.first();
                        if (!firstValue.columnValue.isDeleted()) {
                            return firstValue.columnValue;
                        } else {
                            metaValues.clear();
                        }
                    }
                }
            } finally {
                for (int i = 0; i < readers.length; i++) {
                    try {
                        if (readers[i] != null) {
                            readers[i].close();
                        }
                    } catch (Exception e) {
                        LOG.warn("Can't close MapFileReader:" + e.getMessage());
                    }
                }
            }
        } finally {
            lock.releaseReadLock();
        }
    }

    public boolean hasValue(Row.Key rowKey, String columnName, Cell.Key cellKey) throws IOException {
        lock.obtainReadLock();
        try {
            List<TabletMapFile> tabletMapFiles = mapFiles.get(columnName);
            if (tabletMapFiles == null || tabletMapFiles.isEmpty()) {
                return false;
            }

            for (TabletMapFile tabletMapFile : tabletMapFiles) {
                if (tabletMapFile.hasValue(rowKey, cellKey)) {
                    return true;
                }
            }
            return false;
        } finally {
            lock.releaseReadLock();
        }
    }

    /**
     * 
     * @throws IOException
     */
    public void printMapFileInfo() throws IOException {
        lock.obtainReadLock();
        try {
            for (Map.Entry<String, List<TabletMapFile>> entry : mapFiles.entrySet()) {
                for (TabletMapFile mapFile : entry.getValue()) {
                    LOG.info("printMapFileInfo:tabletName=" + tabletInfo.getTabletName() + ", path="
                            + mapFile.getFilePath() + ", size=" + mapFile.getDataFileSize());
                }
            }
        } finally {
            lock.releaseReadLock();
        }
    }

    /**
     * Tablet? ?  ? ?  Tablet? ? .
     * Split? Major Compaction?  TabletMapFile? ?  ? .
     * split  ?? DiskSSTable? ?. 
     * @param createdTablets
     * @return
     */
    public void split(Row.Key midRowKey, Tablet[] createdTablets) throws IOException {
        Map<String, List<TabletMapFile>> cloneMapFiles = null;

        lock.obtainReadLock();
        try {
            cloneMapFiles = new HashMap<String, List<TabletMapFile>>(mapFiles);
        } finally {
            lock.releaseReadLock();
        }

        String fileId = IdGenerator.getId();

        // (for  ?   )
        for (Map.Entry<String, List<TabletMapFile>> entry : cloneMapFiles.entrySet()) {
            List<TabletMapFile> columnMapFiles = entry.getValue();
            String columnName = entry.getKey();

            splitMapFile(midRowKey, createdTablets, columnName, columnMapFiles, fileId);
        }
    }

    public void splitMapFile(final Row.Key midRowKey, final Tablet[] createdTablets, final String columnName,
            List<TabletMapFile> columnMapFiles, String fileId) throws IOException {

        MapFileReader[] readers = new MapFileReader[columnMapFiles.size()];
        MapFileWriter[] writers = new MapFileWriter[2];
        TabletMapFile[] mapFiles = new TabletMapFile[2];
        long targetMapFileSize = 0;

        try {
            for (int i = 0; i < 2; i++) {
                GPath tempMapFilePath = new GPath(
                        Tablet.getTabletSplitTempPath(conf, createdTablets[i].getTabletInfo()),
                        columnName + "/" + fileId + "/");
                mapFiles[i] = new TabletMapFile(conf, CloudataFileSystem.get(conf),
                        createdTablets[i].getTabletInfo(), columnName, fileId, tempMapFilePath, numOfVersion);
                createdTablets[i].getDiskSSTable().addTabletMapFile(columnName, mapFiles[i]);
                writers[i] = mapFiles[i].getMapFileWriter();
            }

            TreeSet<ColumnValueEntry> workPlace = new TreeSet<ColumnValueEntry>();

            ColumnValue columnValue = null;

            for (int i = 0; i < readers.length; i++) {
                TabletMapFile mapFile = columnMapFiles.get(i);
                targetMapFileSize += mapFile.getDataFileSize();

                readers[i] = mapFile.getMapFileReader(Row.Key.MIN_KEY, Row.Key.MAX_KEY);
                columnValue = readers[i].next();
                if (columnValue != null) {
                    workPlace.add(new ColumnValueEntry(columnValue, i));
                } else {
                    readers[i].close();
                    readers[i] = null;
                }
            }

            while (true) {
                if (workPlace.isEmpty()) {
                    break;
                }

                ColumnValueEntry winnerValue = workPlace.pollFirst();
                int winnerIndex = winnerValue.index;
                columnValue = winnerValue.columnValue;

                Row.Key rowKey = columnValue.getRowKey();

                int nw = 0;
                if (rowKey.compareTo(midRowKey) > 0) {
                    nw = writers[1].write(columnValue);
                } else {
                    nw = writers[0].write(columnValue);
                }

                if (readers[winnerIndex] != null) {
                    columnValue = readers[winnerIndex].next();

                    if (columnValue != null) {
                        workPlace.add(new ColumnValueEntry(columnValue, winnerIndex));
                    } else {
                        readers[winnerIndex].close();
                        readers[winnerIndex] = null;
                    }
                }
            } //end of while
        } finally {
            for (int i = 0; i < writers.length; i++) {
                try {
                    if (writers[i] != null) {
                        writers[i].close();
                    }
                } catch (IOException e) {
                    LOG.warn("IOException in closing map file writer, exception : " + e);
                    for (int j = 0; j < mapFiles.length; j++) {
                        mapFiles[j].delete();
                    }
                    throw e;
                }
            }

            LOG.debug(columnName + " file splited from orig: " + targetMapFileSize + " to file1: "
                    + mapFiles[0].getDataFileSize() + " & file2: " + mapFiles[1].getDataFileSize());

            for (int i = 0; i < readers.length; i++) {
                try {
                    if (readers[i] != null) {
                        readers[i].close();
                    }
                } catch (IOException e) {
                    throw e;
                }
            }
        }
    }

    //  private PerformanceRegulator createRegulator(long targetMapFileSize) {
    //    return TabletSplitAction.isFastProcessingMode() ?
    //        new NullPerformanceRegulator() :
    //          new DefaultPerformanceRegulator(targetMapFileSize / 300,
    //              TabletSplitAction.getSlowProcessSleepTime());
    //  }

    public Row.Key findMidRowKeyForSplit() throws IOException {
        lock.obtainReadLock();

        try {
            //MapFile Index?  Row.Key merge.
            SortedSet<Row.Key> mergedRowKeySet = new TreeSet<Row.Key>();
            int indexRecordCount = 0;
            int mapFileCount = 0;
            for (Map.Entry<String, List<TabletMapFile>> entry : mapFiles.entrySet()) {
                List<TabletMapFile> columnMapFiles = entry.getValue();
                if (columnMapFiles.isEmpty())
                    continue;

                for (TabletMapFile columnMapFile : columnMapFiles) {
                    mapFileCount++;
                    for (MapFileIndexRecord mapFileIndexRecord : columnMapFile.getMapFileIndexRecords()) {
                        mergedRowKeySet.add(mapFileIndexRecord.getRowKey());
                        indexRecordCount++;
                    }
                }
            }

            //Split   key .
            int rowRangeSize = mergedRowKeySet.size();

            if (rowRangeSize == 1) {
                ///////////////////////////////////////////////////////////////////////////
                // 
                //        Row.Key previousRowKey = null;
                //        for(Map.Entry<String, List<TabletMapFile>> entry: mapFiles.entrySet()) {
                //          List<TabletMapFile> columnMapFiles = entry.getValue();
                //          if(columnMapFiles.size() == 0)    continue;
                //          
                //          if(columnMapFiles.size() > 1) {
                //            LOG.warn("Map file size > 1 while split:" + tabletInfo.getTabletName());
                //          }
                //          for(TabletMapFile columnMapFile: columnMapFiles) {
                //            for(MapFileIndexRecord mapFileIndexRecord: columnMapFile.getMapFileIndexRecords()) {
                //              Row.Key indexRowKey = mapFileIndexRecord.getRowKey();
                //              if(previousRowKey != null && !indexRowKey.equals(previousRowKey)) {
                //                LOG.debug("Different rowKey:" + indexRowKey);
                //              }
                //              previousRowKey = indexRowKey;
                //            }
                //          }
                //        }
                //End of  
                ///////////////////////////////////////////////////////////////////////////

                return null;
            } else if (rowRangeSize == 2) {
                return mergedRowKeySet.first();
            }

            int count = 0;
            Row.Key midRowKey = null;
            for (Row.Key eachRowKey : mergedRowKeySet) {
                if (count >= (rowRangeSize / 2)) {
                    midRowKey = eachRowKey;
                    break;
                }
                count++;
            }

            if (midRowKey != null && midRowKey.equals(tabletInfo.getEndRowKey())) {
                LOG.warn("Wrong mid rowkey:");
                for (Row.Key eachRowKey : mergedRowKeySet) {
                    LOG.info("MapFile rowKey:" + eachRowKey);
                }
            }
            return midRowKey;
        } finally {
            lock.releaseReadLock();
        }
    }

    public long sumMapFileSize() throws IOException {
        return totalMapFileSize;
    }

    public void calculateTotalMapFileSize() {
        long size = 0;
        try {
            for (Map.Entry<String, List<TabletMapFile>> entry : mapFiles.entrySet()) {
                for (TabletMapFile mapFile : entry.getValue()) {
                    try {
                        size += mapFile.getDataFileSize();
                    } catch (java.io.FileNotFoundException e) {
                        continue;
                    }
                }
            }
            totalMapFileSize = size;
        } catch (Exception e) {
            LOG.warn("Error in calculating total map file size", e);
        }
    }

    public void moveTempToTablet(boolean deleteParent) throws IOException {
        lock.obtainReadLock();
        try {
            for (Map.Entry<String, List<TabletMapFile>> entry : mapFiles.entrySet()) {
                String columnName = entry.getKey();
                for (TabletMapFile mapFile : entry.getValue()) {
                    GPath targetPath = new GPath(Tablet.getTabletPath(conf, tabletInfo),
                            columnName + "/" + mapFile.getFileId() + "/");
                    mapFile.moveTo(targetPath, deleteParent);
                }
            }
        } finally {
            lock.releaseReadLock();
        }
    }

    /**
     *  ? (?? ?? ? Column? ?   MapFile? ? ?? .
     * @return
     */
    protected boolean checkMapFile() throws IOException {
        long[] sums = new long[mapFiles.size()];
        String[] columnNames = new String[mapFiles.size()];
        int index = 0;
        for (Map.Entry<String, List<TabletMapFile>> entry : mapFiles.entrySet()) {
            columnNames[index] = entry.getKey();
            List<TabletMapFile> columnMapFiles = entry.getValue();
            for (TabletMapFile mapFile : columnMapFiles) {
                sums[index] += mapFile.getDataFileSize();
            }
            index++;
        }

        for (int i = 1; i < sums.length; i++) {
            if (sums[i] != sums[i - 1]) {
                LOG.error(tabletInfo.getTabletName() + "," + columnNames[i - 1] + "," + columnNames[i]
                        + " different");
                return false;
            }
        }

        return true;
    }

    public boolean isEmpty() {
        lock.obtainReadLock();
        try {
            return mapFiles.isEmpty();
        } finally {
            lock.releaseReadLock();
        }
    }

    public String[] getMapFilePathList() {
        List<String> result = new ArrayList<String>();
        for (Map.Entry<String, List<TabletMapFile>> entry : mapFiles.entrySet()) {
            for (TabletMapFile mapFile : entry.getValue()) {
                result.add(mapFile.getFilePath().toString());
            }
        }
        return result.toArray(new String[] {});
    }

    public int[] getMapFileCount() {
        int[] result = new int[mapFiles.size()];
        //lock.obtainReadLock();
        try {
            int indedx = 0;
            for (Map.Entry<String, List<TabletMapFile>> entry : mapFiles.entrySet()) {
                result[indedx++] = entry.getValue().size();
            }
        } finally {
            //lock.releaseReadLock();
        }
        return result;
    }

    public long[] getMapFileSize() throws IOException {
        long[] result = new long[mapFiles.size()];
        //lock.obtainReadLock();
        try {
            int indedx = 0;
            for (Map.Entry<String, List<TabletMapFile>> entry : mapFiles.entrySet()) {
                long sum = 0;
                for (TabletMapFile mapFile : entry.getValue()) {
                    try {
                        sum += mapFile.getDataFileSize();
                    } catch (java.io.FileNotFoundException e) {
                        continue;
                    }
                }
                result[indedx++] = sum;
            }
        } finally {
            //lock.releaseReadLock();
        }
        return result;
    }

    public long[] getMapFileIndexSize() throws IOException {
        long[] result = new long[mapFiles.size()];
        //lock.obtainReadLock();
        try {
            int indedx = 0;
            for (Map.Entry<String, List<TabletMapFile>> entry : mapFiles.entrySet()) {
                long sum = 0;
                for (TabletMapFile mapFile : entry.getValue()) {
                    sum += mapFile.getIndexMemorySize();
                }
                result[indedx++] = sum;
            }
        } finally {
            //lock.releaseReadLock();
        }
        return result;
    }

    public List<Searchable> getSearchers(Row.Key rowKey, CellFilter cellFilter) throws IOException {
        List<Searchable> mapFileReaders = new ArrayList<Searchable>();
        List<TabletMapFile> tabletMapFileList = null;

        String columnName = cellFilter.getColumnName();

        lock.obtainReadLock();
        try {
            if ((tabletMapFileList = mapFiles.get(columnName)) == null) {
                return mapFileReaders;
            }
        } finally {
            lock.releaseReadLock();
        }

        if (columnMemoryCaches.containsKey(columnName)) {
            //LOG.info(tabletInfo.getTabletName() + " use MemoryCacheSearcher");

            mapFileReaders.add(columnMemoryCaches.get(columnName).getSearcher(rowKey, cellFilter));
        } else {
            for (TabletMapFile mapFile : tabletMapFileList) {
                MapFileReader reader = mapFile.getMapFileReader(rowKey, rowKey, cellFilter);
                mapFileReaders.add(reader);
            }
        }
        return mapFileReaders;
    }

    //for test
    public long getMapFileMemorySize() throws IOException {
        long memorySize = 0;
        for (Map.Entry<String, List<TabletMapFile>> entry : mapFiles.entrySet()) {
            for (TabletMapFile mapFile : entry.getValue()) {
                memorySize += mapFile.getIndexMemorySize();
            }
        }
        return memorySize;
    }

    public String getTestHandlerKey() {
        return tabletServer.getHostName();
    }

    public TabletServer getTabletServer() {
        return tabletServer;
    }

    static class MetaValue implements Comparable<MetaValue> {
        ColumnValue columnValue;

        MetaValue(ColumnValue columnValue) {
            this.columnValue = columnValue;
        }

        @Override
        public int compareTo(MetaValue obj) {
            int result = columnValue.getRowKey().compareTo(obj.columnValue.getRowKey());
            if (result != 0) {
                return result;
            }
            if (columnValue.getTimestamp() > obj.columnValue.getTimestamp())
                return -1;
            else if (columnValue.getTimestamp() == obj.columnValue.getTimestamp()) {
                if (columnValue.isDeleted() == obj.columnValue.isDeleted())
                    return 0;
                else {
                    if (columnValue.isDeleted()) {
                        return -1;
                    } else {
                        return 1;
                    }
                }
            } else
                return 1;
        }

        @Override
        public boolean equals(Object obj) {
            if (!(obj instanceof MetaValue)) {
                return false;
            }

            return compareTo((MetaValue) obj) == 0;
        }
    }

    public void loadToMemoryCache() throws IOException {
        TableSchema tableScehma = tablet.getTable();
        for (ColumnInfo eachColumnInfo : tableScehma.getColumnInfos()) {
            if (eachColumnInfo.getColumnType() == TableSchema.CACHE_TYPE) {
                String columnName = eachColumnInfo.getColumnName();
                if (notUseColumnMemoryCache.contains(columnName)) {
                    continue;
                }
                List<TabletMapFile> tabletMapFiles = mapFiles.get(columnName);
                if (tabletMapFiles == null || tabletMapFiles.isEmpty()) {
                    continue;
                }

                ColumnCollection columnCollection = new ColumnCollection(conf);
                for (TabletMapFile eachMapFile : tabletMapFiles) {
                    MapFileReader reader = eachMapFile.getMapFileReader(Row.Key.MIN_KEY, Row.Key.MAX_KEY);
                    ColumnValue columnValue = null;
                    while ((columnValue = reader.next()) != null) {
                        columnCollection.addValue(columnValue.getRowKey(), columnValue,
                                tableScehma.getNumOfVersion());
                    }
                }
                setMemoryCache(columnName, columnCollection);
            }
        }
    }

    public void setMemoryCache(HashMap<String, ColumnCollection> compactingColumnCollections) {
        TableSchema tableScehma = tablet.getTable();

        for (ColumnInfo eachColumnInfo : tableScehma.getColumnInfos()) {
            if (eachColumnInfo.getColumnType() == TableSchema.CACHE_TYPE) {
                String columnName = eachColumnInfo.getColumnName();
                if (compactingColumnCollections.containsKey(columnName)) {
                    setMemoryCache(columnName, compactingColumnCollections.get(columnName));
                }
            }
        }
    }

    private void setMemoryCache(String columnName, ColumnCollection columnCollection) {
        long addedMemorySize = columnCollection.getMemorySize();

        if (addedMemorySize == 0 || notUseColumnMemoryCache.contains(columnName)) {
            return;
        }

        if (!tablet.getTabletServer().checkCacheCapacity(tabletInfo.getTabletName(), addedMemorySize)) {
            LOG.info(tabletInfo.getTabletName() + " can't use MemoryCache["
                    + tablet.getTabletServer().currentMemoryCacheSize + ", " + addedMemorySize + "]");
            lock.obtainWriteLock();
            try {
                //if can't add, remove cache data
                ColumnMemoryCache cache = columnMemoryCaches.remove(columnName);
                if (cache != null) {
                    tabletServer.removeFromMemoryCache(tabletInfo.getTabletName(), cache.getMemorySize());
                }
                notUseColumnMemoryCache.add(columnName);
            } finally {
                lock.releaseWriteLock();
            }
            return;
        } else {
            LOG.debug(tabletInfo.getTabletName() + "." + columnName + " use MemoryCache["
                    + columnCollection.getMemorySize() + " bytes]");
            lock.obtainWriteLock();
            try {
                if (columnMemoryCaches.containsKey(columnName)) {
                    columnMemoryCaches.get(columnName).addColumnCollection(columnCollection);
                } else {
                    columnMemoryCaches.put(columnName, new ColumnMemoryCache(columnCollection));
                }
            } finally {
                lock.releaseWriteLock();
            }
        }
    }

    public long getColumnMemoryCachesSize() {
        long memorySize = 0;
        for (ColumnMemoryCache eachCache : columnMemoryCaches.values()) {
            memorySize += eachCache.getMemorySize();
        }

        return memorySize;
    }

    public static void main(String[] args) throws Exception {
        DiskSSTable diskSSTable = new DiskSSTable();
        TabletInfo tabletInfo = new TabletInfo();
        tabletInfo.setTableName("META");
        tabletInfo.setTabletName("META_33211206903548981");

        TabletMapFile mapFile1 = new TabletMapFile(new CloudataConf(), tabletInfo, "TabletInfo",
                "33211281913761034", 0);
        mapFile1.loadIndex();
        TabletMapFile mapFile2 = new TabletMapFile(new CloudataConf(), tabletInfo, "TabletInfo",
                "33211275611202613", 0);
        mapFile2.loadIndex();
        diskSSTable.addTabletMapFile("TabletInfo", mapFile1);
        diskSSTable.addTabletMapFile("TabletInfo", mapFile2);
        ColumnValue columnValue = diskSSTable.findClosestMeta(new Row.Key("T_TERA_1T.04657695175873701783"),
                "TabletInfo", true);
        System.out.println(">>>>" + columnValue);
    }
}