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.io.IOException; 022import java.io.InterruptedIOException; 023import java.net.InetSocketAddress; 024import java.util.ArrayList; 025import java.util.Collection; 026import java.util.Collections; 027import java.util.HashMap; 028import java.util.HashSet; 029import java.util.Iterator; 030import java.util.List; 031import java.util.Map; 032import java.util.NavigableSet; 033import java.util.Optional; 034import java.util.OptionalDouble; 035import java.util.OptionalLong; 036import java.util.Set; 037import java.util.concurrent.Callable; 038import java.util.concurrent.CompletionService; 039import java.util.concurrent.ConcurrentHashMap; 040import java.util.concurrent.ExecutionException; 041import java.util.concurrent.ExecutorCompletionService; 042import java.util.concurrent.Future; 043import java.util.concurrent.ThreadPoolExecutor; 044import java.util.concurrent.atomic.AtomicBoolean; 045import java.util.concurrent.atomic.AtomicLong; 046import java.util.concurrent.locks.ReentrantLock; 047import java.util.concurrent.locks.ReentrantReadWriteLock; 048import java.util.function.Predicate; 049import java.util.function.ToLongFunction; 050import java.util.stream.Collectors; 051import java.util.stream.LongStream; 052 053import org.apache.hadoop.conf.Configuration; 054import org.apache.hadoop.fs.FileSystem; 055import org.apache.hadoop.fs.Path; 056import org.apache.hadoop.hbase.Cell; 057import org.apache.hadoop.hbase.CellComparator; 058import org.apache.hadoop.hbase.CellUtil; 059import org.apache.hadoop.hbase.CompoundConfiguration; 060import org.apache.hadoop.hbase.HConstants; 061import org.apache.hadoop.hbase.MemoryCompactionPolicy; 062import org.apache.hadoop.hbase.TableName; 063import org.apache.hadoop.hbase.backup.FailedArchiveException; 064import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor; 065import org.apache.hadoop.hbase.client.RegionInfo; 066import org.apache.hadoop.hbase.client.Scan; 067import org.apache.hadoop.hbase.conf.ConfigurationManager; 068import org.apache.hadoop.hbase.conf.PropagatingConfigurationObserver; 069import org.apache.hadoop.hbase.io.HeapSize; 070import org.apache.hadoop.hbase.io.compress.Compression; 071import org.apache.hadoop.hbase.io.crypto.Encryption; 072import org.apache.hadoop.hbase.io.hfile.CacheConfig; 073import org.apache.hadoop.hbase.io.hfile.HFile; 074import org.apache.hadoop.hbase.io.hfile.HFileContext; 075import org.apache.hadoop.hbase.io.hfile.HFileContextBuilder; 076import org.apache.hadoop.hbase.io.hfile.HFileDataBlockEncoder; 077import org.apache.hadoop.hbase.io.hfile.HFileDataBlockEncoderImpl; 078import org.apache.hadoop.hbase.io.hfile.HFileScanner; 079import org.apache.hadoop.hbase.io.hfile.InvalidHFileException; 080import org.apache.hadoop.hbase.log.HBaseMarkers; 081import org.apache.hadoop.hbase.monitoring.MonitoredTask; 082import org.apache.hadoop.hbase.regionserver.compactions.CompactionContext; 083import org.apache.hadoop.hbase.regionserver.compactions.CompactionLifeCycleTracker; 084import org.apache.hadoop.hbase.regionserver.compactions.CompactionProgress; 085import org.apache.hadoop.hbase.regionserver.compactions.CompactionRequestImpl; 086import org.apache.hadoop.hbase.regionserver.compactions.DefaultCompactor; 087import org.apache.hadoop.hbase.regionserver.compactions.OffPeakHours; 088import org.apache.hadoop.hbase.regionserver.querymatcher.ScanQueryMatcher; 089import org.apache.hadoop.hbase.regionserver.throttle.ThroughputController; 090import org.apache.hadoop.hbase.regionserver.wal.WALUtil; 091import org.apache.hadoop.hbase.security.EncryptionUtil; 092import org.apache.hadoop.hbase.security.User; 093import org.apache.hadoop.hbase.util.Bytes; 094import org.apache.hadoop.hbase.util.ChecksumType; 095import org.apache.hadoop.hbase.util.ClassSize; 096import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 097import org.apache.hadoop.hbase.util.Pair; 098import org.apache.hadoop.hbase.util.ReflectionUtils; 099import org.apache.hadoop.util.StringUtils; 100import org.apache.hadoop.util.StringUtils.TraditionalBinaryPrefix; 101import org.apache.yetus.audience.InterfaceAudience; 102import org.slf4j.Logger; 103import org.slf4j.LoggerFactory; 104import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting; 105import org.apache.hbase.thirdparty.com.google.common.base.Preconditions; 106import org.apache.hbase.thirdparty.com.google.common.collect.ImmutableCollection; 107import org.apache.hbase.thirdparty.com.google.common.collect.ImmutableList; 108import org.apache.hbase.thirdparty.com.google.common.collect.Lists; 109import org.apache.hbase.thirdparty.com.google.common.collect.Sets; 110import org.apache.hbase.thirdparty.org.apache.commons.collections4.CollectionUtils; 111import org.apache.hbase.thirdparty.org.apache.commons.collections4.IterableUtils; 112import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 113import org.apache.hadoop.hbase.shaded.protobuf.generated.WALProtos.CompactionDescriptor; 114 115/** 116 * A Store holds a column family in a Region. Its a memstore and a set of zero 117 * or more StoreFiles, which stretch backwards over time. 118 * 119 * <p>There's no reason to consider append-logging at this level; all logging 120 * and locking is handled at the HRegion level. Store just provides 121 * services to manage sets of StoreFiles. One of the most important of those 122 * services is compaction services where files are aggregated once they pass 123 * a configurable threshold. 124 * 125 * <p>Locking and transactions are handled at a higher level. This API should 126 * not be called directly but by an HRegion manager. 127 */ 128@InterfaceAudience.Private 129public class HStore implements Store, HeapSize, StoreConfigInformation, PropagatingConfigurationObserver { 130 public static final String MEMSTORE_CLASS_NAME = "hbase.regionserver.memstore.class"; 131 public static final String COMPACTCHECKER_INTERVAL_MULTIPLIER_KEY = 132 "hbase.server.compactchecker.interval.multiplier"; 133 public static final String BLOCKING_STOREFILES_KEY = "hbase.hstore.blockingStoreFiles"; 134 public static final String BLOCK_STORAGE_POLICY_KEY = "hbase.hstore.block.storage.policy"; 135 // keep in accordance with HDFS default storage policy 136 public static final String DEFAULT_BLOCK_STORAGE_POLICY = "HOT"; 137 public static final int DEFAULT_COMPACTCHECKER_INTERVAL_MULTIPLIER = 1000; 138 public static final int DEFAULT_BLOCKING_STOREFILE_COUNT = 16; 139 140 private static final Logger LOG = LoggerFactory.getLogger(HStore.class); 141 142 protected final MemStore memstore; 143 // This stores directory in the filesystem. 144 protected final HRegion region; 145 private final ColumnFamilyDescriptor family; 146 private final HRegionFileSystem fs; 147 protected Configuration conf; 148 protected CacheConfig cacheConf; 149 private long lastCompactSize = 0; 150 volatile boolean forceMajor = false; 151 /* how many bytes to write between status checks */ 152 static int closeCheckInterval = 0; 153 private AtomicLong storeSize = new AtomicLong(); 154 private AtomicLong totalUncompressedBytes = new AtomicLong(); 155 156 /** 157 * RWLock for store operations. 158 * Locked in shared mode when the list of component stores is looked at: 159 * - all reads/writes to table data 160 * - checking for split 161 * Locked in exclusive mode when the list of component stores is modified: 162 * - closing 163 * - completing a compaction 164 */ 165 final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); 166 /** 167 * Lock specific to archiving compacted store files. This avoids races around 168 * the combination of retrieving the list of compacted files and moving them to 169 * the archive directory. Since this is usually a background process (other than 170 * on close), we don't want to handle this with the store write lock, which would 171 * block readers and degrade performance. 172 * 173 * Locked by: 174 * - CompactedHFilesDispatchHandler via closeAndArchiveCompactedFiles() 175 * - close() 176 */ 177 final ReentrantLock archiveLock = new ReentrantLock(); 178 179 private final boolean verifyBulkLoads; 180 181 private ScanInfo scanInfo; 182 183 // All access must be synchronized. 184 // TODO: ideally, this should be part of storeFileManager, as we keep passing this to it. 185 private final List<HStoreFile> filesCompacting = Lists.newArrayList(); 186 187 // All access must be synchronized. 188 private final Set<ChangedReadersObserver> changedReaderObservers = 189 Collections.newSetFromMap(new ConcurrentHashMap<ChangedReadersObserver, Boolean>()); 190 191 protected final int blocksize; 192 private HFileDataBlockEncoder dataBlockEncoder; 193 194 /** Checksum configuration */ 195 protected ChecksumType checksumType; 196 protected int bytesPerChecksum; 197 198 // Comparing KeyValues 199 protected final CellComparator comparator; 200 201 final StoreEngine<?, ?, ?, ?> storeEngine; 202 203 private static final AtomicBoolean offPeakCompactionTracker = new AtomicBoolean(); 204 private volatile OffPeakHours offPeakHours; 205 206 private static final int DEFAULT_FLUSH_RETRIES_NUMBER = 10; 207 private int flushRetriesNumber; 208 private int pauseTime; 209 210 private long blockingFileCount; 211 private int compactionCheckMultiplier; 212 protected Encryption.Context cryptoContext = Encryption.Context.NONE; 213 214 private AtomicLong flushedCellsCount = new AtomicLong(); 215 private AtomicLong compactedCellsCount = new AtomicLong(); 216 private AtomicLong majorCompactedCellsCount = new AtomicLong(); 217 private AtomicLong flushedCellsSize = new AtomicLong(); 218 private AtomicLong flushedOutputFileSize = new AtomicLong(); 219 private AtomicLong compactedCellsSize = new AtomicLong(); 220 private AtomicLong majorCompactedCellsSize = new AtomicLong(); 221 222 /** 223 * Constructor 224 * @param region 225 * @param family HColumnDescriptor for this column 226 * @param confParam configuration object 227 * failed. Can be null. 228 * @throws IOException 229 */ 230 protected HStore(final HRegion region, final ColumnFamilyDescriptor family, 231 final Configuration confParam) throws IOException { 232 233 this.fs = region.getRegionFileSystem(); 234 235 // Assemble the store's home directory and Ensure it exists. 236 fs.createStoreDir(family.getNameAsString()); 237 this.region = region; 238 this.family = family; 239 // 'conf' renamed to 'confParam' b/c we use this.conf in the constructor 240 // CompoundConfiguration will look for keys in reverse order of addition, so we'd 241 // add global config first, then table and cf overrides, then cf metadata. 242 this.conf = new CompoundConfiguration() 243 .add(confParam) 244 .addBytesMap(region.getTableDescriptor().getValues()) 245 .addStringMap(family.getConfiguration()) 246 .addBytesMap(family.getValues()); 247 this.blocksize = family.getBlocksize(); 248 249 // set block storage policy for store directory 250 String policyName = family.getStoragePolicy(); 251 if (null == policyName) { 252 policyName = this.conf.get(BLOCK_STORAGE_POLICY_KEY, DEFAULT_BLOCK_STORAGE_POLICY); 253 } 254 this.fs.setStoragePolicy(family.getNameAsString(), policyName.trim()); 255 256 this.dataBlockEncoder = new HFileDataBlockEncoderImpl(family.getDataBlockEncoding()); 257 258 this.comparator = region.getCellComparator(); 259 // used by ScanQueryMatcher 260 long timeToPurgeDeletes = 261 Math.max(conf.getLong("hbase.hstore.time.to.purge.deletes", 0), 0); 262 LOG.trace("Time to purge deletes set to {}ms in store {}", timeToPurgeDeletes, this); 263 // Get TTL 264 long ttl = determineTTLFromFamily(family); 265 // Why not just pass a HColumnDescriptor in here altogether? Even if have 266 // to clone it? 267 scanInfo = new ScanInfo(conf, family, ttl, timeToPurgeDeletes, this.comparator); 268 this.memstore = getMemstore(); 269 this.offPeakHours = OffPeakHours.getInstance(conf); 270 271 // Setting up cache configuration for this family 272 createCacheConf(family); 273 274 this.verifyBulkLoads = conf.getBoolean("hbase.hstore.bulkload.verify", false); 275 276 this.blockingFileCount = 277 conf.getInt(BLOCKING_STOREFILES_KEY, DEFAULT_BLOCKING_STOREFILE_COUNT); 278 this.compactionCheckMultiplier = conf.getInt( 279 COMPACTCHECKER_INTERVAL_MULTIPLIER_KEY, DEFAULT_COMPACTCHECKER_INTERVAL_MULTIPLIER); 280 if (this.compactionCheckMultiplier <= 0) { 281 LOG.error("Compaction check period multiplier must be positive, setting default: {}", 282 DEFAULT_COMPACTCHECKER_INTERVAL_MULTIPLIER); 283 this.compactionCheckMultiplier = DEFAULT_COMPACTCHECKER_INTERVAL_MULTIPLIER; 284 } 285 286 if (HStore.closeCheckInterval == 0) { 287 HStore.closeCheckInterval = conf.getInt( 288 "hbase.hstore.close.check.interval", 10*1000*1000 /* 10 MB */); 289 } 290 291 this.storeEngine = createStoreEngine(this, this.conf, this.comparator); 292 this.storeEngine.getStoreFileManager().loadFiles(loadStoreFiles()); 293 294 // Initialize checksum type from name. The names are CRC32, CRC32C, etc. 295 this.checksumType = getChecksumType(conf); 296 // Initialize bytes per checksum 297 this.bytesPerChecksum = getBytesPerChecksum(conf); 298 flushRetriesNumber = conf.getInt( 299 "hbase.hstore.flush.retries.number", DEFAULT_FLUSH_RETRIES_NUMBER); 300 pauseTime = conf.getInt(HConstants.HBASE_SERVER_PAUSE, HConstants.DEFAULT_HBASE_SERVER_PAUSE); 301 if (flushRetriesNumber <= 0) { 302 throw new IllegalArgumentException( 303 "hbase.hstore.flush.retries.number must be > 0, not " 304 + flushRetriesNumber); 305 } 306 cryptoContext = EncryptionUtil.createEncryptionContext(conf, family); 307 308 LOG.info("Store={}, memstore type={}, storagePolicy={}, verifyBulkLoads={}", 309 getColumnFamilyName(), this.memstore.getClass().getSimpleName(), policyName, 310 this.verifyBulkLoads); 311 } 312 313 /** 314 * @return MemStore Instance to use in this store. 315 */ 316 private MemStore getMemstore() { 317 MemStore ms = null; 318 // Check if in-memory-compaction configured. Note MemoryCompactionPolicy is an enum! 319 MemoryCompactionPolicy inMemoryCompaction = null; 320 if (this.getTableName().isSystemTable()) { 321 inMemoryCompaction = MemoryCompactionPolicy.valueOf( 322 conf.get("hbase.systemtables.compacting.memstore.type", "NONE")); 323 } else { 324 inMemoryCompaction = family.getInMemoryCompaction(); 325 } 326 if (inMemoryCompaction == null) { 327 inMemoryCompaction = 328 MemoryCompactionPolicy.valueOf(conf.get(CompactingMemStore.COMPACTING_MEMSTORE_TYPE_KEY, 329 CompactingMemStore.COMPACTING_MEMSTORE_TYPE_DEFAULT)); 330 } 331 switch (inMemoryCompaction) { 332 case NONE: 333 ms = ReflectionUtils.newInstance(DefaultMemStore.class, 334 new Object[] { conf, this.comparator, 335 this.getHRegion().getRegionServicesForStores()}); 336 break; 337 default: 338 Class<? extends CompactingMemStore> clz = conf.getClass(MEMSTORE_CLASS_NAME, 339 CompactingMemStore.class, CompactingMemStore.class); 340 ms = ReflectionUtils.newInstance(clz, new Object[]{conf, this.comparator, this, 341 this.getHRegion().getRegionServicesForStores(), inMemoryCompaction}); 342 } 343 return ms; 344 } 345 346 /** 347 * Creates the cache config. 348 * @param family The current column family. 349 */ 350 protected void createCacheConf(final ColumnFamilyDescriptor family) { 351 this.cacheConf = new CacheConfig(conf, family); 352 } 353 354 /** 355 * Creates the store engine configured for the given Store. 356 * @param store The store. An unfortunate dependency needed due to it 357 * being passed to coprocessors via the compactor. 358 * @param conf Store configuration. 359 * @param kvComparator KVComparator for storeFileManager. 360 * @return StoreEngine to use. 361 */ 362 protected StoreEngine<?, ?, ?, ?> createStoreEngine(HStore store, Configuration conf, 363 CellComparator kvComparator) throws IOException { 364 return StoreEngine.create(store, conf, comparator); 365 } 366 367 /** 368 * @param family 369 * @return TTL in seconds of the specified family 370 */ 371 public static long determineTTLFromFamily(final ColumnFamilyDescriptor family) { 372 // HCD.getTimeToLive returns ttl in seconds. Convert to milliseconds. 373 long ttl = family.getTimeToLive(); 374 if (ttl == HConstants.FOREVER) { 375 // Default is unlimited ttl. 376 ttl = Long.MAX_VALUE; 377 } else if (ttl == -1) { 378 ttl = Long.MAX_VALUE; 379 } else { 380 // Second -> ms adjust for user data 381 ttl *= 1000; 382 } 383 return ttl; 384 } 385 386 @Override 387 public String getColumnFamilyName() { 388 return this.family.getNameAsString(); 389 } 390 391 @Override 392 public TableName getTableName() { 393 return this.getRegionInfo().getTable(); 394 } 395 396 @Override 397 public FileSystem getFileSystem() { 398 return this.fs.getFileSystem(); 399 } 400 401 public HRegionFileSystem getRegionFileSystem() { 402 return this.fs; 403 } 404 405 /* Implementation of StoreConfigInformation */ 406 @Override 407 public long getStoreFileTtl() { 408 // TTL only applies if there's no MIN_VERSIONs setting on the column. 409 return (this.scanInfo.getMinVersions() == 0) ? this.scanInfo.getTtl() : Long.MAX_VALUE; 410 } 411 412 @Override 413 public long getMemStoreFlushSize() { 414 // TODO: Why is this in here? The flushsize of the region rather than the store? St.Ack 415 return this.region.memstoreFlushSize; 416 } 417 418 @Override 419 public MemStoreSize getFlushableSize() { 420 return this.memstore.getFlushableSize(); 421 } 422 423 @Override 424 public MemStoreSize getSnapshotSize() { 425 return this.memstore.getSnapshotSize(); 426 } 427 428 @Override 429 public long getCompactionCheckMultiplier() { 430 return this.compactionCheckMultiplier; 431 } 432 433 @Override 434 public long getBlockingFileCount() { 435 return blockingFileCount; 436 } 437 /* End implementation of StoreConfigInformation */ 438 439 /** 440 * Returns the configured bytesPerChecksum value. 441 * @param conf The configuration 442 * @return The bytesPerChecksum that is set in the configuration 443 */ 444 public static int getBytesPerChecksum(Configuration conf) { 445 return conf.getInt(HConstants.BYTES_PER_CHECKSUM, 446 HFile.DEFAULT_BYTES_PER_CHECKSUM); 447 } 448 449 /** 450 * Returns the configured checksum algorithm. 451 * @param conf The configuration 452 * @return The checksum algorithm that is set in the configuration 453 */ 454 public static ChecksumType getChecksumType(Configuration conf) { 455 String checksumName = conf.get(HConstants.CHECKSUM_TYPE_NAME); 456 if (checksumName == null) { 457 return ChecksumType.getDefaultChecksumType(); 458 } else { 459 return ChecksumType.nameToType(checksumName); 460 } 461 } 462 463 /** 464 * @return how many bytes to write between status checks 465 */ 466 public static int getCloseCheckInterval() { 467 return closeCheckInterval; 468 } 469 470 @Override 471 public ColumnFamilyDescriptor getColumnFamilyDescriptor() { 472 return this.family; 473 } 474 475 @Override 476 public OptionalLong getMaxSequenceId() { 477 return StoreUtils.getMaxSequenceIdInList(this.getStorefiles()); 478 } 479 480 @Override 481 public OptionalLong getMaxMemStoreTS() { 482 return StoreUtils.getMaxMemStoreTSInList(this.getStorefiles()); 483 } 484 485 /** 486 * @param tabledir {@link Path} to where the table is being stored 487 * @param hri {@link RegionInfo} for the region. 488 * @param family {@link ColumnFamilyDescriptor} describing the column family 489 * @return Path to family/Store home directory. 490 */ 491 @Deprecated 492 public static Path getStoreHomedir(final Path tabledir, 493 final RegionInfo hri, final byte[] family) { 494 return getStoreHomedir(tabledir, hri.getEncodedName(), family); 495 } 496 497 /** 498 * @param tabledir {@link Path} to where the table is being stored 499 * @param encodedName Encoded region name. 500 * @param family {@link ColumnFamilyDescriptor} describing the column family 501 * @return Path to family/Store home directory. 502 */ 503 @Deprecated 504 public static Path getStoreHomedir(final Path tabledir, 505 final String encodedName, final byte[] family) { 506 return new Path(tabledir, new Path(encodedName, Bytes.toString(family))); 507 } 508 509 /** 510 * @return the data block encoder 511 */ 512 public HFileDataBlockEncoder getDataBlockEncoder() { 513 return dataBlockEncoder; 514 } 515 516 /** 517 * Should be used only in tests. 518 * @param blockEncoder the block delta encoder to use 519 */ 520 void setDataBlockEncoderInTest(HFileDataBlockEncoder blockEncoder) { 521 this.dataBlockEncoder = blockEncoder; 522 } 523 524 /** 525 * Creates an unsorted list of StoreFile loaded in parallel 526 * from the given directory. 527 * @throws IOException 528 */ 529 private List<HStoreFile> loadStoreFiles() throws IOException { 530 Collection<StoreFileInfo> files = fs.getStoreFiles(getColumnFamilyName()); 531 return openStoreFiles(files); 532 } 533 534 private List<HStoreFile> openStoreFiles(Collection<StoreFileInfo> files) throws IOException { 535 if (CollectionUtils.isEmpty(files)) { 536 return Collections.emptyList(); 537 } 538 // initialize the thread pool for opening store files in parallel.. 539 ThreadPoolExecutor storeFileOpenerThreadPool = 540 this.region.getStoreFileOpenAndCloseThreadPool("StoreFileOpenerThread-" + 541 this.getColumnFamilyName()); 542 CompletionService<HStoreFile> completionService = new ExecutorCompletionService<>(storeFileOpenerThreadPool); 543 544 int totalValidStoreFile = 0; 545 for (StoreFileInfo storeFileInfo : files) { 546 // open each store file in parallel 547 completionService.submit(() -> this.createStoreFileAndReader(storeFileInfo)); 548 totalValidStoreFile++; 549 } 550 551 ArrayList<HStoreFile> results = new ArrayList<>(files.size()); 552 IOException ioe = null; 553 try { 554 for (int i = 0; i < totalValidStoreFile; i++) { 555 try { 556 HStoreFile storeFile = completionService.take().get(); 557 if (storeFile != null) { 558 long length = storeFile.getReader().length(); 559 this.storeSize.addAndGet(length); 560 this.totalUncompressedBytes 561 .addAndGet(storeFile.getReader().getTotalUncompressedBytes()); 562 LOG.debug("loaded {}", storeFile); 563 results.add(storeFile); 564 } 565 } catch (InterruptedException e) { 566 if (ioe == null) ioe = new InterruptedIOException(e.getMessage()); 567 } catch (ExecutionException e) { 568 if (ioe == null) ioe = new IOException(e.getCause()); 569 } 570 } 571 } finally { 572 storeFileOpenerThreadPool.shutdownNow(); 573 } 574 if (ioe != null) { 575 // close StoreFile readers 576 boolean evictOnClose = 577 cacheConf != null? cacheConf.shouldEvictOnClose(): true; 578 for (HStoreFile file : results) { 579 try { 580 if (file != null) { 581 file.closeStoreFile(evictOnClose); 582 } 583 } catch (IOException e) { 584 LOG.warn("Could not close store file", e); 585 } 586 } 587 throw ioe; 588 } 589 590 return results; 591 } 592 593 @Override 594 public void refreshStoreFiles() throws IOException { 595 Collection<StoreFileInfo> newFiles = fs.getStoreFiles(getColumnFamilyName()); 596 refreshStoreFilesInternal(newFiles); 597 } 598 599 /** 600 * Replaces the store files that the store has with the given files. Mainly used by secondary 601 * region replicas to keep up to date with the primary region files. 602 * @throws IOException 603 */ 604 public void refreshStoreFiles(Collection<String> newFiles) throws IOException { 605 List<StoreFileInfo> storeFiles = new ArrayList<>(newFiles.size()); 606 for (String file : newFiles) { 607 storeFiles.add(fs.getStoreFileInfo(getColumnFamilyName(), file)); 608 } 609 refreshStoreFilesInternal(storeFiles); 610 } 611 612 /** 613 * Checks the underlying store files, and opens the files that have not 614 * been opened, and removes the store file readers for store files no longer 615 * available. Mainly used by secondary region replicas to keep up to date with 616 * the primary region files. 617 * @throws IOException 618 */ 619 private void refreshStoreFilesInternal(Collection<StoreFileInfo> newFiles) throws IOException { 620 StoreFileManager sfm = storeEngine.getStoreFileManager(); 621 Collection<HStoreFile> currentFiles = sfm.getStorefiles(); 622 Collection<HStoreFile> compactedFiles = sfm.getCompactedfiles(); 623 if (currentFiles == null) currentFiles = Collections.emptySet(); 624 if (newFiles == null) newFiles = Collections.emptySet(); 625 if (compactedFiles == null) compactedFiles = Collections.emptySet(); 626 627 HashMap<StoreFileInfo, HStoreFile> currentFilesSet = new HashMap<>(currentFiles.size()); 628 for (HStoreFile sf : currentFiles) { 629 currentFilesSet.put(sf.getFileInfo(), sf); 630 } 631 HashMap<StoreFileInfo, HStoreFile> compactedFilesSet = new HashMap<>(compactedFiles.size()); 632 for (HStoreFile sf : compactedFiles) { 633 compactedFilesSet.put(sf.getFileInfo(), sf); 634 } 635 636 Set<StoreFileInfo> newFilesSet = new HashSet<StoreFileInfo>(newFiles); 637 // Exclude the files that have already been compacted 638 newFilesSet = Sets.difference(newFilesSet, compactedFilesSet.keySet()); 639 Set<StoreFileInfo> toBeAddedFiles = Sets.difference(newFilesSet, currentFilesSet.keySet()); 640 Set<StoreFileInfo> toBeRemovedFiles = Sets.difference(currentFilesSet.keySet(), newFilesSet); 641 642 if (toBeAddedFiles.isEmpty() && toBeRemovedFiles.isEmpty()) { 643 return; 644 } 645 646 LOG.info("Refreshing store files for region " + this.getRegionInfo().getRegionNameAsString() 647 + " files to add: " + toBeAddedFiles + " files to remove: " + toBeRemovedFiles); 648 649 Set<HStoreFile> toBeRemovedStoreFiles = new HashSet<>(toBeRemovedFiles.size()); 650 for (StoreFileInfo sfi : toBeRemovedFiles) { 651 toBeRemovedStoreFiles.add(currentFilesSet.get(sfi)); 652 } 653 654 // try to open the files 655 List<HStoreFile> openedFiles = openStoreFiles(toBeAddedFiles); 656 657 // propogate the file changes to the underlying store file manager 658 replaceStoreFiles(toBeRemovedStoreFiles, openedFiles); //won't throw an exception 659 660 // Advance the memstore read point to be at least the new store files seqIds so that 661 // readers might pick it up. This assumes that the store is not getting any writes (otherwise 662 // in-flight transactions might be made visible) 663 if (!toBeAddedFiles.isEmpty()) { 664 // we must have the max sequence id here as we do have several store files 665 region.getMVCC().advanceTo(this.getMaxSequenceId().getAsLong()); 666 } 667 668 completeCompaction(toBeRemovedStoreFiles); 669 } 670 671 @VisibleForTesting 672 protected HStoreFile createStoreFileAndReader(final Path p) throws IOException { 673 StoreFileInfo info = new StoreFileInfo(conf, this.getFileSystem(), p); 674 return createStoreFileAndReader(info); 675 } 676 677 private HStoreFile createStoreFileAndReader(StoreFileInfo info) throws IOException { 678 info.setRegionCoprocessorHost(this.region.getCoprocessorHost()); 679 HStoreFile storeFile = new HStoreFile(this.getFileSystem(), info, this.conf, this.cacheConf, 680 this.family.getBloomFilterType(), isPrimaryReplicaStore()); 681 storeFile.initReader(); 682 return storeFile; 683 } 684 685 /** 686 * This message intends to inform the MemStore that next coming updates 687 * are going to be part of the replaying edits from WAL 688 */ 689 public void startReplayingFromWAL(){ 690 this.memstore.startReplayingFromWAL(); 691 } 692 693 /** 694 * This message intends to inform the MemStore that the replaying edits from WAL 695 * are done 696 */ 697 public void stopReplayingFromWAL(){ 698 this.memstore.stopReplayingFromWAL(); 699 } 700 701 /** 702 * Adds a value to the memstore 703 */ 704 public void add(final Cell cell, MemStoreSizing memstoreSizing) { 705 lock.readLock().lock(); 706 try { 707 this.memstore.add(cell, memstoreSizing); 708 } finally { 709 lock.readLock().unlock(); 710 } 711 } 712 713 /** 714 * Adds the specified value to the memstore 715 */ 716 public void add(final Iterable<Cell> cells, MemStoreSizing memstoreSizing) { 717 lock.readLock().lock(); 718 try { 719 memstore.add(cells, memstoreSizing); 720 } finally { 721 lock.readLock().unlock(); 722 } 723 } 724 725 @Override 726 public long timeOfOldestEdit() { 727 return memstore.timeOfOldestEdit(); 728 } 729 730 /** 731 * @return All store files. 732 */ 733 @Override 734 public Collection<HStoreFile> getStorefiles() { 735 return this.storeEngine.getStoreFileManager().getStorefiles(); 736 } 737 738 @Override 739 public Collection<HStoreFile> getCompactedFiles() { 740 return this.storeEngine.getStoreFileManager().getCompactedfiles(); 741 } 742 743 /** 744 * This throws a WrongRegionException if the HFile does not fit in this region, or an 745 * InvalidHFileException if the HFile is not valid. 746 */ 747 public void assertBulkLoadHFileOk(Path srcPath) throws IOException { 748 HFile.Reader reader = null; 749 try { 750 LOG.info("Validating hfile at " + srcPath + " for inclusion in " 751 + "store " + this + " region " + this.getRegionInfo().getRegionNameAsString()); 752 reader = HFile.createReader(srcPath.getFileSystem(conf), srcPath, cacheConf, 753 isPrimaryReplicaStore(), conf); 754 reader.loadFileInfo(); 755 756 Optional<byte[]> firstKey = reader.getFirstRowKey(); 757 Preconditions.checkState(firstKey.isPresent(), "First key can not be null"); 758 Optional<Cell> lk = reader.getLastKey(); 759 Preconditions.checkState(lk.isPresent(), "Last key can not be null"); 760 byte[] lastKey = CellUtil.cloneRow(lk.get()); 761 762 if (LOG.isDebugEnabled()) { 763 LOG.debug("HFile bounds: first=" + Bytes.toStringBinary(firstKey.get()) + 764 " last=" + Bytes.toStringBinary(lastKey)); 765 LOG.debug("Region bounds: first=" + 766 Bytes.toStringBinary(getRegionInfo().getStartKey()) + 767 " last=" + Bytes.toStringBinary(getRegionInfo().getEndKey())); 768 } 769 770 if (!this.getRegionInfo().containsRange(firstKey.get(), lastKey)) { 771 throw new WrongRegionException( 772 "Bulk load file " + srcPath.toString() + " does not fit inside region " 773 + this.getRegionInfo().getRegionNameAsString()); 774 } 775 776 if(reader.length() > conf.getLong(HConstants.HREGION_MAX_FILESIZE, 777 HConstants.DEFAULT_MAX_FILE_SIZE)) { 778 LOG.warn("Trying to bulk load hfile " + srcPath + " with size: " + 779 reader.length() + " bytes can be problematic as it may lead to oversplitting."); 780 } 781 782 if (verifyBulkLoads) { 783 long verificationStartTime = EnvironmentEdgeManager.currentTime(); 784 LOG.info("Full verification started for bulk load hfile: {}", srcPath); 785 Cell prevCell = null; 786 HFileScanner scanner = reader.getScanner(false, false, false); 787 scanner.seekTo(); 788 do { 789 Cell cell = scanner.getCell(); 790 if (prevCell != null) { 791 if (comparator.compareRows(prevCell, cell) > 0) { 792 throw new InvalidHFileException("Previous row is greater than" 793 + " current row: path=" + srcPath + " previous=" 794 + CellUtil.getCellKeyAsString(prevCell) + " current=" 795 + CellUtil.getCellKeyAsString(cell)); 796 } 797 if (CellComparator.getInstance().compareFamilies(prevCell, cell) != 0) { 798 throw new InvalidHFileException("Previous key had different" 799 + " family compared to current key: path=" + srcPath 800 + " previous=" 801 + Bytes.toStringBinary(prevCell.getFamilyArray(), prevCell.getFamilyOffset(), 802 prevCell.getFamilyLength()) 803 + " current=" 804 + Bytes.toStringBinary(cell.getFamilyArray(), cell.getFamilyOffset(), 805 cell.getFamilyLength())); 806 } 807 } 808 prevCell = cell; 809 } while (scanner.next()); 810 LOG.info("Full verification complete for bulk load hfile: " + srcPath.toString() 811 + " took " + (EnvironmentEdgeManager.currentTime() - verificationStartTime) 812 + " ms"); 813 } 814 } finally { 815 if (reader != null) reader.close(); 816 } 817 } 818 819 /** 820 * This method should only be called from Region. It is assumed that the ranges of values in the 821 * HFile fit within the stores assigned region. (assertBulkLoadHFileOk checks this) 822 * 823 * @param srcPathStr 824 * @param seqNum sequence Id associated with the HFile 825 */ 826 public Pair<Path, Path> preBulkLoadHFile(String srcPathStr, long seqNum) throws IOException { 827 Path srcPath = new Path(srcPathStr); 828 return fs.bulkLoadStoreFile(getColumnFamilyName(), srcPath, seqNum); 829 } 830 831 public Path bulkLoadHFile(byte[] family, String srcPathStr, Path dstPath) throws IOException { 832 Path srcPath = new Path(srcPathStr); 833 try { 834 fs.commitStoreFile(srcPath, dstPath); 835 } finally { 836 if (this.getCoprocessorHost() != null) { 837 this.getCoprocessorHost().postCommitStoreFile(family, srcPath, dstPath); 838 } 839 } 840 841 LOG.info("Loaded HFile " + srcPath + " into store '" + getColumnFamilyName() + "' as " 842 + dstPath + " - updating store file list."); 843 844 HStoreFile sf = createStoreFileAndReader(dstPath); 845 bulkLoadHFile(sf); 846 847 LOG.info("Successfully loaded store file {} into store {} (new location: {})", 848 srcPath, this, dstPath); 849 850 return dstPath; 851 } 852 853 public void bulkLoadHFile(StoreFileInfo fileInfo) throws IOException { 854 HStoreFile sf = createStoreFileAndReader(fileInfo); 855 bulkLoadHFile(sf); 856 } 857 858 private void bulkLoadHFile(HStoreFile sf) throws IOException { 859 StoreFileReader r = sf.getReader(); 860 this.storeSize.addAndGet(r.length()); 861 this.totalUncompressedBytes.addAndGet(r.getTotalUncompressedBytes()); 862 863 // Append the new storefile into the list 864 this.lock.writeLock().lock(); 865 try { 866 this.storeEngine.getStoreFileManager().insertNewFiles(Lists.newArrayList(sf)); 867 } finally { 868 // We need the lock, as long as we are updating the storeFiles 869 // or changing the memstore. Let us release it before calling 870 // notifyChangeReadersObservers. See HBASE-4485 for a possible 871 // deadlock scenario that could have happened if continue to hold 872 // the lock. 873 this.lock.writeLock().unlock(); 874 } 875 LOG.info("Loaded HFile " + sf.getFileInfo() + " into store '" + getColumnFamilyName()); 876 if (LOG.isTraceEnabled()) { 877 String traceMessage = "BULK LOAD time,size,store size,store files [" 878 + EnvironmentEdgeManager.currentTime() + "," + r.length() + "," + storeSize 879 + "," + storeEngine.getStoreFileManager().getStorefileCount() + "]"; 880 LOG.trace(traceMessage); 881 } 882 } 883 884 /** 885 * Close all the readers We don't need to worry about subsequent requests because the Region holds 886 * a write lock that will prevent any more reads or writes. 887 * @return the {@link StoreFile StoreFiles} that were previously being used. 888 * @throws IOException on failure 889 */ 890 public ImmutableCollection<HStoreFile> close() throws IOException { 891 this.archiveLock.lock(); 892 this.lock.writeLock().lock(); 893 try { 894 // Clear so metrics doesn't find them. 895 ImmutableCollection<HStoreFile> result = storeEngine.getStoreFileManager().clearFiles(); 896 Collection<HStoreFile> compactedfiles = 897 storeEngine.getStoreFileManager().clearCompactedFiles(); 898 // clear the compacted files 899 if (CollectionUtils.isNotEmpty(compactedfiles)) { 900 removeCompactedfiles(compactedfiles); 901 } 902 if (!result.isEmpty()) { 903 // initialize the thread pool for closing store files in parallel. 904 ThreadPoolExecutor storeFileCloserThreadPool = this.region 905 .getStoreFileOpenAndCloseThreadPool("StoreFileCloserThread-" 906 + this.getColumnFamilyName()); 907 908 // close each store file in parallel 909 CompletionService<Void> completionService = 910 new ExecutorCompletionService<>(storeFileCloserThreadPool); 911 for (HStoreFile f : result) { 912 completionService.submit(new Callable<Void>() { 913 @Override 914 public Void call() throws IOException { 915 boolean evictOnClose = 916 cacheConf != null? cacheConf.shouldEvictOnClose(): true; 917 f.closeStoreFile(evictOnClose); 918 return null; 919 } 920 }); 921 } 922 923 IOException ioe = null; 924 try { 925 for (int i = 0; i < result.size(); i++) { 926 try { 927 Future<Void> future = completionService.take(); 928 future.get(); 929 } catch (InterruptedException e) { 930 if (ioe == null) { 931 ioe = new InterruptedIOException(); 932 ioe.initCause(e); 933 } 934 } catch (ExecutionException e) { 935 if (ioe == null) ioe = new IOException(e.getCause()); 936 } 937 } 938 } finally { 939 storeFileCloserThreadPool.shutdownNow(); 940 } 941 if (ioe != null) throw ioe; 942 } 943 LOG.trace("Closed {}", this); 944 return result; 945 } finally { 946 this.lock.writeLock().unlock(); 947 this.archiveLock.unlock(); 948 } 949 } 950 951 /** 952 * Snapshot this stores memstore. Call before running 953 * {@link #flushCache(long, MemStoreSnapshot, MonitoredTask, ThroughputController, 954 * FlushLifeCycleTracker)} 955 * so it has some work to do. 956 */ 957 void snapshot() { 958 this.lock.writeLock().lock(); 959 try { 960 this.memstore.snapshot(); 961 } finally { 962 this.lock.writeLock().unlock(); 963 } 964 } 965 966 /** 967 * Write out current snapshot. Presumes {@link #snapshot()} has been called previously. 968 * @param logCacheFlushId flush sequence number 969 * @param snapshot 970 * @param status 971 * @param throughputController 972 * @return The path name of the tmp file to which the store was flushed 973 * @throws IOException if exception occurs during process 974 */ 975 protected List<Path> flushCache(final long logCacheFlushId, MemStoreSnapshot snapshot, 976 MonitoredTask status, ThroughputController throughputController, 977 FlushLifeCycleTracker tracker) throws IOException { 978 // If an exception happens flushing, we let it out without clearing 979 // the memstore snapshot. The old snapshot will be returned when we say 980 // 'snapshot', the next time flush comes around. 981 // Retry after catching exception when flushing, otherwise server will abort 982 // itself 983 StoreFlusher flusher = storeEngine.getStoreFlusher(); 984 IOException lastException = null; 985 for (int i = 0; i < flushRetriesNumber; i++) { 986 try { 987 List<Path> pathNames = 988 flusher.flushSnapshot(snapshot, logCacheFlushId, status, throughputController, tracker); 989 Path lastPathName = null; 990 try { 991 for (Path pathName : pathNames) { 992 lastPathName = pathName; 993 validateStoreFile(pathName); 994 } 995 return pathNames; 996 } catch (Exception e) { 997 LOG.warn("Failed validating store file {}, retrying num={}", lastPathName, i, e); 998 if (e instanceof IOException) { 999 lastException = (IOException) e; 1000 } else { 1001 lastException = new IOException(e); 1002 } 1003 } 1004 } catch (IOException e) { 1005 LOG.warn("Failed flushing store file, retrying num={}", i, e); 1006 lastException = e; 1007 } 1008 if (lastException != null && i < (flushRetriesNumber - 1)) { 1009 try { 1010 Thread.sleep(pauseTime); 1011 } catch (InterruptedException e) { 1012 IOException iie = new InterruptedIOException(); 1013 iie.initCause(e); 1014 throw iie; 1015 } 1016 } 1017 } 1018 throw lastException; 1019 } 1020 1021 /** 1022 * @param path The pathname of the tmp file into which the store was flushed 1023 * @param logCacheFlushId 1024 * @param status 1025 * @return store file created. 1026 * @throws IOException 1027 */ 1028 private HStoreFile commitFile(Path path, long logCacheFlushId, MonitoredTask status) 1029 throws IOException { 1030 // Write-out finished successfully, move into the right spot 1031 Path dstPath = fs.commitStoreFile(getColumnFamilyName(), path); 1032 1033 status.setStatus("Flushing " + this + ": reopening flushed file"); 1034 HStoreFile sf = createStoreFileAndReader(dstPath); 1035 1036 StoreFileReader r = sf.getReader(); 1037 this.storeSize.addAndGet(r.length()); 1038 this.totalUncompressedBytes.addAndGet(r.getTotalUncompressedBytes()); 1039 1040 if (LOG.isInfoEnabled()) { 1041 LOG.info("Added " + sf + ", entries=" + r.getEntries() + 1042 ", sequenceid=" + logCacheFlushId + 1043 ", filesize=" + TraditionalBinaryPrefix.long2String(r.length(), "", 1)); 1044 } 1045 return sf; 1046 } 1047 1048 /** 1049 * @param maxKeyCount 1050 * @param compression Compression algorithm to use 1051 * @param isCompaction whether we are creating a new file in a compaction 1052 * @param includeMVCCReadpoint - whether to include MVCC or not 1053 * @param includesTag - includesTag or not 1054 * @return Writer for a new StoreFile in the tmp dir. 1055 */ 1056 // TODO : allow the Writer factory to create Writers of ShipperListener type only in case of 1057 // compaction 1058 public StoreFileWriter createWriterInTmp(long maxKeyCount, Compression.Algorithm compression, 1059 boolean isCompaction, boolean includeMVCCReadpoint, boolean includesTag, 1060 boolean shouldDropBehind) throws IOException { 1061 final CacheConfig writerCacheConf; 1062 if (isCompaction) { 1063 // Don't cache data on write on compactions. 1064 writerCacheConf = new CacheConfig(cacheConf); 1065 writerCacheConf.setCacheDataOnWrite(false); 1066 } else { 1067 writerCacheConf = cacheConf; 1068 } 1069 InetSocketAddress[] favoredNodes = null; 1070 if (region.getRegionServerServices() != null) { 1071 favoredNodes = region.getRegionServerServices().getFavoredNodesForRegion( 1072 region.getRegionInfo().getEncodedName()); 1073 } 1074 HFileContext hFileContext = createFileContext(compression, includeMVCCReadpoint, includesTag, 1075 cryptoContext); 1076 Path familyTempDir = new Path(fs.getTempDir(), family.getNameAsString()); 1077 StoreFileWriter.Builder builder = new StoreFileWriter.Builder(conf, writerCacheConf, 1078 this.getFileSystem()) 1079 .withOutputDir(familyTempDir) 1080 .withComparator(comparator) 1081 .withBloomType(family.getBloomFilterType()) 1082 .withMaxKeyCount(maxKeyCount) 1083 .withFavoredNodes(favoredNodes) 1084 .withFileContext(hFileContext) 1085 .withShouldDropCacheBehind(shouldDropBehind); 1086 return builder.build(); 1087 } 1088 1089 private HFileContext createFileContext(Compression.Algorithm compression, 1090 boolean includeMVCCReadpoint, boolean includesTag, Encryption.Context cryptoContext) { 1091 if (compression == null) { 1092 compression = HFile.DEFAULT_COMPRESSION_ALGORITHM; 1093 } 1094 HFileContext hFileContext = new HFileContextBuilder() 1095 .withIncludesMvcc(includeMVCCReadpoint) 1096 .withIncludesTags(includesTag) 1097 .withCompression(compression) 1098 .withCompressTags(family.isCompressTags()) 1099 .withChecksumType(checksumType) 1100 .withBytesPerCheckSum(bytesPerChecksum) 1101 .withBlockSize(blocksize) 1102 .withHBaseCheckSum(true) 1103 .withDataBlockEncoding(family.getDataBlockEncoding()) 1104 .withEncryptionContext(cryptoContext) 1105 .withCreateTime(EnvironmentEdgeManager.currentTime()) 1106 .build(); 1107 return hFileContext; 1108 } 1109 1110 1111 private long getTotalSize(Collection<HStoreFile> sfs) { 1112 return sfs.stream().mapToLong(sf -> sf.getReader().length()).sum(); 1113 } 1114 1115 /** 1116 * Change storeFiles adding into place the Reader produced by this new flush. 1117 * @param sfs Store files 1118 * @param snapshotId 1119 * @throws IOException 1120 * @return Whether compaction is required. 1121 */ 1122 private boolean updateStorefiles(List<HStoreFile> sfs, long snapshotId) throws IOException { 1123 this.lock.writeLock().lock(); 1124 try { 1125 this.storeEngine.getStoreFileManager().insertNewFiles(sfs); 1126 if (snapshotId > 0) { 1127 this.memstore.clearSnapshot(snapshotId); 1128 } 1129 } finally { 1130 // We need the lock, as long as we are updating the storeFiles 1131 // or changing the memstore. Let us release it before calling 1132 // notifyChangeReadersObservers. See HBASE-4485 for a possible 1133 // deadlock scenario that could have happened if continue to hold 1134 // the lock. 1135 this.lock.writeLock().unlock(); 1136 } 1137 // notify to be called here - only in case of flushes 1138 notifyChangedReadersObservers(sfs); 1139 if (LOG.isTraceEnabled()) { 1140 long totalSize = getTotalSize(sfs); 1141 String traceMessage = "FLUSH time,count,size,store size,store files [" 1142 + EnvironmentEdgeManager.currentTime() + "," + sfs.size() + "," + totalSize 1143 + "," + storeSize + "," + storeEngine.getStoreFileManager().getStorefileCount() + "]"; 1144 LOG.trace(traceMessage); 1145 } 1146 return needsCompaction(); 1147 } 1148 1149 /** 1150 * Notify all observers that set of Readers has changed. 1151 * @throws IOException 1152 */ 1153 private void notifyChangedReadersObservers(List<HStoreFile> sfs) throws IOException { 1154 for (ChangedReadersObserver o : this.changedReaderObservers) { 1155 List<KeyValueScanner> memStoreScanners; 1156 this.lock.readLock().lock(); 1157 try { 1158 memStoreScanners = this.memstore.getScanners(o.getReadPoint()); 1159 } finally { 1160 this.lock.readLock().unlock(); 1161 } 1162 o.updateReaders(sfs, memStoreScanners); 1163 } 1164 } 1165 1166 /** 1167 * Get all scanners with no filtering based on TTL (that happens further down the line). 1168 * @param cacheBlocks cache the blocks or not 1169 * @param usePread true to use pread, false if not 1170 * @param isCompaction true if the scanner is created for compaction 1171 * @param matcher the scan query matcher 1172 * @param startRow the start row 1173 * @param stopRow the stop row 1174 * @param readPt the read point of the current scan 1175 * @return all scanners for this store 1176 */ 1177 public List<KeyValueScanner> getScanners(boolean cacheBlocks, boolean isGet, boolean usePread, 1178 boolean isCompaction, ScanQueryMatcher matcher, byte[] startRow, byte[] stopRow, long readPt) 1179 throws IOException { 1180 return getScanners(cacheBlocks, usePread, isCompaction, matcher, startRow, true, stopRow, false, 1181 readPt); 1182 } 1183 1184 /** 1185 * Get all scanners with no filtering based on TTL (that happens further down the line). 1186 * @param cacheBlocks cache the blocks or not 1187 * @param usePread true to use pread, false if not 1188 * @param isCompaction true if the scanner is created for compaction 1189 * @param matcher the scan query matcher 1190 * @param startRow the start row 1191 * @param includeStartRow true to include start row, false if not 1192 * @param stopRow the stop row 1193 * @param includeStopRow true to include stop row, false if not 1194 * @param readPt the read point of the current scan 1195 * @return all scanners for this store 1196 */ 1197 public List<KeyValueScanner> getScanners(boolean cacheBlocks, boolean usePread, 1198 boolean isCompaction, ScanQueryMatcher matcher, byte[] startRow, boolean includeStartRow, 1199 byte[] stopRow, boolean includeStopRow, long readPt) throws IOException { 1200 Collection<HStoreFile> storeFilesToScan; 1201 List<KeyValueScanner> memStoreScanners; 1202 this.lock.readLock().lock(); 1203 try { 1204 storeFilesToScan = this.storeEngine.getStoreFileManager().getFilesForScan(startRow, 1205 includeStartRow, stopRow, includeStopRow); 1206 memStoreScanners = this.memstore.getScanners(readPt); 1207 } finally { 1208 this.lock.readLock().unlock(); 1209 } 1210 1211 // First the store file scanners 1212 1213 // TODO this used to get the store files in descending order, 1214 // but now we get them in ascending order, which I think is 1215 // actually more correct, since memstore get put at the end. 1216 List<StoreFileScanner> sfScanners = StoreFileScanner.getScannersForStoreFiles(storeFilesToScan, 1217 cacheBlocks, usePread, isCompaction, false, matcher, readPt); 1218 List<KeyValueScanner> scanners = new ArrayList<>(sfScanners.size() + 1); 1219 scanners.addAll(sfScanners); 1220 // Then the memstore scanners 1221 scanners.addAll(memStoreScanners); 1222 return scanners; 1223 } 1224 1225 /** 1226 * Create scanners on the given files and if needed on the memstore with no filtering based on TTL 1227 * (that happens further down the line). 1228 * @param files the list of files on which the scanners has to be created 1229 * @param cacheBlocks cache the blocks or not 1230 * @param usePread true to use pread, false if not 1231 * @param isCompaction true if the scanner is created for compaction 1232 * @param matcher the scan query matcher 1233 * @param startRow the start row 1234 * @param stopRow the stop row 1235 * @param readPt the read point of the current scan 1236 * @param includeMemstoreScanner true if memstore has to be included 1237 * @return scanners on the given files and on the memstore if specified 1238 */ 1239 public List<KeyValueScanner> getScanners(List<HStoreFile> files, boolean cacheBlocks, 1240 boolean isGet, boolean usePread, boolean isCompaction, ScanQueryMatcher matcher, 1241 byte[] startRow, byte[] stopRow, long readPt, boolean includeMemstoreScanner) 1242 throws IOException { 1243 return getScanners(files, cacheBlocks, usePread, isCompaction, matcher, startRow, true, stopRow, 1244 false, readPt, includeMemstoreScanner); 1245 } 1246 1247 /** 1248 * Create scanners on the given files and if needed on the memstore with no filtering based on TTL 1249 * (that happens further down the line). 1250 * @param files the list of files on which the scanners has to be created 1251 * @param cacheBlocks ache the blocks or not 1252 * @param usePread true to use pread, false if not 1253 * @param isCompaction true if the scanner is created for compaction 1254 * @param matcher the scan query matcher 1255 * @param startRow the start row 1256 * @param includeStartRow true to include start row, false if not 1257 * @param stopRow the stop row 1258 * @param includeStopRow true to include stop row, false if not 1259 * @param readPt the read point of the current scan 1260 * @param includeMemstoreScanner true if memstore has to be included 1261 * @return scanners on the given files and on the memstore if specified 1262 */ 1263 public List<KeyValueScanner> getScanners(List<HStoreFile> files, boolean cacheBlocks, 1264 boolean usePread, boolean isCompaction, ScanQueryMatcher matcher, byte[] startRow, 1265 boolean includeStartRow, byte[] stopRow, boolean includeStopRow, long readPt, 1266 boolean includeMemstoreScanner) throws IOException { 1267 List<KeyValueScanner> memStoreScanners = null; 1268 if (includeMemstoreScanner) { 1269 this.lock.readLock().lock(); 1270 try { 1271 memStoreScanners = this.memstore.getScanners(readPt); 1272 } finally { 1273 this.lock.readLock().unlock(); 1274 } 1275 } 1276 List<StoreFileScanner> sfScanners = StoreFileScanner.getScannersForStoreFiles(files, 1277 cacheBlocks, usePread, isCompaction, false, matcher, readPt); 1278 List<KeyValueScanner> scanners = new ArrayList<>(sfScanners.size() + 1); 1279 scanners.addAll(sfScanners); 1280 // Then the memstore scanners 1281 if (memStoreScanners != null) { 1282 scanners.addAll(memStoreScanners); 1283 } 1284 return scanners; 1285 } 1286 1287 /** 1288 * @param o Observer who wants to know about changes in set of Readers 1289 */ 1290 public void addChangedReaderObserver(ChangedReadersObserver o) { 1291 this.changedReaderObservers.add(o); 1292 } 1293 1294 /** 1295 * @param o Observer no longer interested in changes in set of Readers. 1296 */ 1297 public void deleteChangedReaderObserver(ChangedReadersObserver o) { 1298 // We don't check if observer present; it may not be (legitimately) 1299 this.changedReaderObservers.remove(o); 1300 } 1301 1302 ////////////////////////////////////////////////////////////////////////////// 1303 // Compaction 1304 ////////////////////////////////////////////////////////////////////////////// 1305 1306 /** 1307 * Compact the StoreFiles. This method may take some time, so the calling 1308 * thread must be able to block for long periods. 1309 * 1310 * <p>During this time, the Store can work as usual, getting values from 1311 * StoreFiles and writing new StoreFiles from the memstore. 1312 * 1313 * Existing StoreFiles are not destroyed until the new compacted StoreFile is 1314 * completely written-out to disk. 1315 * 1316 * <p>The compactLock prevents multiple simultaneous compactions. 1317 * The structureLock prevents us from interfering with other write operations. 1318 * 1319 * <p>We don't want to hold the structureLock for the whole time, as a compact() 1320 * can be lengthy and we want to allow cache-flushes during this period. 1321 * 1322 * <p> Compaction event should be idempotent, since there is no IO Fencing for 1323 * the region directory in hdfs. A region server might still try to complete the 1324 * compaction after it lost the region. That is why the following events are carefully 1325 * ordered for a compaction: 1326 * 1. Compaction writes new files under region/.tmp directory (compaction output) 1327 * 2. Compaction atomically moves the temporary file under region directory 1328 * 3. Compaction appends a WAL edit containing the compaction input and output files. 1329 * Forces sync on WAL. 1330 * 4. Compaction deletes the input files from the region directory. 1331 * 1332 * Failure conditions are handled like this: 1333 * - If RS fails before 2, compaction wont complete. Even if RS lives on and finishes 1334 * the compaction later, it will only write the new data file to the region directory. 1335 * Since we already have this data, this will be idempotent but we will have a redundant 1336 * copy of the data. 1337 * - If RS fails between 2 and 3, the region will have a redundant copy of the data. The 1338 * RS that failed won't be able to finish snyc() for WAL because of lease recovery in WAL. 1339 * - If RS fails after 3, the region region server who opens the region will pick up the 1340 * the compaction marker from the WAL and replay it by removing the compaction input files. 1341 * Failed RS can also attempt to delete those files, but the operation will be idempotent 1342 * 1343 * See HBASE-2231 for details. 1344 * 1345 * @param compaction compaction details obtained from requestCompaction() 1346 * @throws IOException 1347 * @return Storefile we compacted into or null if we failed or opted out early. 1348 */ 1349 public List<HStoreFile> compact(CompactionContext compaction, 1350 ThroughputController throughputController, User user) throws IOException { 1351 assert compaction != null; 1352 List<HStoreFile> sfs = null; 1353 CompactionRequestImpl cr = compaction.getRequest(); 1354 try { 1355 // Do all sanity checking in here if we have a valid CompactionRequestImpl 1356 // because we need to clean up after it on the way out in a finally 1357 // block below 1358 long compactionStartTime = EnvironmentEdgeManager.currentTime(); 1359 assert compaction.hasSelection(); 1360 Collection<HStoreFile> filesToCompact = cr.getFiles(); 1361 assert !filesToCompact.isEmpty(); 1362 synchronized (filesCompacting) { 1363 // sanity check: we're compacting files that this store knows about 1364 // TODO: change this to LOG.error() after more debugging 1365 Preconditions.checkArgument(filesCompacting.containsAll(filesToCompact)); 1366 } 1367 1368 // Ready to go. Have list of files to compact. 1369 LOG.info("Starting compaction of " + filesToCompact + 1370 " into tmpdir=" + fs.getTempDir() + ", totalSize=" + 1371 TraditionalBinaryPrefix.long2String(cr.getSize(), "", 1)); 1372 1373 return doCompaction(cr, filesToCompact, user, compactionStartTime, 1374 compaction.compact(throughputController, user)); 1375 } finally { 1376 finishCompactionRequest(cr); 1377 } 1378 } 1379 1380 @VisibleForTesting 1381 protected List<HStoreFile> doCompaction(CompactionRequestImpl cr, 1382 Collection<HStoreFile> filesToCompact, User user, long compactionStartTime, 1383 List<Path> newFiles) throws IOException { 1384 // Do the steps necessary to complete the compaction. 1385 List<HStoreFile> sfs = moveCompactedFilesIntoPlace(cr, newFiles, user); 1386 writeCompactionWalRecord(filesToCompact, sfs); 1387 replaceStoreFiles(filesToCompact, sfs); 1388 if (cr.isMajor()) { 1389 majorCompactedCellsCount.addAndGet(getCompactionProgress().getTotalCompactingKVs()); 1390 majorCompactedCellsSize.addAndGet(getCompactionProgress().totalCompactedSize); 1391 } else { 1392 compactedCellsCount.addAndGet(getCompactionProgress().getTotalCompactingKVs()); 1393 compactedCellsSize.addAndGet(getCompactionProgress().totalCompactedSize); 1394 } 1395 long outputBytes = getTotalSize(sfs); 1396 1397 // At this point the store will use new files for all new scanners. 1398 completeCompaction(filesToCompact); // update store size. 1399 1400 long now = EnvironmentEdgeManager.currentTime(); 1401 if (region.getRegionServerServices() != null 1402 && region.getRegionServerServices().getMetrics() != null) { 1403 region.getRegionServerServices().getMetrics().updateCompaction(cr.isMajor(), 1404 now - compactionStartTime, cr.getFiles().size(), newFiles.size(), cr.getSize(), 1405 outputBytes); 1406 } 1407 1408 logCompactionEndMessage(cr, sfs, now, compactionStartTime); 1409 return sfs; 1410 } 1411 1412 private List<HStoreFile> moveCompactedFilesIntoPlace(CompactionRequestImpl cr, List<Path> newFiles, 1413 User user) throws IOException { 1414 List<HStoreFile> sfs = new ArrayList<>(newFiles.size()); 1415 for (Path newFile : newFiles) { 1416 assert newFile != null; 1417 HStoreFile sf = moveFileIntoPlace(newFile); 1418 if (this.getCoprocessorHost() != null) { 1419 getCoprocessorHost().postCompact(this, sf, cr.getTracker(), cr, user); 1420 } 1421 assert sf != null; 1422 sfs.add(sf); 1423 } 1424 return sfs; 1425 } 1426 1427 // Package-visible for tests 1428 HStoreFile moveFileIntoPlace(Path newFile) throws IOException { 1429 validateStoreFile(newFile); 1430 // Move the file into the right spot 1431 Path destPath = fs.commitStoreFile(getColumnFamilyName(), newFile); 1432 return createStoreFileAndReader(destPath); 1433 } 1434 1435 /** 1436 * Writes the compaction WAL record. 1437 * @param filesCompacted Files compacted (input). 1438 * @param newFiles Files from compaction. 1439 */ 1440 private void writeCompactionWalRecord(Collection<HStoreFile> filesCompacted, 1441 Collection<HStoreFile> newFiles) throws IOException { 1442 if (region.getWAL() == null) { 1443 return; 1444 } 1445 List<Path> inputPaths = 1446 filesCompacted.stream().map(HStoreFile::getPath).collect(Collectors.toList()); 1447 List<Path> outputPaths = 1448 newFiles.stream().map(HStoreFile::getPath).collect(Collectors.toList()); 1449 RegionInfo info = this.region.getRegionInfo(); 1450 CompactionDescriptor compactionDescriptor = ProtobufUtil.toCompactionDescriptor(info, 1451 family.getName(), inputPaths, outputPaths, fs.getStoreDir(getColumnFamilyDescriptor().getNameAsString())); 1452 // Fix reaching into Region to get the maxWaitForSeqId. 1453 // Does this method belong in Region altogether given it is making so many references up there? 1454 // Could be Region#writeCompactionMarker(compactionDescriptor); 1455 WALUtil.writeCompactionMarker(this.region.getWAL(), this.region.getReplicationScope(), 1456 this.region.getRegionInfo(), compactionDescriptor, this.region.getMVCC()); 1457 } 1458 1459 @VisibleForTesting 1460 void replaceStoreFiles(Collection<HStoreFile> compactedFiles, Collection<HStoreFile> result) 1461 throws IOException { 1462 this.lock.writeLock().lock(); 1463 try { 1464 this.storeEngine.getStoreFileManager().addCompactionResults(compactedFiles, result); 1465 synchronized (filesCompacting) { 1466 filesCompacting.removeAll(compactedFiles); 1467 } 1468 } finally { 1469 this.lock.writeLock().unlock(); 1470 } 1471 } 1472 1473 /** 1474 * Log a very elaborate compaction completion message. 1475 * @param cr Request. 1476 * @param sfs Resulting files. 1477 * @param compactionStartTime Start time. 1478 */ 1479 private void logCompactionEndMessage( 1480 CompactionRequestImpl cr, List<HStoreFile> sfs, long now, long compactionStartTime) { 1481 StringBuilder message = new StringBuilder( 1482 "Completed" + (cr.isMajor() ? " major" : "") + " compaction of " 1483 + cr.getFiles().size() + (cr.isAllFiles() ? " (all)" : "") + " file(s) in " 1484 + this + " of " + this.getRegionInfo().getShortNameToLog() + " into "); 1485 if (sfs.isEmpty()) { 1486 message.append("none, "); 1487 } else { 1488 for (HStoreFile sf: sfs) { 1489 message.append(sf.getPath().getName()); 1490 message.append("(size="); 1491 message.append(TraditionalBinaryPrefix.long2String(sf.getReader().length(), "", 1)); 1492 message.append("), "); 1493 } 1494 } 1495 message.append("total size for store is ") 1496 .append(StringUtils.TraditionalBinaryPrefix.long2String(storeSize.get(), "", 1)) 1497 .append(". This selection was in queue for ") 1498 .append(StringUtils.formatTimeDiff(compactionStartTime, cr.getSelectionTime())) 1499 .append(", and took ").append(StringUtils.formatTimeDiff(now, compactionStartTime)) 1500 .append(" to execute."); 1501 LOG.info(message.toString()); 1502 if (LOG.isTraceEnabled()) { 1503 int fileCount = storeEngine.getStoreFileManager().getStorefileCount(); 1504 long resultSize = getTotalSize(sfs); 1505 String traceMessage = "COMPACTION start,end,size out,files in,files out,store size," 1506 + "store files [" + compactionStartTime + "," + now + "," + resultSize + "," 1507 + cr.getFiles().size() + "," + sfs.size() + "," + storeSize + "," + fileCount + "]"; 1508 LOG.trace(traceMessage); 1509 } 1510 } 1511 1512 /** 1513 * Call to complete a compaction. Its for the case where we find in the WAL a compaction 1514 * that was not finished. We could find one recovering a WAL after a regionserver crash. 1515 * See HBASE-2231. 1516 * @param compaction 1517 */ 1518 public void replayCompactionMarker(CompactionDescriptor compaction, boolean pickCompactionFiles, 1519 boolean removeFiles) throws IOException { 1520 LOG.debug("Completing compaction from the WAL marker"); 1521 List<String> compactionInputs = compaction.getCompactionInputList(); 1522 List<String> compactionOutputs = Lists.newArrayList(compaction.getCompactionOutputList()); 1523 1524 // The Compaction Marker is written after the compaction is completed, 1525 // and the files moved into the region/family folder. 1526 // 1527 // If we crash after the entry is written, we may not have removed the 1528 // input files, but the output file is present. 1529 // (The unremoved input files will be removed by this function) 1530 // 1531 // If we scan the directory and the file is not present, it can mean that: 1532 // - The file was manually removed by the user 1533 // - The file was removed as consequence of subsequent compaction 1534 // so, we can't do anything with the "compaction output list" because those 1535 // files have already been loaded when opening the region (by virtue of 1536 // being in the store's folder) or they may be missing due to a compaction. 1537 1538 String familyName = this.getColumnFamilyName(); 1539 Set<String> inputFiles = new HashSet<>(); 1540 for (String compactionInput : compactionInputs) { 1541 Path inputPath = fs.getStoreFilePath(familyName, compactionInput); 1542 inputFiles.add(inputPath.getName()); 1543 } 1544 1545 //some of the input files might already be deleted 1546 List<HStoreFile> inputStoreFiles = new ArrayList<>(compactionInputs.size()); 1547 for (HStoreFile sf : this.getStorefiles()) { 1548 if (inputFiles.contains(sf.getPath().getName())) { 1549 inputStoreFiles.add(sf); 1550 } 1551 } 1552 1553 // check whether we need to pick up the new files 1554 List<HStoreFile> outputStoreFiles = new ArrayList<>(compactionOutputs.size()); 1555 1556 if (pickCompactionFiles) { 1557 for (HStoreFile sf : this.getStorefiles()) { 1558 compactionOutputs.remove(sf.getPath().getName()); 1559 } 1560 for (String compactionOutput : compactionOutputs) { 1561 StoreFileInfo storeFileInfo = fs.getStoreFileInfo(getColumnFamilyName(), compactionOutput); 1562 HStoreFile storeFile = createStoreFileAndReader(storeFileInfo); 1563 outputStoreFiles.add(storeFile); 1564 } 1565 } 1566 1567 if (!inputStoreFiles.isEmpty() || !outputStoreFiles.isEmpty()) { 1568 LOG.info("Replaying compaction marker, replacing input files: " + 1569 inputStoreFiles + " with output files : " + outputStoreFiles); 1570 this.replaceStoreFiles(inputStoreFiles, outputStoreFiles); 1571 this.completeCompaction(inputStoreFiles); 1572 } 1573 } 1574 1575 /** 1576 * This method tries to compact N recent files for testing. 1577 * Note that because compacting "recent" files only makes sense for some policies, 1578 * e.g. the default one, it assumes default policy is used. It doesn't use policy, 1579 * but instead makes a compaction candidate list by itself. 1580 * @param N Number of files. 1581 */ 1582 @VisibleForTesting 1583 public void compactRecentForTestingAssumingDefaultPolicy(int N) throws IOException { 1584 List<HStoreFile> filesToCompact; 1585 boolean isMajor; 1586 1587 this.lock.readLock().lock(); 1588 try { 1589 synchronized (filesCompacting) { 1590 filesToCompact = Lists.newArrayList(storeEngine.getStoreFileManager().getStorefiles()); 1591 if (!filesCompacting.isEmpty()) { 1592 // exclude all files older than the newest file we're currently 1593 // compacting. this allows us to preserve contiguity (HBASE-2856) 1594 HStoreFile last = filesCompacting.get(filesCompacting.size() - 1); 1595 int idx = filesToCompact.indexOf(last); 1596 Preconditions.checkArgument(idx != -1); 1597 filesToCompact.subList(0, idx + 1).clear(); 1598 } 1599 int count = filesToCompact.size(); 1600 if (N > count) { 1601 throw new RuntimeException("Not enough files"); 1602 } 1603 1604 filesToCompact = filesToCompact.subList(count - N, count); 1605 isMajor = (filesToCompact.size() == storeEngine.getStoreFileManager().getStorefileCount()); 1606 filesCompacting.addAll(filesToCompact); 1607 Collections.sort(filesCompacting, storeEngine.getStoreFileManager() 1608 .getStoreFileComparator()); 1609 } 1610 } finally { 1611 this.lock.readLock().unlock(); 1612 } 1613 1614 try { 1615 // Ready to go. Have list of files to compact. 1616 List<Path> newFiles = ((DefaultCompactor)this.storeEngine.getCompactor()) 1617 .compactForTesting(filesToCompact, isMajor); 1618 for (Path newFile: newFiles) { 1619 // Move the compaction into place. 1620 HStoreFile sf = moveFileIntoPlace(newFile); 1621 if (this.getCoprocessorHost() != null) { 1622 this.getCoprocessorHost().postCompact(this, sf, null, null, null); 1623 } 1624 replaceStoreFiles(filesToCompact, Collections.singletonList(sf)); 1625 completeCompaction(filesToCompact); 1626 } 1627 } finally { 1628 synchronized (filesCompacting) { 1629 filesCompacting.removeAll(filesToCompact); 1630 } 1631 } 1632 } 1633 1634 @Override 1635 public boolean hasReferences() { 1636 List<HStoreFile> reloadedStoreFiles = null; 1637 try { 1638 // Reloading the store files from file system due to HBASE-20940. As split can happen with an 1639 // region which has references 1640 reloadedStoreFiles = loadStoreFiles(); 1641 return StoreUtils.hasReferences(reloadedStoreFiles); 1642 } catch (IOException ioe) { 1643 LOG.error("Error trying to determine if store has references, assuming references exists", 1644 ioe); 1645 return true; 1646 } finally { 1647 if (reloadedStoreFiles != null) { 1648 for (HStoreFile storeFile : reloadedStoreFiles) { 1649 try { 1650 storeFile.closeStoreFile(false); 1651 } catch (IOException ioe) { 1652 LOG.warn("Encountered exception closing " + storeFile + ": " + ioe.getMessage()); 1653 // continue with closing the remaining store files 1654 } 1655 } 1656 } 1657 } 1658 } 1659 1660 /** 1661 * getter for CompactionProgress object 1662 * @return CompactionProgress object; can be null 1663 */ 1664 public CompactionProgress getCompactionProgress() { 1665 return this.storeEngine.getCompactor().getProgress(); 1666 } 1667 1668 @Override 1669 public boolean shouldPerformMajorCompaction() throws IOException { 1670 for (HStoreFile sf : this.storeEngine.getStoreFileManager().getStorefiles()) { 1671 // TODO: what are these reader checks all over the place? 1672 if (sf.getReader() == null) { 1673 LOG.debug("StoreFile {} has null Reader", sf); 1674 return false; 1675 } 1676 } 1677 return storeEngine.getCompactionPolicy().shouldPerformMajorCompaction( 1678 this.storeEngine.getStoreFileManager().getStorefiles()); 1679 } 1680 1681 public Optional<CompactionContext> requestCompaction() throws IOException { 1682 return requestCompaction(NO_PRIORITY, CompactionLifeCycleTracker.DUMMY, null); 1683 } 1684 1685 public Optional<CompactionContext> requestCompaction(int priority, 1686 CompactionLifeCycleTracker tracker, User user) throws IOException { 1687 // don't even select for compaction if writes are disabled 1688 if (!this.areWritesEnabled()) { 1689 return Optional.empty(); 1690 } 1691 // Before we do compaction, try to get rid of unneeded files to simplify things. 1692 removeUnneededFiles(); 1693 1694 final CompactionContext compaction = storeEngine.createCompaction(); 1695 CompactionRequestImpl request = null; 1696 this.lock.readLock().lock(); 1697 try { 1698 synchronized (filesCompacting) { 1699 // First, see if coprocessor would want to override selection. 1700 if (this.getCoprocessorHost() != null) { 1701 final List<HStoreFile> candidatesForCoproc = compaction.preSelect(this.filesCompacting); 1702 boolean override = getCoprocessorHost().preCompactSelection(this, 1703 candidatesForCoproc, tracker, user); 1704 if (override) { 1705 // Coprocessor is overriding normal file selection. 1706 compaction.forceSelect(new CompactionRequestImpl(candidatesForCoproc)); 1707 } 1708 } 1709 1710 // Normal case - coprocessor is not overriding file selection. 1711 if (!compaction.hasSelection()) { 1712 boolean isUserCompaction = priority == Store.PRIORITY_USER; 1713 boolean mayUseOffPeak = offPeakHours.isOffPeakHour() && 1714 offPeakCompactionTracker.compareAndSet(false, true); 1715 try { 1716 compaction.select(this.filesCompacting, isUserCompaction, 1717 mayUseOffPeak, forceMajor && filesCompacting.isEmpty()); 1718 } catch (IOException e) { 1719 if (mayUseOffPeak) { 1720 offPeakCompactionTracker.set(false); 1721 } 1722 throw e; 1723 } 1724 assert compaction.hasSelection(); 1725 if (mayUseOffPeak && !compaction.getRequest().isOffPeak()) { 1726 // Compaction policy doesn't want to take advantage of off-peak. 1727 offPeakCompactionTracker.set(false); 1728 } 1729 } 1730 if (this.getCoprocessorHost() != null) { 1731 this.getCoprocessorHost().postCompactSelection( 1732 this, ImmutableList.copyOf(compaction.getRequest().getFiles()), tracker, 1733 compaction.getRequest(), user); 1734 } 1735 // Finally, we have the resulting files list. Check if we have any files at all. 1736 request = compaction.getRequest(); 1737 Collection<HStoreFile> selectedFiles = request.getFiles(); 1738 if (selectedFiles.isEmpty()) { 1739 return Optional.empty(); 1740 } 1741 1742 addToCompactingFiles(selectedFiles); 1743 1744 // If we're enqueuing a major, clear the force flag. 1745 this.forceMajor = this.forceMajor && !request.isMajor(); 1746 1747 // Set common request properties. 1748 // Set priority, either override value supplied by caller or from store. 1749 request.setPriority((priority != Store.NO_PRIORITY) ? priority : getCompactPriority()); 1750 request.setDescription(getRegionInfo().getRegionNameAsString(), getColumnFamilyName()); 1751 request.setTracker(tracker); 1752 } 1753 } finally { 1754 this.lock.readLock().unlock(); 1755 } 1756 1757 if (LOG.isDebugEnabled()) { 1758 LOG.debug(getRegionInfo().getEncodedName() + " - " + getColumnFamilyName() 1759 + ": Initiating " + (request.isMajor() ? "major" : "minor") + " compaction" 1760 + (request.isAllFiles() ? " (all files)" : "")); 1761 } 1762 this.region.reportCompactionRequestStart(request.isMajor()); 1763 return Optional.of(compaction); 1764 } 1765 1766 /** Adds the files to compacting files. filesCompacting must be locked. */ 1767 private void addToCompactingFiles(Collection<HStoreFile> filesToAdd) { 1768 if (CollectionUtils.isEmpty(filesToAdd)) { 1769 return; 1770 } 1771 // Check that we do not try to compact the same StoreFile twice. 1772 if (!Collections.disjoint(filesCompacting, filesToAdd)) { 1773 Preconditions.checkArgument(false, "%s overlaps with %s", filesToAdd, filesCompacting); 1774 } 1775 filesCompacting.addAll(filesToAdd); 1776 Collections.sort(filesCompacting, storeEngine.getStoreFileManager().getStoreFileComparator()); 1777 } 1778 1779 private void removeUnneededFiles() throws IOException { 1780 if (!conf.getBoolean("hbase.store.delete.expired.storefile", true)) return; 1781 if (getColumnFamilyDescriptor().getMinVersions() > 0) { 1782 LOG.debug("Skipping expired store file removal due to min version being {}", 1783 getColumnFamilyDescriptor().getMinVersions()); 1784 return; 1785 } 1786 this.lock.readLock().lock(); 1787 Collection<HStoreFile> delSfs = null; 1788 try { 1789 synchronized (filesCompacting) { 1790 long cfTtl = getStoreFileTtl(); 1791 if (cfTtl != Long.MAX_VALUE) { 1792 delSfs = storeEngine.getStoreFileManager().getUnneededFiles( 1793 EnvironmentEdgeManager.currentTime() - cfTtl, filesCompacting); 1794 addToCompactingFiles(delSfs); 1795 } 1796 } 1797 } finally { 1798 this.lock.readLock().unlock(); 1799 } 1800 1801 if (CollectionUtils.isEmpty(delSfs)) { 1802 return; 1803 } 1804 1805 Collection<HStoreFile> newFiles = Collections.emptyList(); // No new files. 1806 writeCompactionWalRecord(delSfs, newFiles); 1807 replaceStoreFiles(delSfs, newFiles); 1808 completeCompaction(delSfs); 1809 LOG.info("Completed removal of " + delSfs.size() + " unnecessary (expired) file(s) in " 1810 + this + " of " + this.getRegionInfo().getRegionNameAsString() 1811 + "; total size for store is " 1812 + TraditionalBinaryPrefix.long2String(storeSize.get(), "", 1)); 1813 } 1814 1815 public void cancelRequestedCompaction(CompactionContext compaction) { 1816 finishCompactionRequest(compaction.getRequest()); 1817 } 1818 1819 private void finishCompactionRequest(CompactionRequestImpl cr) { 1820 this.region.reportCompactionRequestEnd(cr.isMajor(), cr.getFiles().size(), cr.getSize()); 1821 if (cr.isOffPeak()) { 1822 offPeakCompactionTracker.set(false); 1823 cr.setOffPeak(false); 1824 } 1825 synchronized (filesCompacting) { 1826 filesCompacting.removeAll(cr.getFiles()); 1827 } 1828 } 1829 1830 /** 1831 * Validates a store file by opening and closing it. In HFileV2 this should not be an expensive 1832 * operation. 1833 * @param path the path to the store file 1834 */ 1835 private void validateStoreFile(Path path) throws IOException { 1836 HStoreFile storeFile = null; 1837 try { 1838 storeFile = createStoreFileAndReader(path); 1839 } catch (IOException e) { 1840 LOG.error("Failed to open store file : {}, keeping it in tmp location", path, e); 1841 throw e; 1842 } finally { 1843 if (storeFile != null) { 1844 storeFile.closeStoreFile(false); 1845 } 1846 } 1847 } 1848 1849 /** 1850 * <p>It works by processing a compaction that's been written to disk. 1851 * 1852 * <p>It is usually invoked at the end of a compaction, but might also be 1853 * invoked at HStore startup, if the prior execution died midway through. 1854 * 1855 * <p>Moving the compacted TreeMap into place means: 1856 * <pre> 1857 * 1) Unload all replaced StoreFile, close and collect list to delete. 1858 * 2) Compute new store size 1859 * </pre> 1860 * 1861 * @param compactedFiles list of files that were compacted 1862 */ 1863 @VisibleForTesting 1864 protected void completeCompaction(Collection<HStoreFile> compactedFiles) 1865 throws IOException { 1866 this.storeSize.set(0L); 1867 this.totalUncompressedBytes.set(0L); 1868 for (HStoreFile hsf : this.storeEngine.getStoreFileManager().getStorefiles()) { 1869 StoreFileReader r = hsf.getReader(); 1870 if (r == null) { 1871 LOG.warn("StoreFile {} has a null Reader", hsf); 1872 continue; 1873 } 1874 this.storeSize.addAndGet(r.length()); 1875 this.totalUncompressedBytes.addAndGet(r.getTotalUncompressedBytes()); 1876 } 1877 } 1878 1879 /* 1880 * @param wantedVersions How many versions were asked for. 1881 * @return wantedVersions or this families' {@link HConstants#VERSIONS}. 1882 */ 1883 int versionsToReturn(final int wantedVersions) { 1884 if (wantedVersions <= 0) { 1885 throw new IllegalArgumentException("Number of versions must be > 0"); 1886 } 1887 // Make sure we do not return more than maximum versions for this store. 1888 int maxVersions = this.family.getMaxVersions(); 1889 return wantedVersions > maxVersions ? maxVersions: wantedVersions; 1890 } 1891 1892 @Override 1893 public boolean canSplit() { 1894 this.lock.readLock().lock(); 1895 try { 1896 // Not split-able if we find a reference store file present in the store. 1897 boolean result = !hasReferences(); 1898 if (!result) { 1899 LOG.trace("Not splittable; has references: {}", this); 1900 } 1901 return result; 1902 } finally { 1903 this.lock.readLock().unlock(); 1904 } 1905 } 1906 1907 /** 1908 * Determines if Store should be split. 1909 */ 1910 public Optional<byte[]> getSplitPoint() { 1911 this.lock.readLock().lock(); 1912 try { 1913 // Should already be enforced by the split policy! 1914 assert !this.getRegionInfo().isMetaRegion(); 1915 // Not split-able if we find a reference store file present in the store. 1916 if (hasReferences()) { 1917 LOG.trace("Not splittable; has references: {}", this); 1918 return Optional.empty(); 1919 } 1920 return this.storeEngine.getStoreFileManager().getSplitPoint(); 1921 } catch(IOException e) { 1922 LOG.warn("Failed getting store size for {}", this, e); 1923 } finally { 1924 this.lock.readLock().unlock(); 1925 } 1926 return Optional.empty(); 1927 } 1928 1929 @Override 1930 public long getLastCompactSize() { 1931 return this.lastCompactSize; 1932 } 1933 1934 @Override 1935 public long getSize() { 1936 return storeSize.get(); 1937 } 1938 1939 public void triggerMajorCompaction() { 1940 this.forceMajor = true; 1941 } 1942 1943 ////////////////////////////////////////////////////////////////////////////// 1944 // File administration 1945 ////////////////////////////////////////////////////////////////////////////// 1946 1947 /** 1948 * Return a scanner for both the memstore and the HStore files. Assumes we are not in a 1949 * compaction. 1950 * @param scan Scan to apply when scanning the stores 1951 * @param targetCols columns to scan 1952 * @return a scanner over the current key values 1953 * @throws IOException on failure 1954 */ 1955 public KeyValueScanner getScanner(Scan scan, final NavigableSet<byte[]> targetCols, long readPt) 1956 throws IOException { 1957 lock.readLock().lock(); 1958 try { 1959 ScanInfo scanInfo; 1960 if (this.getCoprocessorHost() != null) { 1961 scanInfo = this.getCoprocessorHost().preStoreScannerOpen(this); 1962 } else { 1963 scanInfo = getScanInfo(); 1964 } 1965 return createScanner(scan, scanInfo, targetCols, readPt); 1966 } finally { 1967 lock.readLock().unlock(); 1968 } 1969 } 1970 1971 // HMobStore will override this method to return its own implementation. 1972 protected KeyValueScanner createScanner(Scan scan, ScanInfo scanInfo, 1973 NavigableSet<byte[]> targetCols, long readPt) throws IOException { 1974 return scan.isReversed() ? new ReversedStoreScanner(this, scanInfo, scan, targetCols, readPt) 1975 : new StoreScanner(this, scanInfo, scan, targetCols, readPt); 1976 } 1977 1978 /** 1979 * Recreates the scanners on the current list of active store file scanners 1980 * @param currentFileScanners the current set of active store file scanners 1981 * @param cacheBlocks cache the blocks or not 1982 * @param usePread use pread or not 1983 * @param isCompaction is the scanner for compaction 1984 * @param matcher the scan query matcher 1985 * @param startRow the scan's start row 1986 * @param includeStartRow should the scan include the start row 1987 * @param stopRow the scan's stop row 1988 * @param includeStopRow should the scan include the stop row 1989 * @param readPt the read point of the current scane 1990 * @param includeMemstoreScanner whether the current scanner should include memstorescanner 1991 * @return list of scanners recreated on the current Scanners 1992 * @throws IOException 1993 */ 1994 public List<KeyValueScanner> recreateScanners(List<KeyValueScanner> currentFileScanners, 1995 boolean cacheBlocks, boolean usePread, boolean isCompaction, ScanQueryMatcher matcher, 1996 byte[] startRow, boolean includeStartRow, byte[] stopRow, boolean includeStopRow, long readPt, 1997 boolean includeMemstoreScanner) throws IOException { 1998 this.lock.readLock().lock(); 1999 try { 2000 Map<String, HStoreFile> name2File = 2001 new HashMap<>(getStorefilesCount() + getCompactedFilesCount()); 2002 for (HStoreFile file : getStorefiles()) { 2003 name2File.put(file.getFileInfo().getActiveFileName(), file); 2004 } 2005 Collection<HStoreFile> compactedFiles = getCompactedFiles(); 2006 for (HStoreFile file : IterableUtils.emptyIfNull(compactedFiles)) { 2007 name2File.put(file.getFileInfo().getActiveFileName(), file); 2008 } 2009 List<HStoreFile> filesToReopen = new ArrayList<>(); 2010 for (KeyValueScanner kvs : currentFileScanners) { 2011 assert kvs.isFileScanner(); 2012 if (kvs.peek() == null) { 2013 continue; 2014 } 2015 filesToReopen.add(name2File.get(kvs.getFilePath().getName())); 2016 } 2017 if (filesToReopen.isEmpty()) { 2018 return null; 2019 } 2020 return getScanners(filesToReopen, cacheBlocks, false, false, matcher, startRow, 2021 includeStartRow, stopRow, includeStopRow, readPt, false); 2022 } finally { 2023 this.lock.readLock().unlock(); 2024 } 2025 } 2026 2027 @Override 2028 public String toString() { 2029 return this.getColumnFamilyName(); 2030 } 2031 2032 @Override 2033 public int getStorefilesCount() { 2034 return this.storeEngine.getStoreFileManager().getStorefileCount(); 2035 } 2036 2037 @Override 2038 public int getCompactedFilesCount() { 2039 return this.storeEngine.getStoreFileManager().getCompactedFilesCount(); 2040 } 2041 2042 private LongStream getStoreFileAgeStream() { 2043 return this.storeEngine.getStoreFileManager().getStorefiles().stream().filter(sf -> { 2044 if (sf.getReader() == null) { 2045 LOG.warn("StoreFile {} has a null Reader", sf); 2046 return false; 2047 } else { 2048 return true; 2049 } 2050 }).filter(HStoreFile::isHFile).mapToLong(sf -> sf.getFileInfo().getCreatedTimestamp()) 2051 .map(t -> EnvironmentEdgeManager.currentTime() - t); 2052 } 2053 2054 @Override 2055 public OptionalLong getMaxStoreFileAge() { 2056 return getStoreFileAgeStream().max(); 2057 } 2058 2059 @Override 2060 public OptionalLong getMinStoreFileAge() { 2061 return getStoreFileAgeStream().min(); 2062 } 2063 2064 @Override 2065 public OptionalDouble getAvgStoreFileAge() { 2066 return getStoreFileAgeStream().average(); 2067 } 2068 2069 @Override 2070 public long getNumReferenceFiles() { 2071 return this.storeEngine.getStoreFileManager().getStorefiles().stream() 2072 .filter(HStoreFile::isReference).count(); 2073 } 2074 2075 @Override 2076 public long getNumHFiles() { 2077 return this.storeEngine.getStoreFileManager().getStorefiles().stream() 2078 .filter(HStoreFile::isHFile).count(); 2079 } 2080 2081 @Override 2082 public long getStoreSizeUncompressed() { 2083 return this.totalUncompressedBytes.get(); 2084 } 2085 2086 @Override 2087 public long getStorefilesSize() { 2088 // Include all StoreFiles 2089 return getStorefilesSize(storeFile -> true); 2090 } 2091 2092 @Override 2093 public long getHFilesSize() { 2094 // Include only StoreFiles which are HFiles 2095 return getStorefilesSize(storeFile -> storeFile.isHFile()); 2096 } 2097 2098 private long getStorefilesSize(Predicate<HStoreFile> predicate) { 2099 return this.storeEngine.getStoreFileManager().getStorefiles().stream().filter(sf -> { 2100 if (sf.getReader() == null) { 2101 LOG.warn("StoreFile {} has a null Reader", sf); 2102 return false; 2103 } else { 2104 return true; 2105 } 2106 }).filter(predicate).mapToLong(sf -> sf.getReader().length()).sum(); 2107 } 2108 2109 private long getStoreFileFieldSize(ToLongFunction<StoreFileReader> f) { 2110 return this.storeEngine.getStoreFileManager().getStorefiles().stream().filter(sf -> { 2111 if (sf.getReader() == null) { 2112 LOG.warn("StoreFile {} has a null Reader", sf); 2113 return false; 2114 } else { 2115 return true; 2116 } 2117 }).map(HStoreFile::getReader).mapToLong(f).sum(); 2118 } 2119 2120 @Override 2121 public long getStorefilesRootLevelIndexSize() { 2122 return getStoreFileFieldSize(StoreFileReader::indexSize); 2123 } 2124 2125 @Override 2126 public long getTotalStaticIndexSize() { 2127 return getStoreFileFieldSize(StoreFileReader::getUncompressedDataIndexSize); 2128 } 2129 2130 @Override 2131 public long getTotalStaticBloomSize() { 2132 return getStoreFileFieldSize(StoreFileReader::getTotalBloomSize); 2133 } 2134 2135 @Override 2136 public MemStoreSize getMemStoreSize() { 2137 return this.memstore.size(); 2138 } 2139 2140 @Override 2141 public int getCompactPriority() { 2142 int priority = this.storeEngine.getStoreFileManager().getStoreCompactionPriority(); 2143 if (priority == PRIORITY_USER) { 2144 LOG.warn("Compaction priority is USER despite there being no user compaction"); 2145 } 2146 return priority; 2147 } 2148 2149 public boolean throttleCompaction(long compactionSize) { 2150 return storeEngine.getCompactionPolicy().throttleCompaction(compactionSize); 2151 } 2152 2153 public HRegion getHRegion() { 2154 return this.region; 2155 } 2156 2157 public RegionCoprocessorHost getCoprocessorHost() { 2158 return this.region.getCoprocessorHost(); 2159 } 2160 2161 @Override 2162 public RegionInfo getRegionInfo() { 2163 return this.fs.getRegionInfo(); 2164 } 2165 2166 @Override 2167 public boolean areWritesEnabled() { 2168 return this.region.areWritesEnabled(); 2169 } 2170 2171 @Override 2172 public long getSmallestReadPoint() { 2173 return this.region.getSmallestReadPoint(); 2174 } 2175 2176 /** 2177 * Adds or replaces the specified KeyValues. 2178 * <p> 2179 * For each KeyValue specified, if a cell with the same row, family, and qualifier exists in 2180 * MemStore, it will be replaced. Otherwise, it will just be inserted to MemStore. 2181 * <p> 2182 * This operation is atomic on each KeyValue (row/family/qualifier) but not necessarily atomic 2183 * across all of them. 2184 * @param readpoint readpoint below which we can safely remove duplicate KVs 2185 * @throws IOException 2186 */ 2187 public void upsert(Iterable<Cell> cells, long readpoint, MemStoreSizing memstoreSizing) 2188 throws IOException { 2189 this.lock.readLock().lock(); 2190 try { 2191 this.memstore.upsert(cells, readpoint, memstoreSizing); 2192 } finally { 2193 this.lock.readLock().unlock(); 2194 } 2195 } 2196 2197 public StoreFlushContext createFlushContext(long cacheFlushId, FlushLifeCycleTracker tracker) { 2198 return new StoreFlusherImpl(cacheFlushId, tracker); 2199 } 2200 2201 private final class StoreFlusherImpl implements StoreFlushContext { 2202 2203 private final FlushLifeCycleTracker tracker; 2204 private final long cacheFlushSeqNum; 2205 private MemStoreSnapshot snapshot; 2206 private List<Path> tempFiles; 2207 private List<Path> committedFiles; 2208 private long cacheFlushCount; 2209 private long cacheFlushSize; 2210 private long outputFileSize; 2211 2212 private StoreFlusherImpl(long cacheFlushSeqNum, FlushLifeCycleTracker tracker) { 2213 this.cacheFlushSeqNum = cacheFlushSeqNum; 2214 this.tracker = tracker; 2215 } 2216 2217 /** 2218 * This is not thread safe. The caller should have a lock on the region or the store. 2219 * If necessary, the lock can be added with the patch provided in HBASE-10087 2220 */ 2221 @Override 2222 public MemStoreSize prepare() { 2223 // passing the current sequence number of the wal - to allow bookkeeping in the memstore 2224 this.snapshot = memstore.snapshot(); 2225 this.cacheFlushCount = snapshot.getCellsCount(); 2226 this.cacheFlushSize = snapshot.getDataSize(); 2227 committedFiles = new ArrayList<>(1); 2228 return snapshot.getMemStoreSize(); 2229 } 2230 2231 @Override 2232 public void flushCache(MonitoredTask status) throws IOException { 2233 RegionServerServices rsService = region.getRegionServerServices(); 2234 ThroughputController throughputController = 2235 rsService == null ? null : rsService.getFlushThroughputController(); 2236 tempFiles = 2237 HStore.this.flushCache(cacheFlushSeqNum, snapshot, status, throughputController, tracker); 2238 } 2239 2240 @Override 2241 public boolean commit(MonitoredTask status) throws IOException { 2242 if (CollectionUtils.isEmpty(this.tempFiles)) { 2243 return false; 2244 } 2245 List<HStoreFile> storeFiles = new ArrayList<>(this.tempFiles.size()); 2246 for (Path storeFilePath : tempFiles) { 2247 try { 2248 HStoreFile sf = HStore.this.commitFile(storeFilePath, cacheFlushSeqNum, status); 2249 outputFileSize += sf.getReader().length(); 2250 storeFiles.add(sf); 2251 } catch (IOException ex) { 2252 LOG.error("Failed to commit store file {}", storeFilePath, ex); 2253 // Try to delete the files we have committed before. 2254 for (HStoreFile sf : storeFiles) { 2255 Path pathToDelete = sf.getPath(); 2256 try { 2257 sf.deleteStoreFile(); 2258 } catch (IOException deleteEx) { 2259 LOG.error(HBaseMarkers.FATAL, "Failed to delete store file we committed, " 2260 + "halting {}", pathToDelete, ex); 2261 Runtime.getRuntime().halt(1); 2262 } 2263 } 2264 throw new IOException("Failed to commit the flush", ex); 2265 } 2266 } 2267 2268 for (HStoreFile sf : storeFiles) { 2269 if (HStore.this.getCoprocessorHost() != null) { 2270 HStore.this.getCoprocessorHost().postFlush(HStore.this, sf, tracker); 2271 } 2272 committedFiles.add(sf.getPath()); 2273 } 2274 2275 HStore.this.flushedCellsCount.addAndGet(cacheFlushCount); 2276 HStore.this.flushedCellsSize.addAndGet(cacheFlushSize); 2277 HStore.this.flushedOutputFileSize.addAndGet(outputFileSize); 2278 2279 // Add new file to store files. Clear snapshot too while we have the Store write lock. 2280 return HStore.this.updateStorefiles(storeFiles, snapshot.getId()); 2281 } 2282 2283 @Override 2284 public long getOutputFileSize() { 2285 return outputFileSize; 2286 } 2287 2288 @Override 2289 public List<Path> getCommittedFiles() { 2290 return committedFiles; 2291 } 2292 2293 /** 2294 * Similar to commit, but called in secondary region replicas for replaying the 2295 * flush cache from primary region. Adds the new files to the store, and drops the 2296 * snapshot depending on dropMemstoreSnapshot argument. 2297 * @param fileNames names of the flushed files 2298 * @param dropMemstoreSnapshot whether to drop the prepared memstore snapshot 2299 * @throws IOException 2300 */ 2301 @Override 2302 public void replayFlush(List<String> fileNames, boolean dropMemstoreSnapshot) 2303 throws IOException { 2304 List<HStoreFile> storeFiles = new ArrayList<>(fileNames.size()); 2305 for (String file : fileNames) { 2306 // open the file as a store file (hfile link, etc) 2307 StoreFileInfo storeFileInfo = fs.getStoreFileInfo(getColumnFamilyName(), file); 2308 HStoreFile storeFile = createStoreFileAndReader(storeFileInfo); 2309 storeFiles.add(storeFile); 2310 HStore.this.storeSize.addAndGet(storeFile.getReader().length()); 2311 HStore.this.totalUncompressedBytes 2312 .addAndGet(storeFile.getReader().getTotalUncompressedBytes()); 2313 if (LOG.isInfoEnabled()) { 2314 LOG.info("Region: " + HStore.this.getRegionInfo().getEncodedName() + 2315 " added " + storeFile + ", entries=" + storeFile.getReader().getEntries() + 2316 ", sequenceid=" + +storeFile.getReader().getSequenceID() + ", filesize=" 2317 + TraditionalBinaryPrefix.long2String(storeFile.getReader().length(), "", 1)); 2318 } 2319 } 2320 2321 long snapshotId = -1; // -1 means do not drop 2322 if (dropMemstoreSnapshot && snapshot != null) { 2323 snapshotId = snapshot.getId(); 2324 snapshot.close(); 2325 } 2326 HStore.this.updateStorefiles(storeFiles, snapshotId); 2327 } 2328 2329 /** 2330 * Abort the snapshot preparation. Drops the snapshot if any. 2331 * @throws IOException 2332 */ 2333 @Override 2334 public void abort() throws IOException { 2335 if (snapshot != null) { 2336 //We need to close the snapshot when aborting, otherwise, the segment scanner 2337 //won't be closed. If we are using MSLAB, the chunk referenced by those scanners 2338 //can't be released, thus memory leak 2339 snapshot.close(); 2340 HStore.this.updateStorefiles(Collections.emptyList(), snapshot.getId()); 2341 } 2342 } 2343 } 2344 2345 @Override 2346 public boolean needsCompaction() { 2347 List<HStoreFile> filesCompactingClone = null; 2348 synchronized (filesCompacting) { 2349 filesCompactingClone = Lists.newArrayList(filesCompacting); 2350 } 2351 return this.storeEngine.needsCompaction(filesCompactingClone); 2352 } 2353 2354 /** 2355 * Used for tests. 2356 * @return cache configuration for this Store. 2357 */ 2358 @VisibleForTesting 2359 public CacheConfig getCacheConfig() { 2360 return this.cacheConf; 2361 } 2362 2363 public static final long FIXED_OVERHEAD = 2364 ClassSize.align(ClassSize.OBJECT + (26 * ClassSize.REFERENCE) + (2 * Bytes.SIZEOF_LONG) 2365 + (5 * Bytes.SIZEOF_INT) + (2 * Bytes.SIZEOF_BOOLEAN)); 2366 2367 public static final long DEEP_OVERHEAD = ClassSize.align(FIXED_OVERHEAD 2368 + ClassSize.OBJECT + ClassSize.REENTRANT_LOCK 2369 + ClassSize.CONCURRENT_SKIPLISTMAP 2370 + ClassSize.CONCURRENT_SKIPLISTMAP_ENTRY + ClassSize.OBJECT 2371 + ScanInfo.FIXED_OVERHEAD); 2372 2373 @Override 2374 public long heapSize() { 2375 MemStoreSize memstoreSize = this.memstore.size(); 2376 return DEEP_OVERHEAD + memstoreSize.getHeapSize(); 2377 } 2378 2379 @Override 2380 public CellComparator getComparator() { 2381 return comparator; 2382 } 2383 2384 public ScanInfo getScanInfo() { 2385 return scanInfo; 2386 } 2387 2388 /** 2389 * Set scan info, used by test 2390 * @param scanInfo new scan info to use for test 2391 */ 2392 void setScanInfo(ScanInfo scanInfo) { 2393 this.scanInfo = scanInfo; 2394 } 2395 2396 @Override 2397 public boolean hasTooManyStoreFiles() { 2398 return getStorefilesCount() > this.blockingFileCount; 2399 } 2400 2401 @Override 2402 public long getFlushedCellsCount() { 2403 return flushedCellsCount.get(); 2404 } 2405 2406 @Override 2407 public long getFlushedCellsSize() { 2408 return flushedCellsSize.get(); 2409 } 2410 2411 @Override 2412 public long getFlushedOutputFileSize() { 2413 return flushedOutputFileSize.get(); 2414 } 2415 2416 @Override 2417 public long getCompactedCellsCount() { 2418 return compactedCellsCount.get(); 2419 } 2420 2421 @Override 2422 public long getCompactedCellsSize() { 2423 return compactedCellsSize.get(); 2424 } 2425 2426 @Override 2427 public long getMajorCompactedCellsCount() { 2428 return majorCompactedCellsCount.get(); 2429 } 2430 2431 @Override 2432 public long getMajorCompactedCellsSize() { 2433 return majorCompactedCellsSize.get(); 2434 } 2435 2436 /** 2437 * Returns the StoreEngine that is backing this concrete implementation of Store. 2438 * @return Returns the {@link StoreEngine} object used internally inside this HStore object. 2439 */ 2440 @VisibleForTesting 2441 public StoreEngine<?, ?, ?, ?> getStoreEngine() { 2442 return this.storeEngine; 2443 } 2444 2445 protected OffPeakHours getOffPeakHours() { 2446 return this.offPeakHours; 2447 } 2448 2449 /** 2450 * {@inheritDoc} 2451 */ 2452 @Override 2453 public void onConfigurationChange(Configuration conf) { 2454 this.conf = new CompoundConfiguration() 2455 .add(conf) 2456 .addBytesMap(family.getValues()); 2457 this.storeEngine.compactionPolicy.setConf(conf); 2458 this.offPeakHours = OffPeakHours.getInstance(conf); 2459 } 2460 2461 /** 2462 * {@inheritDoc} 2463 */ 2464 @Override 2465 public void registerChildren(ConfigurationManager manager) { 2466 // No children to register 2467 } 2468 2469 /** 2470 * {@inheritDoc} 2471 */ 2472 @Override 2473 public void deregisterChildren(ConfigurationManager manager) { 2474 // No children to deregister 2475 } 2476 2477 @Override 2478 public double getCompactionPressure() { 2479 return storeEngine.getStoreFileManager().getCompactionPressure(); 2480 } 2481 2482 @Override 2483 public boolean isPrimaryReplicaStore() { 2484 return getRegionInfo().getReplicaId() == RegionInfo.DEFAULT_REPLICA_ID; 2485 } 2486 2487 /** 2488 * Sets the store up for a region level snapshot operation. 2489 * @see #postSnapshotOperation() 2490 */ 2491 public void preSnapshotOperation() { 2492 archiveLock.lock(); 2493 } 2494 2495 /** 2496 * Perform tasks needed after the completion of snapshot operation. 2497 * @see #preSnapshotOperation() 2498 */ 2499 public void postSnapshotOperation() { 2500 archiveLock.unlock(); 2501 } 2502 2503 /** 2504 * Closes and archives the compacted files under this store 2505 */ 2506 public synchronized void closeAndArchiveCompactedFiles() throws IOException { 2507 // ensure other threads do not attempt to archive the same files on close() 2508 archiveLock.lock(); 2509 try { 2510 lock.readLock().lock(); 2511 Collection<HStoreFile> copyCompactedfiles = null; 2512 try { 2513 Collection<HStoreFile> compactedfiles = 2514 this.getStoreEngine().getStoreFileManager().getCompactedfiles(); 2515 if (CollectionUtils.isNotEmpty(compactedfiles)) { 2516 // Do a copy under read lock 2517 copyCompactedfiles = new ArrayList<>(compactedfiles); 2518 } else { 2519 LOG.trace("No compacted files to archive"); 2520 } 2521 } finally { 2522 lock.readLock().unlock(); 2523 } 2524 if (CollectionUtils.isNotEmpty(copyCompactedfiles)) { 2525 removeCompactedfiles(copyCompactedfiles); 2526 } 2527 } finally { 2528 archiveLock.unlock(); 2529 } 2530 } 2531 2532 /** 2533 * Archives and removes the compacted files 2534 * @param compactedfiles The compacted files in this store that are not active in reads 2535 * @throws IOException 2536 */ 2537 private void removeCompactedfiles(Collection<HStoreFile> compactedfiles) 2538 throws IOException { 2539 final List<HStoreFile> filesToRemove = new ArrayList<>(compactedfiles.size()); 2540 for (final HStoreFile file : compactedfiles) { 2541 synchronized (file) { 2542 try { 2543 StoreFileReader r = file.getReader(); 2544 if (r == null) { 2545 LOG.debug("The file {} was closed but still not archived", file); 2546 filesToRemove.add(file); 2547 continue; 2548 } 2549 if (file.isCompactedAway() && !file.isReferencedInReads()) { 2550 // Even if deleting fails we need not bother as any new scanners won't be 2551 // able to use the compacted file as the status is already compactedAway 2552 LOG.trace("Closing and archiving the file {}", file); 2553 r.close(true); 2554 // Just close and return 2555 filesToRemove.add(file); 2556 } else { 2557 LOG.info("Can't archive compacted file " + file.getPath() 2558 + " because of either isCompactedAway = " + file.isCompactedAway() 2559 + " or file has reference, isReferencedInReads = " + file.isReferencedInReads() 2560 + ", skipping for now."); 2561 } 2562 } catch (Exception e) { 2563 LOG.error("Exception while trying to close the compacted store file {}", 2564 file.getPath(), e); 2565 } 2566 } 2567 } 2568 if (this.isPrimaryReplicaStore()) { 2569 // Only the primary region is allowed to move the file to archive. 2570 // The secondary region does not move the files to archive. Any active reads from 2571 // the secondary region will still work because the file as such has active readers on it. 2572 if (!filesToRemove.isEmpty()) { 2573 LOG.debug("Moving the files {} to archive", filesToRemove); 2574 // Only if this is successful it has to be removed 2575 try { 2576 this.fs.removeStoreFiles(this.getColumnFamilyDescriptor().getNameAsString(), filesToRemove); 2577 } catch (FailedArchiveException fae) { 2578 // Even if archiving some files failed, we still need to clear out any of the 2579 // files which were successfully archived. Otherwise we will receive a 2580 // FileNotFoundException when we attempt to re-archive them in the next go around. 2581 Collection<Path> failedFiles = fae.getFailedFiles(); 2582 Iterator<HStoreFile> iter = filesToRemove.iterator(); 2583 while (iter.hasNext()) { 2584 if (failedFiles.contains(iter.next().getPath())) { 2585 iter.remove(); 2586 } 2587 } 2588 if (!filesToRemove.isEmpty()) { 2589 clearCompactedfiles(filesToRemove); 2590 } 2591 throw fae; 2592 } 2593 } 2594 } 2595 if (!filesToRemove.isEmpty()) { 2596 // Clear the compactedfiles from the store file manager 2597 clearCompactedfiles(filesToRemove); 2598 } 2599 } 2600 2601 public Long preFlushSeqIDEstimation() { 2602 return memstore.preFlushSeqIDEstimation(); 2603 } 2604 2605 @Override 2606 public boolean isSloppyMemStore() { 2607 return this.memstore.isSloppy(); 2608 } 2609 2610 private void clearCompactedfiles(List<HStoreFile> filesToRemove) throws IOException { 2611 LOG.trace("Clearing the compacted file {} from this store", filesToRemove); 2612 try { 2613 lock.writeLock().lock(); 2614 this.getStoreEngine().getStoreFileManager().removeCompactedFiles(filesToRemove); 2615 } finally { 2616 lock.writeLock().unlock(); 2617 } 2618 } 2619}