1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.io.hfile.slab;
20
21 import java.nio.ByteBuffer;
22 import java.util.Iterator;
23 import java.util.concurrent.ConcurrentMap;
24 import java.util.concurrent.atomic.AtomicLong;
25
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28 import org.apache.hadoop.classification.InterfaceAudience;
29 import org.apache.hadoop.hbase.io.HeapSize;
30 import org.apache.hadoop.hbase.io.hfile.BlockCache;
31 import org.apache.hadoop.hbase.io.hfile.BlockCacheKey;
32 import org.apache.hadoop.hbase.io.hfile.CacheStats;
33 import org.apache.hadoop.hbase.io.hfile.Cacheable;
34 import org.apache.hadoop.hbase.io.hfile.CacheableDeserializer;
35 import org.apache.hadoop.hbase.io.hfile.CachedBlock;
36 import org.apache.hadoop.hbase.util.Bytes;
37 import org.apache.hadoop.hbase.util.ClassSize;
38 import org.apache.hadoop.util.StringUtils;
39
40 import com.google.common.cache.CacheBuilder;
41 import com.google.common.cache.RemovalListener;
42 import com.google.common.cache.RemovalNotification;
43
44
45
46
47
48
49
50
51
52
53
54
55
56 @InterfaceAudience.Private
57 @Deprecated
58 public class SingleSizeCache implements BlockCache, HeapSize {
59 private final Slab backingStore;
60 private final ConcurrentMap<BlockCacheKey, CacheablePair> backingMap;
61 private final int numBlocks;
62 private final int blockSize;
63 private final CacheStats stats;
64 private final SlabItemActionWatcher actionWatcher;
65 private final AtomicLong size;
66 private final AtomicLong timeSinceLastAccess;
67 public final static long CACHE_FIXED_OVERHEAD = ClassSize
68 .align((2 * Bytes.SIZEOF_INT) + (5 * ClassSize.REFERENCE)
69 + +ClassSize.OBJECT);
70
71 static final Log LOG = LogFactory.getLog(SingleSizeCache.class);
72
73
74
75
76
77
78
79
80
81
82
83
84 public SingleSizeCache(int blockSize, int numBlocks,
85 SlabItemActionWatcher master) {
86 this.blockSize = blockSize;
87 this.numBlocks = numBlocks;
88 backingStore = new Slab(blockSize, numBlocks);
89 this.stats = new CacheStats();
90 this.actionWatcher = master;
91 this.size = new AtomicLong(CACHE_FIXED_OVERHEAD + backingStore.heapSize());
92 this.timeSinceLastAccess = new AtomicLong();
93
94
95
96 RemovalListener<BlockCacheKey, CacheablePair> listener =
97 new RemovalListener<BlockCacheKey, CacheablePair>() {
98 @Override
99 public void onRemoval(
100 RemovalNotification<BlockCacheKey, CacheablePair> notification) {
101 if (!notification.wasEvicted()) {
102
103
104 return;
105 }
106 CacheablePair value = notification.getValue();
107 timeSinceLastAccess.set(System.nanoTime()
108 - value.recentlyAccessed.get());
109 stats.evict();
110 doEviction(notification.getKey(), value);
111 }
112 };
113
114 backingMap = CacheBuilder.newBuilder()
115 .maximumSize(numBlocks - 1)
116 .removalListener(listener)
117 .<BlockCacheKey, CacheablePair>build()
118 .asMap();
119 }
120
121 @Override
122 public void cacheBlock(BlockCacheKey blockName, Cacheable toBeCached) {
123 ByteBuffer storedBlock;
124
125 try {
126 storedBlock = backingStore.alloc(toBeCached.getSerializedLength());
127 } catch (InterruptedException e) {
128 LOG.warn("SlabAllocator was interrupted while waiting for block to become available");
129 LOG.warn(e);
130 return;
131 }
132
133 CacheablePair newEntry = new CacheablePair(toBeCached.getDeserializer(),
134 storedBlock);
135 toBeCached.serialize(storedBlock);
136
137 synchronized (this) {
138 CacheablePair alreadyCached = backingMap.putIfAbsent(blockName, newEntry);
139
140 if (alreadyCached != null) {
141 backingStore.free(storedBlock);
142 throw new RuntimeException("already cached " + blockName);
143 }
144 if (actionWatcher != null) {
145 actionWatcher.onInsertion(blockName, this);
146 }
147 }
148 newEntry.recentlyAccessed.set(System.nanoTime());
149 this.size.addAndGet(newEntry.heapSize());
150 }
151
152 @Override
153 public Cacheable getBlock(BlockCacheKey key, boolean caching, boolean repeat,
154 boolean updateCacheMetrics) {
155 CacheablePair contentBlock = backingMap.get(key);
156 if (contentBlock == null) {
157 if (!repeat && updateCacheMetrics) stats.miss(caching);
158 return null;
159 }
160
161 if (updateCacheMetrics) stats.hit(caching);
162
163 try {
164 contentBlock.recentlyAccessed.set(System.nanoTime());
165 synchronized (contentBlock) {
166 if (contentBlock.serializedData == null) {
167
168 LOG.warn("Concurrent eviction of " + key);
169 return null;
170 }
171 return contentBlock.deserializer
172 .deserialize(contentBlock.serializedData.asReadOnlyBuffer());
173 }
174 } catch (Throwable t) {
175 LOG.error("Deserializer threw an exception. This may indicate a bug.", t);
176 return null;
177 }
178 }
179
180
181
182
183
184
185
186 public boolean evictBlock(BlockCacheKey key) {
187 stats.evict();
188 CacheablePair evictedBlock = backingMap.remove(key);
189
190 if (evictedBlock != null) {
191 doEviction(key, evictedBlock);
192 }
193 return evictedBlock != null;
194 }
195
196 private void doEviction(BlockCacheKey key, CacheablePair evictedBlock) {
197 long evictedHeap = 0;
198 synchronized (evictedBlock) {
199 if (evictedBlock.serializedData == null) {
200
201 return;
202 }
203 evictedHeap = evictedBlock.heapSize();
204 ByteBuffer bb = evictedBlock.serializedData;
205 evictedBlock.serializedData = null;
206 backingStore.free(bb);
207
208
209
210
211
212
213
214
215
216
217
218
219 if (actionWatcher != null) {
220 actionWatcher.onEviction(key, this);
221 }
222 }
223 stats.evicted();
224 size.addAndGet(-1 * evictedHeap);
225 }
226
227 public void logStats() {
228
229 long milliseconds = this.timeSinceLastAccess.get() / 1000000;
230
231 LOG.info("For Slab of size " + this.blockSize + ": "
232 + this.getOccupiedSize() / this.blockSize
233 + " occupied, out of a capacity of " + this.numBlocks
234 + " blocks. HeapSize is "
235 + StringUtils.humanReadableInt(this.heapSize()) + " bytes." + ", "
236 + "churnTime=" + StringUtils.formatTime(milliseconds));
237
238 LOG.info("Slab Stats: " + "accesses="
239 + stats.getRequestCount()
240 + ", "
241 + "hits="
242 + stats.getHitCount()
243 + ", "
244 + "hitRatio="
245 + (stats.getHitCount() == 0 ? "0" : (StringUtils.formatPercent(
246 stats.getHitRatio(), 2) + "%, "))
247 + "cachingAccesses="
248 + stats.getRequestCachingCount()
249 + ", "
250 + "cachingHits="
251 + stats.getHitCachingCount()
252 + ", "
253 + "cachingHitsRatio="
254 + (stats.getHitCachingCount() == 0 ? "0" : (StringUtils.formatPercent(
255 stats.getHitCachingRatio(), 2) + "%, ")) + "evictions="
256 + stats.getEvictionCount() + ", " + "evicted="
257 + stats.getEvictedCount() + ", " + "evictedPerRun="
258 + stats.evictedPerEviction());
259
260 }
261
262 public void shutdown() {
263 backingStore.shutdown();
264 }
265
266 public long heapSize() {
267 return this.size.get() + backingStore.heapSize();
268 }
269
270 public long size() {
271 return (long) this.blockSize * (long) this.numBlocks;
272 }
273
274 public long getFreeSize() {
275 return (long) backingStore.getBlocksRemaining() * (long) blockSize;
276 }
277
278 public long getOccupiedSize() {
279 return (long) (numBlocks - backingStore.getBlocksRemaining()) * (long) blockSize;
280 }
281
282 public long getEvictedCount() {
283 return stats.getEvictedCount();
284 }
285
286 public CacheStats getStats() {
287 return this.stats;
288 }
289
290 @Override
291 public long getBlockCount() {
292 return numBlocks - backingStore.getBlocksRemaining();
293 }
294
295
296 @Override
297 public void cacheBlock(BlockCacheKey cacheKey, Cacheable buf, boolean inMemory) {
298 this.cacheBlock(cacheKey, buf);
299 }
300
301
302
303
304
305 @Override
306 public int evictBlocksByHfileName(String hfileName) {
307 int evictedCount = 0;
308 for (BlockCacheKey e : backingMap.keySet()) {
309 if (e.getHfileName().equals(hfileName)) {
310 this.evictBlock(e);
311 }
312 }
313 return evictedCount;
314 }
315
316 @Override
317 public long getCurrentSize() {
318 return 0;
319 }
320
321
322 private static class CacheablePair implements HeapSize {
323 final CacheableDeserializer<Cacheable> deserializer;
324 ByteBuffer serializedData;
325 AtomicLong recentlyAccessed;
326
327 private CacheablePair(CacheableDeserializer<Cacheable> deserializer,
328 ByteBuffer serializedData) {
329 this.recentlyAccessed = new AtomicLong();
330 this.deserializer = deserializer;
331 this.serializedData = serializedData;
332 }
333
334
335
336
337
338
339 @Override
340 public long heapSize() {
341 return ClassSize.align(ClassSize.OBJECT + ClassSize.REFERENCE * 3
342 + ClassSize.ATOMIC_LONG);
343 }
344 }
345
346 @Override
347 public Iterator<CachedBlock> iterator() {
348 return null;
349 }
350
351 @Override
352 public BlockCache[] getBlockCaches() {
353 return null;
354 }
355 }