All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sleepycat.je.rep.util.ldiff.Protocol Maven / Gradle / Ivy

The newest version!
/*-
 * Copyright (C) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
 *
 * This file was distributed by Oracle as part of a version of Oracle Berkeley
 * DB Java Edition made available at:
 *
 * http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html
 *
 * Please see the LICENSE file included in the top-level directory of the
 * appropriate version of Oracle Berkeley DB Java Edition for a copy of the
 * license and additional information.
 */

package com.sleepycat.je.rep.util.ldiff;

import java.nio.ByteBuffer;

import com.sleepycat.je.dbi.EnvironmentImpl;
import com.sleepycat.je.log.LogUtils;
import com.sleepycat.je.rep.impl.node.NameIdPair;
import com.sleepycat.je.rep.utilint.BinaryProtocol;
import com.sleepycat.je.utilint.VLSN;

/**
 * {@literal
 * Protocol used by LDiff to request the blocks associated with a database and
 * do the record level analysis.
 *
 * BLOCK LEVEL ANALYSIS
 * =========================================
 * The request response sequence for a block list request is:
 *
 * EnvDiff -> EnvInfo -> DbBlocks -> BlockListStart [BlockInfo]+ BlockListEnd
 *
 * A DbMismatch response is sent back if the database does not exist, or if the
 * database has different persistent attributes associated with it.
 *
 * Note that this request is designed to maximize overlap. That is, the
 * responder could be working on block n, while the requester is working on
 * blocks n+1 and beyond (to the end of the database).
 *
 * The above is the minimal set of messages, biased for performance when the
 * databases are expected to match.
 *
 * RECORD LEVEL ANALYSIS
 * =========================================
 * User can configure LDiff to do record level analysis by setting 
 * LDiffConfig.setDiffAnalysis(true), it can help you find out which records 
 * are different between two databases.
 *
 * The request response sequence for record level analysis is: 
 *
 * RemoteDiffRequest -> DiffAreaStart|Error [RemoteRecord] DiffAreaEnd -> Done 
 *
 * The local Environment would send out a RemoteDiffRequest to the remote 
 * Environment, the remote Environment can get all the records of a different 
 * area: 
 *    1. If there exists exceptions during the fetching process, remote 
 *       Environment would send back an Error message, local Environment would 
 *       throw out a LDiffRecordRequestException and exit.
 *    2. If the fetching process is correct, remote Environment would send a 
 *       DiffAreaStart message to the local Environment to notify that it now
 *       transfers RemoteRecords of a different area. 
 *    3. After all the records of a different area are transferred, remote 
 *       Environment would send out a DiffAreaEnd message, which specifies 
 *       transferring a different area is finished. 
 *    4. When all the RemoteDiffRequest are processed, local Environment would
 *       send a Done message to presents the record level analysis is done.
 *
 * TODO: 1) Protocol version matching 2) Block granularity sync request. 3)
 * Protocol to narrow a diff down to a specific set of key inserts, updates and
 * deletes.
 * }
 */
public class Protocol extends BinaryProtocol {

    static public final int VERSION = 2;

    public final static MessageOp DB_BLOCKS =
        new MessageOp((short) 1, DbBlocks.class);

    public final static MessageOp DB_MISMATCH =
        new MessageOp((short) 2, DbMismatch.class);

    public final static MessageOp BLOCK_LIST_START =
        new MessageOp((short) 3, BlockListStart.class);

    public final static MessageOp BLOCK_INFO =
        new MessageOp((short) 4, BlockInfo.class);

    public final static MessageOp BLOCK_LIST_END =
        new MessageOp((short) 5, BlockListEnd.class);

    public final static MessageOp ENV_DIFF =
        new MessageOp((short) 6, EnvDiff.class);

    public final static MessageOp ENV_INFO =
        new MessageOp((short) 7, EnvInfo.class);

    public final static MessageOp REMOTE_DIFF_REQUEST =
        new MessageOp((short) 8, RemoteDiffRequest.class);

    public final static MessageOp REMOTE_RECORD =
        new MessageOp((short) 9, RemoteRecord.class);

    public final static MessageOp DIFF_AREA_START =
        new MessageOp((short) 10, DiffAreaStart.class);

