org.cloudata.util.upgrade.CloudataUpgrade.java Source code

Java tutorial

Introduction

Here is the source code for org.cloudata.util.upgrade.CloudataUpgrade.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.util.upgrade;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.KeeperException.NoNodeException;
import org.cloudata.core.client.Cell;
import org.cloudata.core.client.Row;
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.conf.CloudataConf;
import org.cloudata.core.common.io.CWritableComparable;
import org.cloudata.core.common.io.CWritableUtils;
import org.cloudata.core.common.ipc.CRPC;
import org.cloudata.core.common.lock.LockUtil;
import org.cloudata.core.common.util.NetworkUtil;
import org.cloudata.core.fs.CloudataFileSystem;
import org.cloudata.core.fs.GPath;
import org.cloudata.core.master.CloudataMaster;
import org.cloudata.core.master.TableManagerProtocol;
import org.cloudata.core.tablet.ColumnValue;
import org.cloudata.core.tablet.TableSchema;
import org.cloudata.core.tablet.TabletInfo;
import org.cloudata.core.tabletserver.MapFileColumnValue;
import org.cloudata.core.tabletserver.MapFileIndexRecord;
import org.cloudata.core.tabletserver.Tablet;
import org.cloudata.core.tabletserver.TabletManagerProtocol;
import org.cloudata.core.tabletserver.TabletMapFile;
import org.cloudata.core.tabletserver.TabletServerInfo;
import org.cloudata.core.tabletserver.ValueCollection;

/**
 * @author jindolk
 * 
 */
public class CloudataUpgrade {
    // upgrade 1.3 -> 1.4
    // 1. modify TabletManagerProtocol
    // 2. modify TabletServer(add doAllTabletMinorCompaction)
    // 3. add CloudataUpgrade class
    // 4. build, cluster restart
    // 5. java CloudataUpgrade minor
    // 6. check memory empty
    // 7. java CloudataUpgrade major
    // 8. stop cluster
    // move to cloudata-1.4
    // 9. java CloudataUpgrade meta
    //10. backup ROOT, META file in DFS
    //11. replace local MapFie
    //12. java TabletMapFile 
    //13. start cluster(new version)

    static ZooKeeper zk;

    static CloudataConf conf;

    static Log LOG = LogFactory.getLog(CloudataUpgrade.class);

    public static void main(String[] args) throws IOException {
        if (args.length < 1) {
            System.out.println("Usage: java CloudataUpgrade <minor|major|meta|rollback>");
            System.exit(0);
        }

        conf = new CloudataConf();
        zk = LockUtil.getZooKeeper(conf, "CloudataClient_Upgrade_" + System.currentTimeMillis(), null);
        if ("minor".equals(args[0])) {
            doAllTabletMinorCompaction();
        } else if ("major".equals(args[0])) {
            doMetaMajorCompaction();
        } else if ("meta".equals(args[0])) {
            modifyMetaMapFile();
        } else if ("rollback".equals(args[0])) {
            rollback();
        } else {
            System.out.println("Usage: java CloudataUpgrade <minor|meta>");
            System.exit(0);
        }
    }

    private static void rollback() throws IOException {
        TabletInfo14 rootTablet14 = getRootTabletInfo14(conf, zk);

        LOG.info("====== Modify ROOT TabletInfo in ZooKeeper: " + LockUtil.getZKPath(conf, Constants.ROOT_TABLET));
        TabletInfo13 rootTablet13 = rootTablet14.getTabletInfo13();

        try {
            zk.setData(LockUtil.getZKPath(conf, Constants.ROOT_TABLET), rootTablet13.getWriteBytes(), -1);
        } catch (KeeperException e) {
            LOG.error(e);
        } catch (InterruptedException e) {
            LOG.error(e);
        }
    }

