3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
19 package org
.apache
.hadoop
.hbase
.wal
;
21 import java
.io
.IOException
;
22 import java
.util
.ArrayList
;
23 import java
.util
.List
;
26 import java
.util
.TreeSet
;
27 import org
.apache
.hadoop
.hbase
.Cell
;
28 import org
.apache
.hadoop
.hbase
.CellUtil
;
29 import org
.apache
.hadoop
.hbase
.HBaseInterfaceAudience
;
30 import org
.apache
.hadoop
.hbase
.KeyValue
;
31 import org
.apache
.hadoop
.hbase
.PrivateCellUtil
;
32 import org
.apache
.hadoop
.hbase
.client
.RegionInfo
;
33 import org
.apache
.hadoop
.hbase
.codec
.Codec
;
34 import org
.apache
.hadoop
.hbase
.io
.HeapSize
;
35 import org
.apache
.hadoop
.hbase
.util
.Bytes
;
36 import org
.apache
.hadoop
.hbase
.util
.ClassSize
;
37 import org
.apache
.hadoop
.hbase
.util
.EnvironmentEdgeManager
;
38 import org
.apache
.yetus
.audience
.InterfaceAudience
;
40 import org
.apache
.hbase
.thirdparty
.com
.google
.common
.annotations
.VisibleForTesting
;
42 import org
.apache
.hadoop
.hbase
.shaded
.protobuf
.generated
.WALProtos
;
43 import org
.apache
.hadoop
.hbase
.shaded
.protobuf
.generated
.WALProtos
.CompactionDescriptor
;
44 import org
.apache
.hadoop
.hbase
.shaded
.protobuf
.generated
.WALProtos
.FlushDescriptor
;
45 import org
.apache
.hadoop
.hbase
.shaded
.protobuf
.generated
.WALProtos
.RegionEventDescriptor
;
48 * Used in HBase's transaction log (WAL) to represent a collection of edits (Cell/KeyValue objects)
49 * that came in as a single transaction. All the edits for a given transaction are written out as a
50 * single record, in PB format, followed (optionally) by Cells written via the WALCellEncoder.
51 * <p>This class is LimitedPrivate for CPs to read-only. The {@link #add} methods are
52 * classified as private methods, not for use by CPs.</p>
53 * <p>WALEdit will accumulate a Set of all column family names referenced by the Cells
54 * {@link #add(Cell)}'d. This is an optimization. Usually when loading a WALEdit, we have the
55 * column family name to-hand.. just shove it into the WALEdit if available. Doing this, we can
56 * save on a parse of each Cell to figure column family down the line when we go to add the
57 * WALEdit to the WAL file. See the hand-off in FSWALEntry Constructor.
59 // TODO: Do not expose this class to Coprocessors. It has set methods. A CP might meddle.
60 @InterfaceAudience.LimitedPrivate({ HBaseInterfaceAudience
.REPLICATION
,
61 HBaseInterfaceAudience
.COPROC
})
62 public class WALEdit
implements HeapSize
{
64 // TODO: Get rid of this; see HBASE-8457
65 public static final byte [] METAFAMILY
= Bytes
.toBytes("METAFAMILY");
67 public static final byte [] METAROW
= Bytes
.toBytes("METAROW");
69 public static final byte[] COMPACTION
= Bytes
.toBytes("HBASE::COMPACTION");
71 public static final byte [] FLUSH
= Bytes
.toBytes("HBASE::FLUSH");
73 public static final byte [] REGION_EVENT
= Bytes
.toBytes("HBASE::REGION_EVENT");
75 public static final byte [] BULK_LOAD
= Bytes
.toBytes("HBASE::BULK_LOAD");
77 private final boolean replay
;
79 private ArrayList
<Cell
> cells
= null;
82 * All the Cell families in <code>cells</code>. Updated by {@link #add(Cell)} and
83 * {@link #add(Map)}. This Set is passed to the FSWALEntry so it does not have
84 * to recalculate the Set of families in a transaction; makes for a bunch of CPU savings.
85 * An optimization that saves on CPU-expensive Cell-parsing.
87 private Set
<byte []> families
= null;
94 * @deprecated Since 2.0.1. Use {@link #WALEdit(int, boolean)} instead.
97 public WALEdit(boolean isReplay
) {
102 * @deprecated Since 2.0.1. Use {@link #WALEdit(int, boolean)} instead.
105 public WALEdit(int cellCount
) {
106 this(cellCount
, false);
110 * @param cellCount Pass so can pre-size the WALEdit. Optimization.
112 public WALEdit(int cellCount
, boolean isReplay
) {
113 this.replay
= isReplay
;
114 cells
= new ArrayList
<>(cellCount
);
117 private Set
<byte[]> getOrCreateFamilies() {
118 if (this.families
== null) {
119 this.families
= new TreeSet
<byte []>(Bytes
.BYTES_COMPARATOR
);
121 return this.families
;
125 * For use by FSWALEntry ONLY. An optimization.
126 * @return All families in {@link #getCells()}; may be null.
128 public Set
<byte []> getFamilies() {
129 return this.families
;
133 * @return True is <code>f</code> is {@link #METAFAMILY}
135 public static boolean isMetaEditFamily(final byte [] f
) {
136 return Bytes
.equals(METAFAMILY
, f
);
139 public static boolean isMetaEditFamily(Cell cell
) {
140 return CellUtil
.matchingFamily(cell
, METAFAMILY
);
143 public boolean isMetaEdit() {
144 for (Cell cell
: cells
) {
145 if (!isMetaEditFamily(cell
)) {
153 * @return True when current WALEdit is created by log replay. Replication skips WALEdits from
156 public boolean isReplay() {
160 @InterfaceAudience.Private
161 public WALEdit
add(Cell cell
, byte [] family
) {
162 getOrCreateFamilies().add(family
);
163 return addCell(cell
);
166 @InterfaceAudience.Private
167 public WALEdit
add(Cell cell
) {
168 // We clone Family each time we add a Cell. Expensive but safe. For CPU savings, use
169 // add(Map) or add(Cell, family).
170 return add(cell
, CellUtil
.cloneFamily(cell
));
173 public boolean isEmpty() {
174 return cells
.isEmpty();
181 public ArrayList
<Cell
> getCells() {
186 * This is not thread safe.
187 * This will change the WALEdit and shouldn't be used unless you are sure that nothing
188 * else depends on the contents being immutable.
190 * @param cells the list of cells that this WALEdit now contains.
192 @InterfaceAudience.Private
194 public void setCells(ArrayList
<Cell
> cells
) {
196 this.families
= null;
200 * Reads WALEdit from cells.
201 * @param cellDecoder Cell decoder.
202 * @param expectedCount Expected cell count.
203 * @return Number of KVs read.
205 public int readFromCells(Codec
.Decoder cellDecoder
, int expectedCount
) throws IOException
{
207 cells
.ensureCapacity(expectedCount
);
208 while (cells
.size() < expectedCount
&& cellDecoder
.advance()) {
209 cells
.add(cellDecoder
.current());
215 public long heapSize() {
216 long ret
= ClassSize
.ARRAYLIST
;
217 for (Cell cell
: cells
) {
218 ret
+= cell
.heapSize();
223 public long estimatedSerializedSizeOf() {
225 for (Cell cell
: cells
) {
226 ret
+= PrivateCellUtil
.estimatedSerializedSizeOf(cell
);
232 public String
toString() {
233 StringBuilder sb
= new StringBuilder();
235 sb
.append("[#edits: " + cells
.size() + " = <");
236 for (Cell cell
: cells
) {
241 return sb
.toString();
244 public static WALEdit
createFlushWALEdit(RegionInfo hri
, FlushDescriptor f
) {
245 KeyValue kv
= new KeyValue(getRowForRegion(hri
), METAFAMILY
, FLUSH
,
246 EnvironmentEdgeManager
.currentTime(), f
.toByteArray());
247 return new WALEdit().add(kv
, METAFAMILY
);
250 public static FlushDescriptor
getFlushDescriptor(Cell cell
) throws IOException
{
251 if (CellUtil
.matchingColumn(cell
, METAFAMILY
, FLUSH
)) {
252 return FlushDescriptor
.parseFrom(CellUtil
.cloneValue(cell
));
257 public static WALEdit
createRegionEventWALEdit(RegionInfo hri
,
258 RegionEventDescriptor regionEventDesc
) {
259 KeyValue kv
= new KeyValue(getRowForRegion(hri
), METAFAMILY
, REGION_EVENT
,
260 EnvironmentEdgeManager
.currentTime(), regionEventDesc
.toByteArray());
261 return new WALEdit().add(kv
, METAFAMILY
);
264 public static RegionEventDescriptor
getRegionEventDescriptor(Cell cell
) throws IOException
{
265 if (CellUtil
.matchingColumn(cell
, METAFAMILY
, REGION_EVENT
)) {
266 return RegionEventDescriptor
.parseFrom(CellUtil
.cloneValue(cell
));
272 * Create a compaction WALEdit
274 * @return A WALEdit that has <code>c</code> serialized as its value
276 public static WALEdit
createCompaction(final RegionInfo hri
, final CompactionDescriptor c
) {
277 byte [] pbbytes
= c
.toByteArray();
278 KeyValue kv
= new KeyValue(getRowForRegion(hri
), METAFAMILY
, COMPACTION
,
279 EnvironmentEdgeManager
.currentTime(), pbbytes
);
280 return new WALEdit().add(kv
, METAFAMILY
); //replication scope null so this won't be replicated
283 public static byte[] getRowForRegion(RegionInfo hri
) {
284 byte[] startKey
= hri
.getStartKey();
285 if (startKey
.length
== 0) {
286 // empty row key is not allowed in mutations because it is both the start key and the end key
287 // we return the smallest byte[] that is bigger (in lex comparison) than byte[0].
288 return new byte[] {0};
294 * Deserialized and returns a CompactionDescriptor is the KeyValue contains one.
295 * @param kv the key value
296 * @return deserialized CompactionDescriptor or null.
298 public static CompactionDescriptor
getCompaction(Cell kv
) throws IOException
{
299 if (isCompactionMarker(kv
)) {
300 return CompactionDescriptor
.parseFrom(CellUtil
.cloneValue(kv
));
306 * Returns true if the given cell is a serialized {@link CompactionDescriptor}
308 * @see #getCompaction(Cell)
310 public static boolean isCompactionMarker(Cell cell
) {
311 return CellUtil
.matchingColumn(cell
, METAFAMILY
, COMPACTION
);
315 * Create a bulk loader WALEdit
317 * @param hri The RegionInfo for the region in which we are bulk loading
318 * @param bulkLoadDescriptor The descriptor for the Bulk Loader
319 * @return The WALEdit for the BulkLoad
321 public static WALEdit
createBulkLoadEvent(RegionInfo hri
,
322 WALProtos
.BulkLoadDescriptor bulkLoadDescriptor
) {
323 KeyValue kv
= new KeyValue(getRowForRegion(hri
),
326 EnvironmentEdgeManager
.currentTime(),
327 bulkLoadDescriptor
.toByteArray());
328 return new WALEdit().add(kv
, METAFAMILY
);
332 * Deserialized and returns a BulkLoadDescriptor from the passed in Cell
333 * @param cell the key value
334 * @return deserialized BulkLoadDescriptor or null.
336 public static WALProtos
.BulkLoadDescriptor
getBulkLoadDescriptor(Cell cell
) throws IOException
{
337 if (CellUtil
.matchingColumn(cell
, METAFAMILY
, BULK_LOAD
)) {
338 return WALProtos
.BulkLoadDescriptor
.parseFrom(CellUtil
.cloneValue(cell
));
344 * Append the given map of family->edits to a WALEdit data structure.
345 * This does not write to the WAL itself.
346 * Note that as an optimization, we will stamp the Set of column families into the WALEdit
347 * to save on our having to calculate it subsequently way down in the actual WAL writing.
349 * @param familyMap map of family->edits
351 public void add(Map
<byte[], List
<Cell
>> familyMap
) {
352 for (Map
.Entry
<byte [], List
<Cell
>> e
: familyMap
.entrySet()) {
353 // 'foreach' loop NOT used. See HBASE-12023 "...creates too many iterator objects."
354 int listSize
= e
.getValue().size();
355 // Add all Cells first and then at end, add the family rather than call {@link #add(Cell)}
356 // and have it clone family each time. Optimization!
357 for (int i
= 0; i
< listSize
; i
++) {
358 addCell(e
.getValue().get(i
));
360 addFamily(e
.getKey());
364 private void addFamily(byte [] family
) {
365 getOrCreateFamilies().add(family
);
368 private WALEdit
addCell(Cell cell
) {
369 this.cells
.add(cell
);