    public final static MessageOp DIFF_AREA_END =
        new MessageOp((short) 11, DiffAreaEnd.class);

    public final static MessageOp DONE = new MessageOp((short) 12, Done.class);

    public final static MessageOp ERROR = 
        new MessageOp((short) 13, Error.class);

    public Protocol(NameIdPair nameIdPair, EnvironmentImpl envImpl) {

        /*
         * Make the configured version the same as the code version for now to
         * ignore protocol negotiation issues.
         */
        super(nameIdPair, VERSION, VERSION, envImpl);

        initializeMessageOps(new MessageOp[]
            { DB_BLOCKS, DB_MISMATCH, BLOCK_LIST_START,
              BLOCK_INFO, BLOCK_LIST_END, ENV_DIFF, ENV_INFO, 
              REMOTE_DIFF_REQUEST, REMOTE_RECORD, DIFF_AREA_START, 
              DIFF_AREA_END, DONE, ERROR });
    }

    /**
     * Message used to request a list of blocks. Note that the message only
     * needs to identify a specific database, since the service itself is
     * associated with a specific environment.
     */
    public class DbBlocks extends SimpleMessage {
        final String dbName;
        final int blockSize;

        // TODO: add all the persistent properties of the database, so they can
        // be checked.
        public DbBlocks(String dbName, int blockSize) {
            super();
            this.dbName = dbName;
            this.blockSize = blockSize;
        }

        public DbBlocks(ByteBuffer buffer) {
            super();
            dbName = getString(buffer);
            blockSize = LogUtils.readInt(buffer);
        }

        @Override
        public ByteBuffer wireFormat() {
            return wireFormat(dbName, blockSize);
        }

        @Override
        public MessageOp getOp() {
            return DB_BLOCKS;
        }

        public String getDbName() {
            return dbName;
        }

        public int getBlockSize() {
            return blockSize;
        }
    }

    /**
     * Issued in response to a database level mismatch either because the
     * database itself does not exist at the node, or because it's properties
     * are different.
     */
    public class DbMismatch extends RejectMessage {

        public DbMismatch(String message) {
            super(message);
        }

        public DbMismatch(ByteBuffer buffer) {
            super(buffer);
        }

        @Override
        public MessageOp getOp() {
            return DB_MISMATCH;
        }
    }

    /**
     * Denotes the start of the list of blocks. 
     */
    public class BlockListStart extends SimpleMessage {

        public BlockListStart() {
            super();
        }

        @SuppressWarnings("unused")
            public BlockListStart(ByteBuffer buffer) {
            super();
        }

        @Override
        public MessageOp getOp() {
            return BLOCK_LIST_START;
        }
    }

    /**
     * Denotes the end of the list of blocks. 
     */
    public class BlockListEnd extends SimpleMessage {

        public BlockListEnd() {
            super();
        }

        @SuppressWarnings("unused")
            public BlockListEnd(ByteBuffer buffer) {
            super();
        }

        @Override
        public MessageOp getOp() {
            return BLOCK_LIST_END;
        }
    }

    /**
     * Supplies the properties of an individual block. 
     */
    public class BlockInfo extends SimpleMessage {
        /* The block associated with the request */
        final Block block;

        public BlockInfo(Block block) {
            super();
            this.block = block;
        }

        public BlockInfo(ByteBuffer buffer) {
            super();
            block = new Block(LogUtils.readInt(buffer));
            block.setBeginKey(getByteArray(buffer));
            block.setBeginData(getByteArray(buffer));
            block.setMd5Hash(getByteArray(buffer));
            block.setRollingChksum(LogUtils.readLong(buffer));
        }

        @Override
        public ByteBuffer wireFormat() {
            return wireFormat(block.getBlockId(), block.getBeginKey(),
                              block.getBeginData(), block.getMd5Hash(),
                              block.getRollingChksum());
        }

        @Override
        public MessageOp getOp() {
            return BLOCK_INFO;
        }

        public Block getBlock() {
            return block;
        }
    }

    /**
     * Message used to present that an Environment is requesting to do a 
     * LDiff with another Environment.
     */
    public class EnvDiff extends SimpleMessage {
        public EnvDiff() {
            super();
        }

