]>
Commit | Line | Data |
---|---|---|
20effc67 TL |
1 | // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
2 | // This source code is licensed under both the GPLv2 (found in the | |
3 | // COPYING file in the root directory) and Apache 2.0 License | |
4 | // (found in the LICENSE.Apache file in the root directory). | |
5 | // | |
6 | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. | |
7 | // Use of this source code is governed by a BSD-style license that can be | |
8 | // found in the LICENSE file. See the AUTHORS file for names of contributors. | |
9 | #pragma once | |
10 | #include "table/block_based/block_based_table_reader.h" | |
20effc67 TL |
11 | #include "table/block_based/reader_common.h" |
12 | ||
13 | // The file contains some member functions of BlockBasedTable that | |
14 | // cannot be implemented in block_based_table_reader.cc because | |
15 | // it's called by other files (e.g. block_based_iterator.h) and | |
16 | // are templates. | |
17 | ||
18 | namespace ROCKSDB_NAMESPACE { | |
19 | // Convert an index iterator value (i.e., an encoded BlockHandle) | |
20 | // into an iterator over the contents of the corresponding block. | |
21 | // If input_iter is null, new a iterator | |
22 | // If input_iter is not null, update this iter and return it | |
23 | template <typename TBlockIter> | |
24 | TBlockIter* BlockBasedTable::NewDataBlockIterator( | |
25 | const ReadOptions& ro, const BlockHandle& handle, TBlockIter* input_iter, | |
26 | BlockType block_type, GetContext* get_context, | |
1e59de90 TL |
27 | BlockCacheLookupContext* lookup_context, |
28 | FilePrefetchBuffer* prefetch_buffer, bool for_compaction, bool async_read, | |
29 | Status& s) const { | |
20effc67 TL |
30 | PERF_TIMER_GUARD(new_table_block_iter_nanos); |
31 | ||
32 | TBlockIter* iter = input_iter != nullptr ? input_iter : new TBlockIter; | |
33 | if (!s.ok()) { | |
34 | iter->Invalidate(s); | |
35 | return iter; | |
36 | } | |
37 | ||
1e59de90 TL |
38 | CachableEntry<Block> block; |
39 | if (rep_->uncompression_dict_reader && block_type == BlockType::kData) { | |
40 | CachableEntry<UncompressionDict> uncompression_dict; | |
20effc67 TL |
41 | const bool no_io = (ro.read_tier == kBlockCacheTier); |
42 | s = rep_->uncompression_dict_reader->GetOrReadUncompressionDictionary( | |
1e59de90 TL |
43 | prefetch_buffer, no_io, ro.verify_checksums, get_context, |
44 | lookup_context, &uncompression_dict); | |
20effc67 TL |
45 | if (!s.ok()) { |
46 | iter->Invalidate(s); | |
47 | return iter; | |
48 | } | |
1e59de90 TL |
49 | const UncompressionDict& dict = uncompression_dict.GetValue() |
50 | ? *uncompression_dict.GetValue() | |
51 | : UncompressionDict::GetEmptyDict(); | |
52 | s = RetrieveBlock(prefetch_buffer, ro, handle, dict, &block, block_type, | |
53 | get_context, lookup_context, for_compaction, | |
54 | /* use_cache */ true, /* wait_for_cache */ true, | |
55 | async_read); | |
56 | } else { | |
57 | s = RetrieveBlock( | |
58 | prefetch_buffer, ro, handle, UncompressionDict::GetEmptyDict(), &block, | |
59 | block_type, get_context, lookup_context, for_compaction, | |
60 | /* use_cache */ true, /* wait_for_cache */ true, async_read); | |
20effc67 TL |
61 | } |
62 | ||
1e59de90 TL |
63 | if (s.IsTryAgain() && async_read) { |
64 | return iter; | |
65 | } | |
20effc67 TL |
66 | |
67 | if (!s.ok()) { | |
68 | assert(block.IsEmpty()); | |
69 | iter->Invalidate(s); | |
70 | return iter; | |
71 | } | |
72 | ||
73 | assert(block.GetValue() != nullptr); | |
74 | ||
75 | // Block contents are pinned and it is still pinned after the iterator | |
76 | // is destroyed as long as cleanup functions are moved to another object, | |
77 | // when: | |
78 | // 1. block cache handle is set to be released in cleanup function, or | |
79 | // 2. it's pointing to immortal source. If own_bytes is true then we are | |
80 | // not reading data from the original source, whether immortal or not. | |
81 | // Otherwise, the block is pinned iff the source is immortal. | |
82 | const bool block_contents_pinned = | |
83 | block.IsCached() || | |
84 | (!block.GetValue()->own_bytes() && rep_->immortal_table); | |
85 | iter = InitBlockIterator<TBlockIter>(rep_, block.GetValue(), block_type, iter, | |
86 | block_contents_pinned); | |
87 | ||
88 | if (!block.IsCached()) { | |
1e59de90 | 89 | if (!ro.fill_cache) { |
20effc67 | 90 | Cache* const block_cache = rep_->table_options.block_cache.get(); |
1e59de90 TL |
91 | if (block_cache) { |
92 | // insert a dummy record to block cache to track the memory usage | |
93 | Cache::Handle* cache_handle = nullptr; | |
94 | CacheKey key = CacheKey::CreateUniqueForCacheLifetime(block_cache); | |
95 | s = block_cache->Insert(key.AsSlice(), nullptr, | |
96 | block.GetValue()->ApproximateMemoryUsage(), | |
97 | nullptr, &cache_handle); | |
98 | ||
99 | if (s.ok()) { | |
100 | assert(cache_handle != nullptr); | |
101 | iter->RegisterCleanup(&ForceReleaseCachedEntry, block_cache, | |
102 | cache_handle); | |
103 | } | |
20effc67 TL |
104 | } |
105 | } | |
106 | } else { | |
107 | iter->SetCacheHandle(block.GetCacheHandle()); | |
108 | } | |
109 | ||
110 | block.TransferTo(iter); | |
111 | ||
112 | return iter; | |
113 | } | |
114 | ||
115 | // Convert an uncompressed data block (i.e CachableEntry<Block>) | |
116 | // into an iterator over the contents of the corresponding block. | |
117 | // If input_iter is null, new a iterator | |
118 | // If input_iter is not null, update this iter and return it | |
119 | template <typename TBlockIter> | |
120 | TBlockIter* BlockBasedTable::NewDataBlockIterator(const ReadOptions& ro, | |
121 | CachableEntry<Block>& block, | |
122 | TBlockIter* input_iter, | |
123 | Status s) const { | |
124 | PERF_TIMER_GUARD(new_table_block_iter_nanos); | |
125 | ||
126 | TBlockIter* iter = input_iter != nullptr ? input_iter : new TBlockIter; | |
127 | if (!s.ok()) { | |
128 | iter->Invalidate(s); | |
129 | return iter; | |
130 | } | |
131 | ||
132 | assert(block.GetValue() != nullptr); | |
133 | // Block contents are pinned and it is still pinned after the iterator | |
134 | // is destroyed as long as cleanup functions are moved to another object, | |
135 | // when: | |
136 | // 1. block cache handle is set to be released in cleanup function, or | |
137 | // 2. it's pointing to immortal source. If own_bytes is true then we are | |
138 | // not reading data from the original source, whether immortal or not. | |
139 | // Otherwise, the block is pinned iff the source is immortal. | |
140 | const bool block_contents_pinned = | |
141 | block.IsCached() || | |
142 | (!block.GetValue()->own_bytes() && rep_->immortal_table); | |
143 | iter = InitBlockIterator<TBlockIter>(rep_, block.GetValue(), BlockType::kData, | |
144 | iter, block_contents_pinned); | |
145 | ||
146 | if (!block.IsCached()) { | |
1e59de90 | 147 | if (!ro.fill_cache) { |
20effc67 | 148 | Cache* const block_cache = rep_->table_options.block_cache.get(); |
1e59de90 TL |
149 | if (block_cache) { |
150 | // insert a dummy record to block cache to track the memory usage | |
151 | Cache::Handle* cache_handle = nullptr; | |
152 | CacheKey key = CacheKey::CreateUniqueForCacheLifetime(block_cache); | |
153 | s = block_cache->Insert(key.AsSlice(), nullptr, | |
154 | block.GetValue()->ApproximateMemoryUsage(), | |
155 | nullptr, &cache_handle); | |
156 | ||
157 | if (s.ok()) { | |
158 | assert(cache_handle != nullptr); | |
159 | iter->RegisterCleanup(&ForceReleaseCachedEntry, block_cache, | |
160 | cache_handle); | |
161 | } | |
20effc67 TL |
162 | } |
163 | } | |
164 | } else { | |
165 | iter->SetCacheHandle(block.GetCacheHandle()); | |
166 | } | |
167 | ||
168 | block.TransferTo(iter); | |
169 | return iter; | |
170 | } | |
171 | } // namespace ROCKSDB_NAMESPACE |