| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204 |
- // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
- // This source code is licensed under both the GPLv2 (found in the
- // COPYING file in the root directory) and Apache 2.0 License
- // (found in the LICENSE.Apache file in the root directory).
- //
- // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file. See the AUTHORS file for names of contributors.
- #pragma once
- #include <type_traits>
- #include "block.h"
- #include "block_cache.h"
- #include "table/block_based/block_based_table_reader.h"
- #include "table/block_based/reader_common.h"
- // The file contains some member functions of BlockBasedTable that
- // cannot be implemented in block_based_table_reader.cc because
- // it's called by other files (e.g. block_based_iterator.h) and
- // are templates.
- namespace ROCKSDB_NAMESPACE {
- namespace {
- using IterPlaceholderCacheInterface =
- PlaceholderCacheInterface<CacheEntryRole::kMisc>;
- template <typename TBlockIter>
- struct IterTraits {};
- template <>
- struct IterTraits<DataBlockIter> {
- using IterBlocklike = Block_kData;
- };
- template <>
- struct IterTraits<IndexBlockIter> {
- using IterBlocklike = Block_kIndex;
- };
- } // namespace
- // Convert an index iterator value (i.e., an encoded BlockHandle)
- // into an iterator over the contents of the corresponding block.
- // If input_iter is null, new a iterator
- // If input_iter is not null, update this iter and return it
- template <typename TBlockIter>
- TBlockIter* BlockBasedTable::NewDataBlockIterator(
- const ReadOptions& ro, const BlockHandle& handle, TBlockIter* input_iter,
- BlockType block_type, GetContext* get_context,
- BlockCacheLookupContext* lookup_context,
- FilePrefetchBuffer* prefetch_buffer, bool for_compaction, bool async_read,
- Status& s, bool use_block_cache_for_lookup) const {
- using IterBlocklike = typename IterTraits<TBlockIter>::IterBlocklike;
- PERF_TIMER_GUARD(new_table_block_iter_nanos);
- TBlockIter* iter = input_iter != nullptr ? input_iter : new TBlockIter;
- if (!s.ok()) {
- iter->Invalidate(s);
- return iter;
- }
- CachableEntry<Block> block;
- {
- CachableEntry<DecompressorDict> dict;
- Decompressor* decomp = rep_->decompressor.get();
- if (rep_->uncompression_dict_reader && block_type == BlockType::kData) {
- // For async scans, don't use the prefetch buffer since an async prefetch
- // might already be under way and this would invalidate it. Also, the
- // uncompression dict is typically at the end of the file and would
- // most likely break the sequentiality of the access pattern.
- // Same is with auto_readahead_size. It iterates over index to lookup for
- // data blocks. And this could break the the sequentiality of the access
- // pattern.
- s = rep_->uncompression_dict_reader->GetOrReadUncompressionDictionary(
- ((ro.async_io || ro.auto_readahead_size) ? nullptr : prefetch_buffer),
- ro, get_context, lookup_context, &dict);
- if (!s.ok()) {
- iter->Invalidate(s);
- return iter;
- }
- assert(dict.GetValue());
- if (dict.GetValue()) {
- decomp = dict.GetValue()->decompressor_.get();
- }
- }
- s = RetrieveBlock(
- prefetch_buffer, ro, handle, decomp, &block.As<IterBlocklike>(),
- get_context, lookup_context, for_compaction,
- /* use_cache */ true, async_read, use_block_cache_for_lookup);
- }
- if (s.IsTryAgain() && async_read) {
- return iter;
- }
- if (!s.ok()) {
- assert(block.IsEmpty());
- iter->Invalidate(s);
- return iter;
- }
- assert(block.GetValue() != nullptr);
- // Block contents are pinned and it is still pinned after the iterator
- // is destroyed as long as cleanup functions are moved to another object,
- // when:
- // 1. block cache handle is set to be released in cleanup function, or
- // 2. it's pointing to immortal source. If own_bytes is true then we are
- // not reading data from the original source, whether immortal or not.
- // Otherwise, the block is pinned iff the source is immortal.
- const bool block_contents_pinned =
- block.IsCached() ||
- (!block.GetValue()->own_bytes() && rep_->immortal_table);
- iter = InitBlockIterator<TBlockIter>(rep_, block.GetValue(), block_type, iter,
- block_contents_pinned);
- if (!block.IsCached()) {
- if (!ro.fill_cache) {
- IterPlaceholderCacheInterface block_cache{
- rep_->table_options.block_cache.get()};
- if (block_cache) {
- // insert a dummy record to block cache to track the memory usage
- Cache::Handle* cache_handle = nullptr;
- CacheKey key =
- CacheKey::CreateUniqueForCacheLifetime(block_cache.get());
- s = block_cache.Insert(key.AsSlice(),
- block.GetValue()->ApproximateMemoryUsage(),
- &cache_handle);
- if (s.ok()) {
- assert(cache_handle != nullptr);
- iter->RegisterCleanup(&ForceReleaseCachedEntry, block_cache.get(),
- cache_handle);
- }
- }
- }
- } else {
- iter->SetCacheHandle(block.GetCacheHandle());
- }
- block.TransferTo(iter);
- return iter;
- }
- // Convert an uncompressed data block (i.e CachableEntry<Block>)
- // into an iterator over the contents of the corresponding block.
- // If input_iter is null, new a iterator
- // If input_iter is not null, update this iter and return it
- template <typename TBlockIter>
- TBlockIter* BlockBasedTable::NewDataBlockIterator(const ReadOptions& ro,
- CachableEntry<Block>& block,
- TBlockIter* input_iter,
- Status s) const {
- PERF_TIMER_GUARD(new_table_block_iter_nanos);
- TBlockIter* iter = input_iter != nullptr ? input_iter : new TBlockIter;
- if (!s.ok()) {
- iter->Invalidate(s);
- return iter;
- }
- assert(block.GetValue() != nullptr);
- // Block contents are pinned and it is still pinned after the iterator
- // is destroyed as long as cleanup functions are moved to another object,
- // when:
- // 1. block cache handle is set to be released in cleanup function, or
- // 2. it's pointing to immortal source. If own_bytes is true then we are
- // not reading data from the original source, whether immortal or not.
- // Otherwise, the block is pinned iff the source is immortal.
- const bool block_contents_pinned =
- block.IsCached() ||
- (!block.GetValue()->own_bytes() && rep_->immortal_table);
- iter = InitBlockIterator<TBlockIter>(rep_, block.GetValue(), BlockType::kData,
- iter, block_contents_pinned);
- if (!block.IsCached()) {
- if (!ro.fill_cache) {
- IterPlaceholderCacheInterface block_cache{
- rep_->table_options.block_cache.get()};
- if (block_cache) {
- // insert a dummy record to block cache to track the memory usage
- Cache::Handle* cache_handle = nullptr;
- CacheKey key =
- CacheKey::CreateUniqueForCacheLifetime(block_cache.get());
- s = block_cache.Insert(key.AsSlice(),
- block.GetValue()->ApproximateMemoryUsage(),
- &cache_handle);
- if (s.ok()) {
- assert(cache_handle != nullptr);
- iter->RegisterCleanup(&ForceReleaseCachedEntry, block_cache.get(),
- cache_handle);
- }
- }
- }
- } else {
- iter->SetCacheHandle(block.GetCacheHandle());
- }
- block.TransferTo(iter);
- return iter;
- }
- } // namespace ROCKSDB_NAMESPACE
|