        @SuppressWarnings("unused")
        public EnvDiff(ByteBuffer buffer) {
            super();
        }

        @Override
        public MessageOp getOp() {
            return ENV_DIFF;
        }
    }

    /**
     * Message used to present how many databases in a compared Environment. 
     */
    public class EnvInfo extends SimpleMessage {
        final int numDBs;

        public EnvInfo(int numberOfDbs) {
            super();
            numDBs = numberOfDbs;
        }

        public EnvInfo(ByteBuffer buffer) {
            super();
            numDBs = LogUtils.readInt(buffer);
        }

        @Override
        public ByteBuffer wireFormat() {
            return wireFormat(numDBs);
        }

        @Override
        public MessageOp getOp() {
            return ENV_INFO;
        }

        public int getNumberOfDBs() {
            return numDBs;
        }
    }

    /**
     * Message used to request records of a different area on the remote
     * database.
     */
    public class RemoteDiffRequest extends SimpleMessage {
        final byte[] key;
        final byte[] data;
        final long diffSize;

        public RemoteDiffRequest(MismatchedRegion region) {
            super();
            key = region.getRemoteBeginKey();
            data = region.getRemoteBeginData();
            diffSize = region.getRemoteDiffSize();
        }

        public RemoteDiffRequest(ByteBuffer buffer) {
            super();
            key = getByteArray(buffer);
            data = getByteArray(buffer);
            diffSize = LogUtils.readInt(buffer);
        }

        @Override
        public ByteBuffer wireFormat() {
            return wireFormat(key, data, diffSize);
        }

        @Override
        public MessageOp getOp() {
            return REMOTE_DIFF_REQUEST;
        }

        public byte[] getKey() {
            return key;
        }

        public byte[] getData() {
            return data;
        }

        public long getDiffSize() {
            return diffSize;
        }
    }

    /**
     * Message used to transfer a record from remote to local database.
     */
    public class RemoteRecord extends SimpleMessage {
        final byte[] key;
        final byte[] data;
        final VLSN vlsn;

        public RemoteRecord(Record record) {
            super();
            key = record.getKey();
            data = record.getData();
            vlsn = record.getVLSN();
        }

        public RemoteRecord(ByteBuffer buffer) {
            super();
            key = getByteArray(buffer);
            data = getByteArray(buffer);
            vlsn = getVLSN(buffer);
        }

        @Override
        public ByteBuffer wireFormat() {
            return wireFormat(key, data, vlsn);
        }

        @Override
        public MessageOp getOp() {
            return REMOTE_RECORD;
        }

        public byte[] getKey() {
            return key;
        }

        public byte[] getData() {
            return data;
        }

        public VLSN getVLSN() {
            return vlsn;
        }
    }

    /**
     * Message used to present the transfer of a different area on remote
     * database begins.
     */
    public class DiffAreaStart extends SimpleMessage {
        public DiffAreaStart() {
            super();
        }

        @SuppressWarnings("unused")
        public DiffAreaStart(ByteBuffer buffer) {
            super();
        }

        @Override
        public MessageOp getOp() {
            return DIFF_AREA_START;
        }
    }

    /**
     * Message used to present the transfer of a different area on remote
     * database is done.
     */
    public class DiffAreaEnd extends SimpleMessage {
        public DiffAreaEnd() {
            super();
        }

        @SuppressWarnings("unused")
        public DiffAreaEnd(ByteBuffer buffer) {
            super();
        }

        @Override
        public MessageOp getOp() {
            return DIFF_AREA_END;
        }
    }

    /**
     * Message used to present the transfer of all the different data is done.
     */
    public class Done extends SimpleMessage {
        public Done() {
            super();
        }

        @SuppressWarnings("unused")
        public Done(ByteBuffer buffer) {
            super();
        }

        @Override
        public MessageOp getOp() {
            return DONE;
        }
    }

    /**
     * Message used to present an operation error on remote database.
     */
    public class Error extends RejectMessage {
        public Error(String message) {
            super(message);
        }

        public Error(ByteBuffer buffer) {
            super(buffer);
        }

        @Override
        public MessageOp getOp() {
            return ERROR;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy