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}