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.regionserver.wal;
019
020import java.io.IOException;
021import java.util.Collections;
022import java.util.List;
023import java.util.Set;
024import java.util.TreeSet;
025import org.apache.hadoop.hbase.Cell;
026import org.apache.hadoop.hbase.CellUtil;
027import org.apache.hadoop.hbase.ExtendedCell;
028import org.apache.hadoop.hbase.PrivateCellUtil;
029import org.apache.hadoop.hbase.client.RegionInfo;
030import org.apache.hadoop.hbase.ipc.ServerCall;
031import org.apache.hadoop.hbase.regionserver.MultiVersionConcurrencyControl;
032import org.apache.hadoop.hbase.util.Bytes;
033import org.apache.hadoop.hbase.wal.WAL.Entry;
034import org.apache.hadoop.hbase.wal.WALEdit;
035import org.apache.hadoop.hbase.wal.WALEditInternalHelper;
036import org.apache.hadoop.hbase.wal.WALKeyImpl;
037import org.apache.yetus.audience.InterfaceAudience;
038
039import org.apache.hbase.thirdparty.org.apache.commons.collections4.CollectionUtils;
040
041/**
042 * A WAL Entry for {@link AbstractFSWAL} implementation. Immutable. A subclass of {@link Entry} that
043 * carries extra info across the ring buffer such as region sequenceid (we want to use this later,
044 * just before we write the WAL to ensure region edits maintain order). The extra info added here is
045 * not 'serialized' as part of the WALEdit hence marked 'transient' to underline this fact. It also
046 * adds mechanism so we can wait on the assign of the region sequence id. See
047 * #stampRegionSequenceId().
048 */
049@InterfaceAudience.Private
050class FSWALEntry extends Entry {
051  // The below data members are denoted 'transient' just to highlight these are not persisted;
052  // they are only in memory and held here while passing over the ring buffer.
053  private final transient long txid;
054
055  /**
056   * If false, means this is a meta edit written by the hbase system itself. It was not in memstore.
057   * HBase uses these edit types to note in the log operational transitions such as compactions,
058   * flushes, or region open/closes.
059   */
060  private final transient boolean inMemstore;
061
062  /**
063   * Set if this is a meta edit and it is of close region type.
064   */
065  private final transient boolean closeRegion;
066
067  private final transient RegionInfo regionInfo;
068  private final transient Set<byte[]> familyNames;
069  private final transient ServerCall<?> rpcCall;
070
071  /**
072   * @param inMemstore If true, then this is a data edit, one that came from client. If false, it is
073   *                   a meta edit made by the hbase system itself and is for the WAL only.
074   */
075  FSWALEntry(final long txid, final WALKeyImpl key, final WALEdit edit, final RegionInfo regionInfo,
076    final boolean inMemstore, ServerCall<?> rpcCall) {
077    super(key, edit);
078    this.inMemstore = inMemstore;
079    this.closeRegion = !inMemstore && edit.isRegionCloseMarker();
080    this.regionInfo = regionInfo;
081    this.txid = txid;
082    if (inMemstore) {
083      // construct familyNames here to reduce the work of log sinker.
084      Set<byte[]> families = edit.getFamilies();
085      this.familyNames =
086        families != null ? families : collectFamilies(WALEditInternalHelper.getExtendedCells(edit));
087    } else {
088      this.familyNames = Collections.emptySet();
089    }
090    this.rpcCall = rpcCall;
091    if (rpcCall != null) {
092      rpcCall.retainByWAL();
093    }
094  }
095
096  static Set<byte[]> collectFamilies(List<ExtendedCell> cells) {
097    if (CollectionUtils.isEmpty(cells)) {
098      return Collections.emptySet();
099    } else {
100      Set<byte[]> set = new TreeSet<>(Bytes.BYTES_COMPARATOR);
101      for (Cell cell : cells) {
102        if (!WALEdit.isMetaEditFamily(cell)) {
103          set.add(CellUtil.cloneFamily(cell));
104        }
105      }
106      return set;
107    }
108  }
109
110  @Override
111  public String toString() {
112    return "sequence=" + this.txid + ", " + super.toString();
113  }
114
115  boolean isInMemStore() {
116    return this.inMemstore;
117  }
118
119  boolean isCloseRegion() {
120    return closeRegion;
121  }
122
123  RegionInfo getRegionInfo() {
124    return this.regionInfo;
125  }
126
127  /** Returns The transaction id of this edit. */
128  long getTxid() {
129    return this.txid;
130  }
131
132  /**
133   * Here is where a WAL edit gets its sequenceid. SIDE-EFFECT is our stamping the sequenceid into
134   * every Cell AND setting the sequenceid into the MVCC WriteEntry!!!!
135   * @return The sequenceid we stamped on this edit.
136   */
137  long stampRegionSequenceId(MultiVersionConcurrencyControl.WriteEntry we) throws IOException {
138    long regionSequenceId = we.getWriteNumber();
139    if (!this.getEdit().isReplay() && inMemstore) {
140      for (Cell c : getEdit().getCells()) {
141        PrivateCellUtil.setSequenceId(c, regionSequenceId);
142      }
143    }
144
145    getKey().setWriteEntry(we);
146    return regionSequenceId;
147  }
148
149  /** Returns the family names which are effected by this edit. */
150  Set<byte[]> getFamilyNames() {
151    return familyNames;
152  }
153
154  void release() {
155    if (rpcCall != null) {
156      rpcCall.releaseByWAL();
157    }
158  }
159}