You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@zookeeper.apache.org by hanm <gi...@git.apache.org> on 2018/09/05 03:59:15 UTC

[GitHub] zookeeper pull request #615: ZOOKEEPER-3137: add a utility to truncate logs ...

Github user hanm commented on a diff in the pull request:

    https://github.com/apache/zookeeper/pull/615#discussion_r215129868
  
    --- Diff: src/java/main/org/apache/zookeeper/server/util/LogChopper.java ---
    @@ -0,0 +1,152 @@
    +/**
    + * Licensed to the Apache Software Foundation (ASF) under one
    + * or more contributor license agreements.  See the NOTICE file
    + * distributed with this work for additional information
    + * regarding copyright ownership.  The ASF licenses this file
    + * to you under the Apache License, Version 2.0 (the
    + * "License"); you may not use this file except in compliance
    + * with the License.  You may obtain a copy of the License at
    + *
    + *     http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.zookeeper.server.util;
    +
    +import org.apache.jute.BinaryInputArchive;
    +import org.apache.jute.BinaryOutputArchive;
    +import org.apache.jute.Record;
    +import org.apache.zookeeper.server.persistence.FileHeader;
    +import org.apache.zookeeper.server.persistence.FileTxnLog;
    +import org.apache.zookeeper.txn.TxnHeader;
    +
    +import java.io.BufferedInputStream;
    +import java.io.BufferedOutputStream;
    +import java.io.EOFException;
    +import java.io.FileInputStream;
    +import java.io.FileOutputStream;
    +import java.io.IOException;
    +import java.io.InputStream;
    +import java.io.OutputStream;
    +import java.util.zip.Adler32;
    +import java.util.zip.Checksum;
    +
    +/**
    + * this class will chop the log at the specified zxid
    + */
    +public class LogChopper {
    +    public static void main(String args[]) {
    +        if (args.length != 3) {
    +            System.out.println("Usage: LogChopper zxid_to_chop_to txn_log_to_chop chopped_filename");
    +            System.out.println("    this program will read the txn_log_to_chop file and copy all the transactions");
    +            System.out.println("    from it up to (and including) the given zxid into chopped_filename.");
    +            System.exit(1);
    +        }
    +        long zxid = Long.decode(args[0]);
    +        String txnLog = args[1];
    +        String choppedLog = args[2];
    +
    +        int rc = 2;
    +        try (
    +            InputStream is = new BufferedInputStream(new FileInputStream(txnLog));
    +            OutputStream os = new BufferedOutputStream(new FileOutputStream(choppedLog))
    +        ) {
    +            if (chop(is, os, zxid)) {
    +                rc = 0;
    +            }
    +        } catch (Exception e) {
    +            System.out.println("Got exception: " + e.getMessage());
    +        }
    +        System.exit(rc);
    +    }
    +
    +    public static boolean chop(InputStream is, OutputStream os, long zxid) throws IOException {
    +        BinaryInputArchive logStream = BinaryInputArchive.getArchive(is);
    +        BinaryOutputArchive choppedStream = BinaryOutputArchive.getArchive(os);
    +        FileHeader fhdr = new FileHeader();
    +        fhdr.deserialize(logStream, "fileheader");
    +
    +        if (fhdr.getMagic() != FileTxnLog.TXNLOG_MAGIC) {
    +            System.err.println("Invalid magic number in txn log file");
    +            return false;
    +        }
    +        System.out.println("ZooKeeper Transactional Log File with dbid "
    +                + fhdr.getDbid() + " txnlog format version "
    +                + fhdr.getVersion());
    +
    +        fhdr.serialize(choppedStream, "fileheader");
    +        int count = 0;
    +        boolean hasZxid = false;
    +        long previousZxid = -1;
    +        while (true) {
    +            long crcValue;
    +            byte[] bytes;
    +            try {
    +                crcValue = logStream.readLong("crcvalue");
    +
    +                bytes = logStream.readBuffer("txnEntry");
    +            } catch (EOFException e) {
    +                System.out.println("EOF reached after " + count + " txns.");
    +                // returning false because nothing was chopped
    +                return false;
    +            }
    +            if (bytes.length == 0) {
    +                // Since we preallocate, we define EOF to be an
    +                // empty transaction
    +                System.out.println("EOF reached after " + count + " txns.");
    +                // returning false because nothing was chopped
    +                return false;
    +            }
    +
    +            Checksum crc = new Adler32();
    +            crc.update(bytes, 0, bytes.length);
    +            if (crcValue != crc.getValue()) {
    +                throw new IOException("CRC doesn't match " + crcValue +
    +                        " vs " + crc.getValue());
    +            }
    +            TxnHeader hdr = new TxnHeader();
    +            Record txn = SerializeUtils.deserializeTxn(bytes, hdr);
    +            if (logStream.readByte("EOR") != 'B') {
    +                System.out.println("Last transaction was partial.");
    +                throw new EOFException("Last transaction was partial.");
    +            }
    +
    +            long txnZxid = hdr.getZxid();
    +            if (txnZxid == zxid) {
    +                hasZxid = true;
    +            }
    +
    +            // logging the gap to make the inconsistency investigation easier
    +            if (previousZxid != -1 && txnZxid != previousZxid + 1) {
    +                long txnEpoch = ZxidUtils.getEpochFromZxid(txnZxid);
    +                long txnCounter = ZxidUtils.getCounterFromZxid(txnZxid);
    +                long previousEpoch = ZxidUtils.getEpochFromZxid(previousZxid);
    +                if (txnEpoch == previousEpoch || txnCounter != 1) {
    --- End diff --
    
    Would it be better if we print more targeted diagnostic information here for both conditions, instead of reuse a single error message? For example:
    * Log gap between txns of the same epoch.
    * Log gap of the new epoch (from 1 to txnCounter)
    Might make it more easier to debug log.


---