    private static void doAllTabletMinorCompaction() throws IOException {
        LOG.info("Start doAllTabletMinorCompaction");
        TableManagerProtocol masterServer = (TableManagerProtocol) CRPC.getProxy(TableManagerProtocol.class,
                TableManagerProtocol.versionID,
                NetworkUtil.getAddress(CloudataMaster.getMasterServerHostName(conf, zk)), conf);

        TabletServerInfo[] tabletServers = masterServer.getTabletServerInfos();
        if (tabletServers == null) {
            LOG.warn("No TabletServers");
            System.exit(0);
        }

        for (TabletServerInfo tabletServerInfo : tabletServers) {
            TabletManagerProtocol tabletServer = (TabletManagerProtocol) CRPC.getProxy(TabletManagerProtocol.class,
                    TabletManagerProtocol.versionID, NetworkUtil.getAddress(tabletServerInfo.getHostName()), conf);
            tabletServer.doAllTabletMinorCompaction();
            LOG.info("Call MinorCompaction to " + tabletServerInfo.getHostName());
        }
        LOG.info("End doAllTabletMinorCompaction");
    }

    private static void doMetaMajorCompaction() throws IOException {
        LOG.info("Start doMetaMajorCompaction");
        TabletInfo rootTablet = Tablet.getRootTabletInfo(conf, zk);

        TableScanner scanner = ScannerFactory.openScanner(conf, rootTablet, Constants.META_COLUMN_NAME_TABLETINFO);
        Row row = null;
        while ((row = scanner.nextRow()) != null) {
            Cell cell = row.getFirst(Constants.META_COLUMN_NAME_TABLETINFO);
            TabletInfo tabletInfo = new TabletInfo();
            tabletInfo.readFields(cell.getBytes());

            LOG.info("majorCompaction: " + tabletInfo.getTabletName());
            TabletManagerProtocol tabletServer = (TabletManagerProtocol) CRPC.getProxy(TabletManagerProtocol.class,
                    TabletManagerProtocol.versionID, NetworkUtil.getAddress(tabletInfo.getAssignedHostName()),
                    conf);

            tabletServer.doActionForTest(tabletInfo.getTabletName(), "majorCompaction");
        }
        LOG.info("majorCompaction: " + rootTablet.getTabletName());
        TabletManagerProtocol tabletServer = (TabletManagerProtocol) CRPC.getProxy(TabletManagerProtocol.class,
                TabletManagerProtocol.versionID, NetworkUtil.getAddress(rootTablet.getAssignedHostName()), conf);

        tabletServer.doActionForTest(rootTablet.getTabletName(), "majorCompaction");
        LOG.info("End doMetaMajorCompaction");
    }

    private static void modifyMetaMapFile() throws IOException {
        LOG.info("====== Start modifyMetaMapFile");

        try {
            //      TabletInfo rootTablet14 = Tablet.getRootTabletInfo(conf, zk);
            //      TabletInfo13 rootTablet = new TabletInfo13();
            //      rootTablet.tableName = rootTablet14.getTableName();
            //      rootTablet.tabletName = rootTablet14.getTabletName();
            //      rootTablet.endRowKey = rootTablet14.getEndRowKey();
            //      rootTablet.assignedHostName = rootTablet14.getAssignedHostName();

            TabletInfo13 rootTablet = getRootTabletInfo(conf, zk);
            if (rootTablet == null) {
                throw new IOException("Root Tablet is : " + rootTablet);
            }

            //////////////////////////////////////////////////////////////////////
            //ROOT Tablet
            List<MapFileColumnValue> mapFileColumnValues = getTabletInfos(rootTablet);
            if (mapFileColumnValues.size() == 0) {
                LOG.warn("No META TableInfo in ROOT Tablet:" + rootTablet);
                return;
            }

            SortedSet<TabletInfo13> metaTablets = new TreeSet<TabletInfo13>();
            UpgradeFileWriter writer = new UpgradeFileWriter(rootTablet.tabletName);
            try {
                Row.Key startRowKey = Row.Key.MIN_KEY;
                String previousTableName = null;
                for (MapFileColumnValue eachColumnValue : mapFileColumnValues) {
                    if (eachColumnValue.isDeleted()) {
                        writer.write(eachColumnValue);
                    } else {
                        TabletInfo13 tabletInfo13 = new TabletInfo13();
                        tabletInfo13.readFields(eachColumnValue.getValue());
                        TabletInfo14 tabletInfo = tabletInfo13.getTabletInfo();

                        if (previousTableName != null && !previousTableName.equals(tabletInfo13.tableName)) {
                            startRowKey = Row.Key.MIN_KEY;
                        }
                        tabletInfo.startRowKey = startRowKey;

                        Row.Key rowKey = Tablet.generateMetaRowKey(tabletInfo.tableName, tabletInfo.endRowKey);
                        Cell cell = new Cell(new Cell.Key(tabletInfo.tabletName), tabletInfo.getWriteBytes());
                        ColumnValue columnValue = new ColumnValue();
                        columnValue.setRowKey(rowKey);
                        columnValue.setCellKey(cell.getKey());
                        cell.getValue().copyToColumnValue(columnValue);
                        columnValue.setDeleted(eachColumnValue.isDeleted());
                        columnValue.setTimestamp(eachColumnValue.getTimestamp());

                        MapFileColumnValue mapFileColumnValue = new MapFileColumnValue();
                        ValueCollection.copyMapFileColumnValue(columnValue, mapFileColumnValue);
                        writer.write(mapFileColumnValue);
                        metaTablets.add(tabletInfo13);

                        startRowKey = tabletInfo13.endRowKey;
                        previousTableName = tabletInfo13.tableName;
                    }
                }
            } finally {
                writer.close();
            }

            //////////////////////////////////////////////////////////////////////
            //META Tablet
            for (TabletInfo13 eachMetaTablet : metaTablets) {
                mapFileColumnValues = getTabletInfos(eachMetaTablet);
                if (mapFileColumnValues.size() == 0) {
                    LOG.warn("No USER TableInfo in META Tablet:" + eachMetaTablet);
                    continue;
                }
                writer = new UpgradeFileWriter(eachMetaTablet.tabletName);
                try {
                    Row.Key startRowKey = Row.Key.MIN_KEY;

                    TabletInfo14 previousTablet = null;
                    //          Row.Key prevoiusEndRowKey = null;
                    //          String previousTableName = null;
                    //          String previousTabletName = null;

                    for (MapFileColumnValue eachColumnValue : mapFileColumnValues) {
                        if (eachColumnValue.isDeleted()) {
                            writer.write(eachColumnValue);
                        } else {
                            TabletInfo13 tabletInfo13 = new TabletInfo13();
                            tabletInfo13.readFields(eachColumnValue.getValue());
                            TabletInfo14 tabletInfo14 = tabletInfo13.getTabletInfo();

                            if (previousTablet != null) {
                                if (!previousTablet.tableName.equals(tabletInfo13.tableName)) {
                                    startRowKey = Row.Key.MIN_KEY;
                                } else {
                                    if (!previousTablet.tabletName.equals(tabletInfo13.tabletName)
                                            && !previousTablet.endRowKey.equals(tabletInfo13.endRowKey)) {
                                        startRowKey = previousTablet.endRowKey;
                                    }
                                }
                            }

                            tabletInfo14.startRowKey = startRowKey;
                            System.out.println(">>>>" + tabletInfo14.tabletName + ">"
                                    + (startRowKey.equals(Row.Key.MIN_KEY) ? "MIN" : startRowKey) + "~"
                                    + (tabletInfo14.endRowKey.equals(Row.Key.MAX_KEY) ? "MAX"
                                            : tabletInfo14.endRowKey)
                                    + ">" + eachColumnValue.getTimestamp() + ">" + eachColumnValue.isDeleted()
                                    + ">");

                            Row.Key rowKey = Tablet.generateMetaRowKey(tabletInfo14.tableName,
                                    tabletInfo14.endRowKey);
                            Cell cell = new Cell(new Cell.Key(tabletInfo14.tabletName),
                                    tabletInfo14.getWriteBytes());
                            ColumnValue columnValue = new ColumnValue();
                            columnValue.setRowKey(rowKey);
                            columnValue.setCellKey(cell.getKey());
                            cell.getValue().copyToColumnValue(columnValue);
                            columnValue.setDeleted(eachColumnValue.isDeleted());
                            columnValue.setTimestamp(eachColumnValue.getTimestamp());

                            MapFileColumnValue mapFileColumnValue = new MapFileColumnValue();
                            ValueCollection.copyMapFileColumnValue(columnValue, mapFileColumnValue);
                            writer.write(mapFileColumnValue);

                            //              prevoiusEndRowKey = tabletInfo13.endRowKey;
                            //              previousTabletName = tabletInfo13.tabletName;
                            //              previousTableName = tabletInfo13.tableName;

                            previousTablet = tabletInfo14;
                        }
                    }
                } finally {
                    writer.close();
                }
            }

            LOG.info("====== Modify ROOT TabletInfo in ZooKeeper: "
                    + LockUtil.getZKPath(conf, Constants.ROOT_TABLET));
            TabletInfo14 newRootTablet = rootTablet.getTabletInfo();
            newRootTablet.startRowKey = Row.Key.MIN_KEY;

            try {
                zk.setData(LockUtil.getZKPath(conf, Constants.ROOT_TABLET), newRootTablet.getWriteBytes(), -1);
            } catch (KeeperException e) {
                LOG.error(e);
            } catch (InterruptedException e) {
                LOG.error(e);
            }
        } catch (Exception e) {
            LOG.error("Error:" + e.getMessage(), e);
        } finally {
            LOG.info("====== End modifyMetaMapFile");
        }
    }

    private static List<MapFileColumnValue> getTabletInfos(TabletInfo13 tabletInfo) throws IOException {
        LOG.info("====== getTabletInfos from: " + tabletInfo.tabletName);
        List<MapFileColumnValue> tablets = new ArrayList<MapFileColumnValue>();

        CloudataFileSystem fs = CloudataFileSystem.get(conf);

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

        if (columnPaths == null || columnPaths.length == 0) {
            LOG.warn("No ROOT TabletMapFile:" + getTabletPath(conf, tabletInfo));
            return tablets;
        }

        for (GPath eachColumnPath : columnPaths) {
            GPath[] mapFilePaths = fs.list(eachColumnPath);

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

            if (mapFilePaths.length > 1) {
                throw new IOException("MapFile not merged: " + eachColumnPath);
            }
            GPath dataPath = new GPath(mapFilePaths[0], TabletMapFile.DATA_FILE);
            tablets.addAll(readTabletMapFile(dataPath));
        }
        return tablets;
    }

    public static TabletInfo14 getRootTabletInfo14(CloudataConf conf, ZooKeeper zk) throws IOException {
        byte[] data = null;
        try {
            data = zk.getData(LockUtil.getZKPath(conf, Constants.ROOT_TABLET), false, null);
        } catch (NoNodeException e) {
        } catch (Exception e) {
            throw new IOException(e);
        }

        if (data == null) {
            LOG.warn(Constants.ROOT_TABLET + " is null" + ", so ROOT tablet info is also null");
            return null;
        }

        TabletInfo14 rootTabletInfo = new TabletInfo14();

        rootTabletInfo.readFields(data);
        rootTabletInfo.assignedHostName = null;

        data = null;
        try {
            data = zk.getData(LockUtil.getZKPath(conf, Constants.ROOT_TABLET_HOST), false, null);
        } catch (NoNodeException e) {
        } catch (Exception e) {
            throw new IOException(e);
        }

        if (data != null) {
            rootTabletInfo.assignedHostName = new String(data);
        }

        return rootTabletInfo;
    }

