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}