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

    https://github.com/apache/zookeeper/pull/615#discussion_r215129442
  
    --- 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) {
    +                    System.out.println(
    +                        String.format("There is gap between %x and %x",
    +                                previousZxid, txnZxid));
    +                }
    +            }
    +            previousZxid = txnZxid;
    +
    +            if (hdr.getZxid() > zxid) {
    --- End diff --
    
    `hdr.getZxid()` is previously assigned to `txnZxid`, so maybe use `txnZxid` 
here instead? 
    
    Also, would it be better to move this check before the gap detection logic 
to terminate earlier?


---

Reply via email to