    public static TabletInfo13 getRootTabletInfo(CloudataConf conf, ZooKeeper zk) throws IOException {
        byte[] data = null;
        try {
            data = zk.getData(LockUtil.getZKPath(conf, Constants.ROOT_TABLET), false, null);
        } catch (NoNodeException e) {
        } catch (Exception e) {
            throw new IOException(e);
        }

        if (data == null) {
            LOG.warn(Constants.ROOT_TABLET + " is null" + ", so ROOT tablet info is also null");
            return null;
        }

        TabletInfo13 rootTabletInfo = new TabletInfo13();

        rootTabletInfo.readFields(data);
        rootTabletInfo.assignedHostName = null;

        data = null;
        try {
            data = zk.getData(LockUtil.getZKPath(conf, Constants.ROOT_TABLET_HOST), false, null);
        } catch (NoNodeException e) {
        } catch (Exception e) {
            throw new IOException(e);
        }

        if (data != null) {
            rootTabletInfo.assignedHostName = new String(data);
        }

        return rootTabletInfo;
    }

    private static GPath getTabletPath(CloudataConf conf, TabletInfo13 tabletInfo) {
        return new GPath(TableSchema.getTableDataPath(conf, tabletInfo.tableName) + "/" + tabletInfo.tabletName);
    }

    private static List<MapFileColumnValue> readTabletMapFile(GPath path) throws IOException {
        LOG.info("====== Read tablet info from: " + path);
        List<MapFileColumnValue> tablets = new ArrayList<MapFileColumnValue>();

        CloudataFileSystem fs = CloudataFileSystem.get(conf);
        DataInputStream in = fs.openDataInputStream(path);

        while (true) {
            try {
                MapFileColumnValue columnValue = new MapFileColumnValue();
                columnValue.readFields(in);

                tablets.add(columnValue);
            } catch (EOFException e) {
                break;
            }
        }

        return tablets;
    }

    static class TabletInfo14 extends TabletInfo13 {
        protected Row.Key startRowKey;

        public TabletInfo14() {
            super();
            startRowKey = new Row.Key();
        }

        public void readFields(DataInput in) throws IOException {
            tableName = CWritableUtils.readString(in);
            tabletName = CWritableUtils.readString(in);

            startRowKey.readFields(in);
            endRowKey.readFields(in);
            assignedHostName = CWritableUtils.readString(in);
        }

        public void write(DataOutput out) throws IOException {
            CWritableUtils.writeString(out, tableName);
            CWritableUtils.writeString(out, tabletName);

            startRowKey.write(out);
            endRowKey.write(out);
            CWritableUtils.writeString(out, assignedHostName);
        }

        public TabletInfo13 getTabletInfo13() {
            TabletInfo13 tabletInfo = new TabletInfo13();
            tabletInfo.tableName = tableName;
            tabletInfo.tabletName = tabletName;
            tabletInfo.assignedHostName = assignedHostName;
            tabletInfo.endRowKey = endRowKey;
            return tabletInfo;
        }
    }

    static class TabletInfo13 implements CWritableComparable {
        protected String tableName;

        protected String tabletName;

        protected Row.Key endRowKey;

        String assignedHostName;

        public TabletInfo13() {
            endRowKey = new Row.Key();
        }

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

            TabletInfo13 otherObj = (TabletInfo13) obj;
            return tabletName.equals(otherObj.tabletName);
        }

        @Override
        public int hashCode() {
            return tabletName.hashCode();
        }

        public void readFields(DataInput in) throws IOException {
            tableName = CWritableUtils.readString(in);
            tabletName = CWritableUtils.readString(in);

            endRowKey.readFields(in);
            assignedHostName = CWritableUtils.readString(in);
        }

        public void write(DataOutput out) throws IOException {
            CWritableUtils.writeString(out, tableName);
            CWritableUtils.writeString(out, tabletName);

            endRowKey.write(out);
            CWritableUtils.writeString(out, assignedHostName);
        }

