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.Optional;
024import java.util.Set;
025import java.util.TreeSet;
026import org.apache.hadoop.hbase.Cell;
027import org.apache.hadoop.hbase.CellUtil;
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.util.CollectionUtils;
034import org.apache.hadoop.hbase.wal.WAL.Entry;
035import org.apache.hadoop.hbase.wal.WALEdit;
036import org.apache.hadoop.hbase.wal.WALKeyImpl;
037import org.apache.yetus.audience.InterfaceAudience;
038
039import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting;
040
041/**
042 * A WAL Entry for {@link AbstractFSWAL} implementation.  Immutable.
043 * A subclass of {@link Entry} that carries extra info across the ring buffer such as
044 * region sequence id (we want to use this later, just before we write the WAL to ensure region
045 * edits maintain order).  The extra info added here is not 'serialized' as part of the WALEdit
046 * hence marked 'transient' to underline this fact.  It also adds mechanism so we can wait on
047 * the assign of the region sequence id.  See #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  private final transient boolean inMemstore;
055  private final transient RegionInfo regionInfo;
056  private final transient Set<byte[]> familyNames;
057  private final transient Optional<ServerCall<?>> rpcCall;
058
059  FSWALEntry(final long txid, final WALKeyImpl key, final WALEdit edit, final RegionInfo regionInfo,
060    final boolean inMemstore, ServerCall<?> rpcCall) {
061    super(key, edit);
062    this.inMemstore = inMemstore;
063    this.regionInfo = regionInfo;
064    this.txid = txid;
065    if (inMemstore) {
066      // construct familyNames here to reduce the work of log sinker.
067      Set<byte[]> families = edit.getFamilies();
068      this.familyNames = families != null ? families : collectFamilies(edit.getCells());
069    } else {
070      this.familyNames = Collections.<byte[]> emptySet();
071    }
072    this.rpcCall = Optional.ofNullable(rpcCall);
073    if (rpcCall != null) {
074      rpcCall.retainByWAL();
075    }
076  }
077
078  @VisibleForTesting
079  static Set<byte[]> collectFamilies(List<Cell> cells) {
080    if (CollectionUtils.isEmpty(cells)) {
081      return Collections.emptySet();
082    } else {
083      Set<byte[]> set = new TreeSet<>(Bytes.BYTES_COMPARATOR);
084      for (Cell cell: cells) {
085        if (!CellUtil.matchingFamily(cell, WALEdit.METAFAMILY)) {
086          set.add(CellUtil.cloneFamily(cell));
087        }
088      }
089      return set;
090    }
091  }
092
093  @Override
094  public String toString() {
095    return "sequence=" + this.txid + ", " + super.toString();
096  };
097
098  boolean isInMemStore() {
099    return this.inMemstore;
100  }
101
102  RegionInfo getRegionInfo() {
103    return this.regionInfo;
104  }
105
106  /**
107   * @return The transaction id of this edit.
108   */
109  long getTxid() {
110    return this.txid;
111  }
112
113  /**
114   * Here is where a WAL edit gets its sequenceid. SIDE-EFFECT is our stamping the sequenceid into
115   * every Cell AND setting the sequenceid into the MVCC WriteEntry!!!!
116   * @return The sequenceid we stamped on this edit.
117   */
118  long stampRegionSequenceId(MultiVersionConcurrencyControl.WriteEntry we) throws IOException {
119    long regionSequenceId = we.getWriteNumber();
120    if (!this.getEdit().isReplay() && inMemstore) {
121      for (Cell c : getEdit().getCells()) {
122        PrivateCellUtil.setSequenceId(c, regionSequenceId);
123      }
124    }
125
126    getKey().setWriteEntry(we);
127    return regionSequenceId;
128  }
129
130  /**
131   * @return the family names which are effected by this edit.
132   */
133  Set<byte[]> getFamilyNames() {
134    return familyNames;
135  }
136
137  void release() {
138    rpcCall.ifPresent(ServerCall::releaseByWAL);
139  }
140}