001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.procedure2.store.region;
019
020import static org.apache.hadoop.hbase.master.region.MasterRegionFactory.PROC_FAMILY;
021
022import java.io.PrintStream;
023import java.time.Instant;
024import java.time.ZoneId;
025import java.time.format.DateTimeFormatter;
026import java.util.Map;
027import org.apache.hadoop.fs.FileSystem;
028import org.apache.hadoop.fs.Path;
029import org.apache.hadoop.hbase.Cell;
030import org.apache.hadoop.hbase.ExtendedCell;
031import org.apache.hadoop.hbase.HBaseInterfaceAudience;
032import org.apache.hadoop.hbase.procedure2.Procedure;
033import org.apache.hadoop.hbase.procedure2.ProcedureUtil;
034import org.apache.hadoop.hbase.util.AbstractHBaseTool;
035import org.apache.hadoop.hbase.util.Bytes;
036import org.apache.hadoop.hbase.wal.WAL;
037import org.apache.hadoop.hbase.wal.WALEdit;
038import org.apache.hadoop.hbase.wal.WALFactory;
039import org.apache.hadoop.hbase.wal.WALKey;
040import org.apache.hadoop.hbase.wal.WALPrettyPrinter;
041import org.apache.hadoop.hbase.wal.WALStreamReader;
042import org.apache.yetus.audience.InterfaceAudience;
043import org.apache.yetus.audience.InterfaceStability;
044
045import org.apache.hbase.thirdparty.org.apache.commons.cli.CommandLine;
046
047import org.apache.hadoop.hbase.shaded.protobuf.generated.ProcedureProtos;
048
049/**
050 * A tool to dump the procedures in the WAL files.
051 * <p/>
052 * The different between this and {@link WALPrettyPrinter} is that, this class will decode the
053 * procedure in the WALEdit for better debugging. You are free to use {@link WALPrettyPrinter} to
054 * dump the same file as well.
055 */
056@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.TOOLS)
057@InterfaceStability.Evolving
058public class WALProcedurePrettyPrinter extends AbstractHBaseTool {
059
060  private static final String KEY_TMPL = "Sequence=%s, at write timestamp=%s";
061
062  private static final DateTimeFormatter FORMATTER =
063    DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(ZoneId.systemDefault());
064
065  private String file;
066
067  private PrintStream out;
068
069  public WALProcedurePrettyPrinter() {
070    this(System.out);
071  }
072
073  public WALProcedurePrettyPrinter(PrintStream out) {
074    this.out = out;
075  }
076
077  @Override
078  protected void addOptions() {
079  }
080
081  @Override
082  protected void processOptions(CommandLine cmd) {
083    if (cmd.getArgList().size() != 1) {
084      throw new IllegalArgumentException("Please specify the file to dump");
085    }
086    file = cmd.getArgList().get(0);
087  }
088
089  @Override
090  protected int doWork() throws Exception {
091    Path path = new Path(file);
092    FileSystem fs = path.getFileSystem(conf);
093    try (WALStreamReader reader = WALFactory.createStreamReader(fs, path, conf)) {
094      for (;;) {
095        WAL.Entry entry = reader.next();
096        if (entry == null) {
097          return 0;
098        }
099        WALKey key = entry.getKey();
100        WALEdit edit = entry.getEdit();
101        long sequenceId = key.getSequenceId();
102        long writeTime = key.getWriteTime();
103        out.println(
104          String.format(KEY_TMPL, sequenceId, FORMATTER.format(Instant.ofEpochMilli(writeTime))));
105        for (Cell cell : edit.getCells()) {
106          Map<String, Object> op = WALPrettyPrinter.toStringMap((ExtendedCell) cell);
107          if (
108            !Bytes.equals(PROC_FAMILY, 0, PROC_FAMILY.length, cell.getFamilyArray(),
109              cell.getFamilyOffset(), cell.getFamilyLength())
110          ) {
111            // We could have cells other than procedure edits, for example, a flush marker
112            WALPrettyPrinter.printCell(out, op, false, false);
113            continue;
114          }
115          long procId = Bytes.toLong(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength());
116          out.println("pid=" + procId + ", type=" + op.get("type") + ", column=" + op.get("family")
117            + ":" + op.get("qualifier"));
118          if (cell.getType() == Cell.Type.Put) {
119            if (cell.getValueLength() > 0) {
120              // should be a normal put
121              Procedure<?> proc =
122                ProcedureUtil.convertToProcedure(ProcedureProtos.Procedure.parser()
123                  .parseFrom(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()));
124              out.println("\t" + proc.toStringDetails());
125            } else {
126              // should be a 'delete' put
127              out.println("\tmark deleted");
128            }
129          }
130          out.println("cell total size sum: " + cell.heapSize());
131        }
132        out.println("edit heap size: " + edit.heapSize());
133        out.println("position: " + reader.getPosition());
134      }
135    }
136  }
137
138  public static void main(String[] args) {
139    new WALProcedurePrettyPrinter().doStaticMain(args);
140  }
141}