        public void readFields(byte[] value) throws IOException {
            DataInputStream din = new DataInputStream(new ByteArrayInputStream(value));
            this.readFields(din);
        }

        public byte[] getWriteBytes() throws IOException {
            ByteArrayOutputStream bout = new ByteArrayOutputStream();
            DataOutputStream dout = new DataOutputStream(bout);
            this.write(dout);
            return bout.toByteArray();
        }

        public int compareTo(Object obj) {
            TabletInfo13 tabletInfo = (TabletInfo13) obj;
            return endRowKey.compareTo(tabletInfo.endRowKey);
        }

        public TabletInfo14 getTabletInfo() {
            TabletInfo14 tabletInfo = new TabletInfo14();
            tabletInfo.tableName = tableName;
            tabletInfo.tabletName = tabletName;
            tabletInfo.assignedHostName = assignedHostName;
            tabletInfo.endRowKey = endRowKey;
            return tabletInfo;
        }
    }

    static class UpgradeFileWriter {
        private DataOutputStream dataOut;
        private DataOutputStream indexOut;

        private MapFileColumnValue lastColumnRecord;

        private int blockWriteBytes = 0;
        private long currentDataFilePos = 0;
        private long indexFileOffset = 0;

        private int indexBlockSize = conf.getInt("mapfile.indexBlockSize", 128) * 1024;

        private final int BLOCK_RECORD_COUNT = 100;

        private int blockWriteRecordCount = 0;

        private long sumDataLength = 0;
        private long sumIndexLength = 0;

        public UpgradeFileWriter(String fileName) throws IOException {
            dataOut = new DataOutputStream(new FileOutputStream(fileName + "_" + TabletMapFile.DATA_FILE));
            indexOut = new DataOutputStream(new FileOutputStream(fileName + "_" + TabletMapFile.IDX_FILE));
        }

        public int write(MapFileColumnValue mapFileColumnValue) throws IOException {
            mapFileColumnValue.write(dataOut);
            int writeSize = mapFileColumnValue.size();

            makeFileIndex(writeSize, mapFileColumnValue, indexBlockSize);

            lastColumnRecord = mapFileColumnValue;
            return writeSize;
        }

        private void makeFileIndex(int writeSize, MapFileColumnValue mapFileColumnValue, int checkSize)
                throws IOException {
            blockWriteBytes += writeSize;
            currentDataFilePos += writeSize;
            blockWriteRecordCount++;

            //?? writeBytes block size(64kb) ?  ?  index? 
            if (blockWriteBytes > checkSize || blockWriteRecordCount > BLOCK_RECORD_COUNT) {
                //index ?? index  
                MapFileIndexRecord mapFileIndexRecord = new MapFileIndexRecord(mapFileColumnValue.getRowKey(),
                        mapFileColumnValue, indexFileOffset);
                mapFileIndexRecord.write(indexOut);

                sumIndexLength += mapFileIndexRecord.getMemorySize();

                blockWriteBytes = 0;
                blockWriteRecordCount = 0;
                indexFileOffset = currentDataFilePos;
            }
        }

        public void close() throws IOException {
            if (dataOut == null) {
                return;
            }

            if (blockWriteBytes > 0) {
                makeFileIndex(0, lastColumnRecord, 0);
            }

            dataOut.flush();
            indexOut.flush();

            IOException exception = null;

            if (dataOut != null) {
                try {
                    dataOut.close();
                    dataOut = null;
                } catch (IOException e) {
                    LOG.error(e.getMessage() + ", sumDataLength=" + sumDataLength, e);
                    exception = e;
                }
            }
            if (indexOut != null) {
                try {
                    indexOut.close();
                    indexOut = null;
                } catch (IOException e) {
                    LOG.error(e.getMessage() + ", sumIndexLength=" + sumIndexLength, e);
                    exception = e;
                }
            }
            //      closed = true;

            if (exception != null) {
                throw exception;
            }
        }
    }
}