001/*
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *     http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 */
019package org.apache.hadoop.hbase.regionserver;
020
021import java.util.Collections;
022import java.util.Iterator;
023import java.util.List;
024import java.util.Objects;
025import java.util.SortedSet;
026import java.util.concurrent.atomic.AtomicReference;
027
028import org.apache.hadoop.hbase.Cell;
029import org.apache.hadoop.hbase.CellComparator;
030import org.apache.hadoop.hbase.PrivateCellUtil;
031import org.apache.hadoop.hbase.KeyValue;
032import org.apache.hadoop.hbase.KeyValueUtil;
033import org.apache.hadoop.hbase.io.TimeRange;
034import org.apache.hadoop.hbase.util.Bytes;
035import org.apache.hadoop.hbase.util.ClassSize;
036import org.apache.yetus.audience.InterfaceAudience;
037import org.slf4j.Logger;
038import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting;
039
040/**
041 * This is an abstraction of a segment maintained in a memstore, e.g., the active
042 * cell set or its snapshot.
043 *
044 * This abstraction facilitates the management of the compaction pipeline and the shifts of these
045 * segments from active set to snapshot set in the default implementation.
046 */
047@InterfaceAudience.Private
048public abstract class Segment implements MemStoreSizing {
049
050  public final static long FIXED_OVERHEAD = ClassSize.align(ClassSize.OBJECT
051      + 5 * ClassSize.REFERENCE // cellSet, comparator, memStoreLAB, memStoreSizing,
052                                // and timeRangeTracker
053      + Bytes.SIZEOF_LONG // minSequenceId
054      + Bytes.SIZEOF_BOOLEAN); // tagsPresent
055  public final static long DEEP_OVERHEAD = FIXED_OVERHEAD + ClassSize.ATOMIC_REFERENCE
056      + ClassSize.CELL_SET + 2 * ClassSize.ATOMIC_LONG;
057
058  private AtomicReference<CellSet> cellSet= new AtomicReference<>();
059  private final CellComparator comparator;
060  protected long minSequenceId;
061  private MemStoreLAB memStoreLAB;
062  // Sum of sizes of all Cells added to this Segment. Cell's HeapSize is considered. This is not
063  // including the heap overhead of this class.
064  protected final MemStoreSizing memStoreSizing;
065  protected final TimeRangeTracker timeRangeTracker;
066  protected volatile boolean tagsPresent;
067
068  // Empty constructor to be used when Segment is used as interface,
069  // and there is no need in true Segments state
070  protected Segment(CellComparator comparator, TimeRangeTracker trt) {
071    this.comparator = comparator;
072    // Do we need to be thread safe always? What if ImmutableSegment?
073    // DITTO for the TimeRangeTracker below.
074    this.memStoreSizing = new ThreadSafeMemStoreSizing();
075    this.timeRangeTracker = trt;
076  }
077
078  protected Segment(CellComparator comparator, List<ImmutableSegment> segments,
079      TimeRangeTracker trt) {
080    long dataSize = 0;
081    long heapSize = 0;
082    long OffHeapSize = 0;
083    for (Segment segment : segments) {
084      MemStoreSize memStoreSize = segment.getMemStoreSize();
085      dataSize += memStoreSize.getDataSize();
086      heapSize += memStoreSize.getHeapSize();
087      OffHeapSize += memStoreSize.getOffHeapSize();
088    }
089    this.comparator = comparator;
090    // Do we need to be thread safe always? What if ImmutableSegment?
091    // DITTO for the TimeRangeTracker below.
092    this.memStoreSizing = new ThreadSafeMemStoreSizing(dataSize, heapSize, OffHeapSize);
093    this.timeRangeTracker = trt;
094  }
095
096  // This constructor is used to create empty Segments.
097  protected Segment(CellSet cellSet, CellComparator comparator, MemStoreLAB memStoreLAB, TimeRangeTracker trt) {
098    this.cellSet.set(cellSet);
099    this.comparator = comparator;
100    this.minSequenceId = Long.MAX_VALUE;
101    this.memStoreLAB = memStoreLAB;
102    // Do we need to be thread safe always? What if ImmutableSegment?
103    // DITTO for the TimeRangeTracker below.
104    this.memStoreSizing = new ThreadSafeMemStoreSizing();
105    this.tagsPresent = false;
106    this.timeRangeTracker = trt;
107  }
108
109  protected Segment(Segment segment) {
110    this.cellSet.set(segment.getCellSet());
111    this.comparator = segment.getComparator();
112    this.minSequenceId = segment.getMinSequenceId();
113    this.memStoreLAB = segment.getMemStoreLAB();
114    this.memStoreSizing = new ThreadSafeMemStoreSizing(segment.memStoreSizing.getMemStoreSize());
115    this.tagsPresent = segment.isTagsPresent();
116    this.timeRangeTracker = segment.getTimeRangeTracker();
117  }
118
119  /**
120   * Creates the scanner for the given read point
121   * @return a scanner for the given read point
122   */
123  protected KeyValueScanner getScanner(long readPoint) {
124    return new SegmentScanner(this, readPoint);
125  }
126
127  /**
128   * Creates the scanner for the given read point, and a specific order in a list
129   * @return a scanner for the given read point
130   */
131  public KeyValueScanner getScanner(long readPoint, long order) {
132    return new SegmentScanner(this, readPoint, order);
133  }
134
135  public List<KeyValueScanner> getScanners(long readPoint, long order) {
136    return Collections.singletonList(new SegmentScanner(this, readPoint, order));
137  }
138
139  /**
140   * @return whether the segment has any cells
141   */
142  public boolean isEmpty() {
143    return getCellSet().isEmpty();
144  }
145
146  /**
147   * @return number of cells in segment
148   */
149  public int getCellsCount() {
150    return getCellSet().size();
151  }
152
153  /**
154   * Closing a segment before it is being discarded
155   */
156  public void close() {
157    if (this.memStoreLAB != null) {
158      this.memStoreLAB.close();
159    }
160    // do not set MSLab to null as scanners may still be reading the data here and need to decrease
161    // the counter when they finish
162  }
163
164  /**
165   * If the segment has a memory allocator the cell is being cloned to this space, and returned;
166   * otherwise the given cell is returned
167   *
168   * When a cell's size is too big (bigger than maxAlloc), it is not allocated on MSLAB.
169   * Since the process of flattening to CellChunkMap assumes that all cells
170   * are allocated on MSLAB, during this process, the input parameter
171   * forceCloneOfBigCell is set to 'true' and the cell is copied into MSLAB.
172   *
173   * @return either the given cell or its clone
174   */
175  public Cell maybeCloneWithAllocator(Cell cell, boolean forceCloneOfBigCell) {
176    if (this.memStoreLAB == null) {
177      return cell;
178    }
179
180    Cell cellFromMslab = null;
181    if (forceCloneOfBigCell) {
182      cellFromMslab = this.memStoreLAB.forceCopyOfBigCellInto(cell);
183    } else {
184      cellFromMslab = this.memStoreLAB.copyCellInto(cell);
185    }
186    return (cellFromMslab != null) ? cellFromMslab : cell;
187  }
188
189  /**
190   * Get cell length after serialized in {@link KeyValue}
191   */
192  @VisibleForTesting
193  static int getCellLength(Cell cell) {
194    return KeyValueUtil.length(cell);
195  }
196
197  public boolean shouldSeek(TimeRange tr, long oldestUnexpiredTS) {
198    return !isEmpty()
199        && (tr.isAllTime() || timeRangeTracker.includesTimeRange(tr))
200        && timeRangeTracker.getMax() >= oldestUnexpiredTS;
201  }
202
203  public boolean isTagsPresent() {
204    return tagsPresent;
205  }
206
207  public void incScannerCount() {
208    if (this.memStoreLAB != null) {
209      this.memStoreLAB.incScannerCount();
210    }
211  }
212
213  public void decScannerCount() {
214    if (this.memStoreLAB != null) {
215      this.memStoreLAB.decScannerCount();
216    }
217  }
218
219  /**
220   * Setting the CellSet of the segment - used only for flat immutable segment for setting
221   * immutable CellSet after its creation in immutable segment constructor
222   * @return this object
223   */
224
225  protected Segment setCellSet(CellSet cellSetOld, CellSet cellSetNew) {
226    this.cellSet.compareAndSet(cellSetOld, cellSetNew);
227    return this;
228  }
229
230  @Override
231  public MemStoreSize getMemStoreSize() {
232    return this.memStoreSizing.getMemStoreSize();
233  }
234
235  @Override
236  public long getDataSize() {
237    return this.memStoreSizing.getDataSize();
238  }
239
240  @Override
241  public long getHeapSize() {
242    return this.memStoreSizing.getHeapSize();
243  }
244
245  @Override
246  public long getOffHeapSize() {
247    return this.memStoreSizing.getOffHeapSize();
248  }
249
250  @Override
251  public long incMemStoreSize(long delta, long heapOverhead, long offHeapOverhead) {
252    return this.memStoreSizing.incMemStoreSize(delta, heapOverhead, offHeapOverhead);
253  }
254
255  public long getMinSequenceId() {
256    return minSequenceId;
257  }
258
259  public TimeRangeTracker getTimeRangeTracker() {
260    return this.timeRangeTracker;
261  }
262
263  //*** Methods for SegmentsScanner
264  public Cell last() {
265    return getCellSet().last();
266  }
267
268  public Iterator<Cell> iterator() {
269    return getCellSet().iterator();
270  }
271
272  public SortedSet<Cell> headSet(Cell firstKeyOnRow) {
273    return getCellSet().headSet(firstKeyOnRow);
274  }
275
276  public int compare(Cell left, Cell right) {
277    return getComparator().compare(left, right);
278  }
279
280  public int compareRows(Cell left, Cell right) {
281    return getComparator().compareRows(left, right);
282  }
283
284  /**
285   * @return a set of all cells in the segment
286   */
287  protected CellSet getCellSet() {
288    return cellSet.get();
289  }
290
291  /**
292   * Returns the Cell comparator used by this segment
293   * @return the Cell comparator used by this segment
294   */
295  protected CellComparator getComparator() {
296    return comparator;
297  }
298
299  protected void internalAdd(Cell cell, boolean mslabUsed, MemStoreSizing memstoreSizing) {
300    boolean succ = getCellSet().add(cell);
301    updateMetaInfo(cell, succ, mslabUsed, memstoreSizing);
302  }
303
304  protected void updateMetaInfo(Cell cellToAdd, boolean succ, boolean mslabUsed,
305      MemStoreSizing memstoreSizing) {
306    long cellSize = 0;
307    // If there's already a same cell in the CellSet and we are using MSLAB, we must count in the
308    // MSLAB allocation size as well, or else there will be memory leak (occupied heap size larger
309    // than the counted number)
310    boolean sizeChanged = succ || mslabUsed;
311    if (sizeChanged) {
312      cellSize = getCellLength(cellToAdd);
313    }
314    // same as above, if MSLAB is used, we need to inc the heap/offheap size, otherwise there will
315    // be a memory miscount. Since we are now use heapSize + offHeapSize to decide whether a flush
316    // is needed.
317    long heapSize = heapSizeChange(cellToAdd, sizeChanged);
318    long offHeapSize = offHeapSizeChange(cellToAdd, sizeChanged);
319    incMemStoreSize(cellSize, heapSize, offHeapSize);
320    if (memstoreSizing != null) {
321      memstoreSizing.incMemStoreSize(cellSize, heapSize, offHeapSize);
322    }
323    getTimeRangeTracker().includeTimestamp(cellToAdd);
324    minSequenceId = Math.min(minSequenceId, cellToAdd.getSequenceId());
325    // In no tags case this NoTagsKeyValue.getTagsLength() is a cheap call.
326    // When we use ACL CP or Visibility CP which deals with Tags during
327    // mutation, the TagRewriteCell.getTagsLength() is a cheaper call. We do not
328    // parse the byte[] to identify the tags length.
329    if (cellToAdd.getTagsLength() > 0) {
330      tagsPresent = true;
331    }
332  }
333
334  protected void updateMetaInfo(Cell cellToAdd, boolean succ, MemStoreSizing memstoreSizing) {
335    updateMetaInfo(cellToAdd, succ, (getMemStoreLAB()!=null), memstoreSizing);
336  }
337
338  /**
339   * @return The increase in heap size because of this cell addition. This includes this cell POJO's
340   *         heap size itself and additional overhead because of addition on to CSLM.
341   */
342  protected long heapSizeChange(Cell cell, boolean succ) {
343    long res = 0;
344    if (succ) {
345      boolean onHeap = true;
346      MemStoreLAB memStoreLAB = getMemStoreLAB();
347      if(memStoreLAB != null) {
348        onHeap = memStoreLAB.isOnHeap();
349      }
350      res += indexEntryOnHeapSize(onHeap);
351      if(onHeap) {
352        res += PrivateCellUtil.estimatedSizeOfCell(cell);
353      }
354      res = ClassSize.align(res);
355    }
356    return res;
357  }
358
359  protected long offHeapSizeChange(Cell cell, boolean succ) {
360    long res = 0;
361    if (succ) {
362      boolean offHeap = false;
363      MemStoreLAB memStoreLAB = getMemStoreLAB();
364      if(memStoreLAB != null) {
365        offHeap = memStoreLAB.isOffHeap();
366      }
367      res += indexEntryOffHeapSize(offHeap);
368      if(offHeap) {
369        res += PrivateCellUtil.estimatedSizeOfCell(cell);
370      }
371      res = ClassSize.align(res);
372    }
373    return res;
374  }
375
376  protected long indexEntryOnHeapSize(boolean onHeap) {
377    // in most cases index is allocated on-heap
378    // override this method when it is not always the case, e.g., in CCM
379    return indexEntrySize();
380  }
381
382  protected long indexEntryOffHeapSize(boolean offHeap) {
383    // in most cases index is allocated on-heap
384    // override this method when it is not always the case, e.g., in CCM
385    return 0;
386  }
387
388  protected abstract long indexEntrySize();
389
390  /**
391   * Returns a subset of the segment cell set, which starts with the given cell
392   * @param firstCell a cell in the segment
393   * @return a subset of the segment cell set, which starts with the given cell
394   */
395  protected SortedSet<Cell> tailSet(Cell firstCell) {
396    return getCellSet().tailSet(firstCell);
397  }
398
399  @VisibleForTesting
400  MemStoreLAB getMemStoreLAB() {
401    return memStoreLAB;
402  }
403
404  // Debug methods
405  /**
406   * Dumps all cells of the segment into the given log
407   */
408  void dump(Logger log) {
409    for (Cell cell: getCellSet()) {
410      log.debug(Objects.toString(cell));
411    }
412  }
413
414  @Override
415  public String toString() {
416    String res = "type=" + this.getClass().getSimpleName() + ", ";
417    res += "empty=" + (isEmpty()? "yes": "no") + ", ";
418    res += "cellCount=" + getCellsCount() + ", ";
419    res += "cellSize=" + getDataSize() + ", ";
420    res += "totalHeapSize=" + getHeapSize() + ", ";
421    res += "min timestamp=" + timeRangeTracker.getMin() + ", ";
422    res += "max timestamp=" + timeRangeTracker.getMax();
423    return res;
424  }
425}