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.wal;
019
020import java.io.Closeable;
021import java.io.IOException;
022import org.apache.hadoop.hbase.wal.WAL.Entry;
023import org.apache.yetus.audience.InterfaceAudience;
024
025/**
026 * A WAL reader which is designed for be able to tailing the WAL file which is currently being
027 * written. It adds support
028 */
029@InterfaceAudience.Private
030public interface WALTailingReader extends Closeable {
031
032  enum State {
033    /** This means we read an Entry without any error */
034    NORMAL,
035    /**
036     * This means the WAL file has a trailer and we have reached it, which means we have finished
037     * reading this file normally
038     */
039    EOF_WITH_TRAILER,
040    /**
041     * This means we meet an error so the upper layer need to reset to read again
042     */
043    ERROR_AND_RESET,
044    /**
045     * Mostly the same with the above {@link #ERROR_AND_RESET}, the difference is that here we also
046     * mess up the compression dictionary when reading data, so the upper layer should also clear
047     * the compression context when reseting, which means when calling resetTo method, we need to
048     * skip to the position instead of just seek to, which will impact performance.
049     */
050    ERROR_AND_RESET_COMPRESSION,
051    /**
052     * This means we reach the EOF and the upper layer need to reset to see if there is more data.
053     * Notice that this does not mean that there is necessarily more data, the upper layer should
054     * determine whether they need to reset and read again.
055     */
056    EOF_AND_RESET,
057    /**
058     * Mostly the same with the above {@link #EOF_AND_RESET}, the difference is that here we also
059     * mess up the compression dictionary when reading data, so the upper layer should also clear
060     * the compression context when reseting, which means when calling resetTo method, we need to
061     * skip to the position instead of just seek to, which will impact performance. The
062     * implementation should try its best to not fall into this situation.
063     */
064    EOF_AND_RESET_COMPRESSION;
065
066    /**
067     * A dummy result for returning, as except {@link NORMAL}, for other state we do not need to
068     * provide fields other than state in the returned {@link Result}.
069     */
070    private Result result = new Result(this, null, -1);
071
072    public Result getResult() {
073      return result;
074    }
075
076    public boolean resetCompression() {
077      return this == ERROR_AND_RESET_COMPRESSION || this == EOF_AND_RESET_COMPRESSION;
078    }
079
080    public boolean eof() {
081      return this == EOF_AND_RESET || this == EOF_AND_RESET_COMPRESSION || this == EOF_WITH_TRAILER;
082    }
083  }
084
085  final class Result {
086
087    private final State state;
088    private final Entry entry;
089    private final long entryEndPos;
090
091    public Result(State state, Entry entry, long entryEndPos) {
092      this.state = state;
093      this.entry = entry;
094      this.entryEndPos = entryEndPos;
095    }
096
097    public State getState() {
098      return state;
099    }
100
101    public Entry getEntry() {
102      return entry;
103    }
104
105    public long getEntryEndPos() {
106      return entryEndPos;
107    }
108  }
109
110  /**
111   * Read the next entry and make sure the position after reading does not go beyond the given
112   * {@code limit}.
113   * <p/>
114   * Notice that we will not throw any checked exception out, all the states are represented by the
115   * return value. Of course we will log the exceptions out. The reason why we do this is that, for
116   * tailing a WAL file which is currently being written, we will hit EOFException many times, so it
117   * should not be considered as an 'exception' and also, creating an Exception is a bit expensive.
118   * @param limit the position limit. See HBASE-14004 for more details about why we need this
119   *              limitation. -1 means no limit.
120   */
121  Result next(long limit);
122
123  /**
124   * Get the current reading position.
125   */
126  long getPosition() throws IOException;
127
128  /**
129   * Reopen the reader to see if there is new data arrives, and also seek(or skip) to the given
130   * position.
131   * <p/>
132   * If you want to read from the beginning instead of a given position, please pass -1 as
133   * {@code position}, then the reader will locate to the first entry. Notice that, since we have a
134   * magic header and a pb header, the first WAL entry is not located at position 0, so passing 0
135   * will cause trouble.
136   * @param position         the position we want to start reading from after resetting, or -1 if
137   *                         you want to start reading from the beginning.
138   * @param resetCompression whether we also need to clear the compression context. If {@code true},
139   *                         we will use skip instead of seek after resetting.
140   */
141  void resetTo(long position, boolean resetCompression) throws IOException;
142
143  /**
144   * Override to remove the 'throws IOException' as we are just a reader.
145   */
146  @Override
147  void close();
148}