1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.hadoop.hbase.io.hfile;
21
22 import net.spy.memcached.CachedData;
23 import net.spy.memcached.ConnectionFactoryBuilder;
24 import net.spy.memcached.FailureMode;
25 import net.spy.memcached.MemcachedClient;
26 import net.spy.memcached.transcoders.Transcoder;
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29 import org.apache.hadoop.conf.Configuration;
30 import org.apache.hadoop.hbase.HConstants;
31 import org.apache.hadoop.hbase.classification.InterfaceAudience;
32 import org.apache.hadoop.hbase.util.Addressing;
33 import org.apache.htrace.Trace;
34 import org.apache.htrace.TraceScope;
35
36 import java.io.IOException;
37 import java.net.InetSocketAddress;
38 import java.nio.ByteBuffer;
39 import java.util.ArrayList;
40 import java.util.Iterator;
41 import java.util.List;
42 import java.util.NoSuchElementException;
43 import java.util.concurrent.ExecutionException;
44
45
46
47
48
49
50
51 @InterfaceAudience.Private
52 public class MemcachedBlockCache implements BlockCache {
53 private static final Log LOG = LogFactory.getLog(MemcachedBlockCache.class.getName());
54
55
56
57 public static final int MAX_SIZE = 1020 * 1024;
58
59
60
61
62
63
64 public static final String MEMCACHED_CONFIG_KEY = "hbase.cache.memcached.servers";
65 public static final String MEMCACHED_TIMEOUT_KEY = "hbase.cache.memcached.timeout";
66 public static final String MEMCACHED_OPTIMEOUT_KEY = "hbase.cache.memcached.optimeout";
67 public static final String MEMCACHED_OPTIMIZE_KEY = "hbase.cache.memcached.spy.optimze";
68 public static final long MEMCACHED_DEFAULT_TIMEOUT = 500;
69 public static final boolean MEMCACHED_OPTIMIZE_DEFAULT = false;
70
71 private final MemcachedClient client;
72 private final HFileBlockTranscoder tc = new HFileBlockTranscoder();
73 private final CacheStats cacheStats = new CacheStats("MemcachedBlockCache");
74
75 public MemcachedBlockCache(Configuration c) throws IOException {
76 LOG.info("Creating MemcachedBlockCache");
77
78 long opTimeout = c.getLong(MEMCACHED_OPTIMEOUT_KEY, MEMCACHED_DEFAULT_TIMEOUT);
79 long queueTimeout = c.getLong(MEMCACHED_TIMEOUT_KEY, opTimeout + MEMCACHED_DEFAULT_TIMEOUT);
80 boolean optimize = c.getBoolean(MEMCACHED_OPTIMIZE_KEY, MEMCACHED_OPTIMIZE_DEFAULT);
81
82 ConnectionFactoryBuilder builder = new ConnectionFactoryBuilder()
83 .setOpTimeout(opTimeout)
84 .setOpQueueMaxBlockTime(queueTimeout)
85 .setFailureMode(FailureMode.Redistribute)
86 .setShouldOptimize(optimize)
87 .setDaemon(true)
88 .setUseNagleAlgorithm(false)
89 .setReadBufferSize(HConstants.DEFAULT_BLOCKSIZE * 4 * 1024);
90
91
92
93
94
95
96
97 String serverListString = c.get(MEMCACHED_CONFIG_KEY,"localhost:11211");
98 String[] servers = serverListString.split(",");
99
100
101
102 List<InetSocketAddress> serverAddresses = new ArrayList<InetSocketAddress>(servers.length);
103 for (String s:servers) {
104 serverAddresses.add(Addressing.createInetSocketAddressFromHostAndPortStr(s));
105 }
106
107 client = new MemcachedClient(builder.build(), serverAddresses);
108 }
109
110 @Override
111 public void cacheBlock(BlockCacheKey cacheKey,
112 Cacheable buf,
113 boolean inMemory,
114 boolean cacheDataInL1) {
115 cacheBlock(cacheKey, buf);
116 }
117
118 @Override
119 public void cacheBlock(BlockCacheKey cacheKey, Cacheable buf) {
120 if (buf instanceof HFileBlock) {
121 client.add(cacheKey.toString(), MAX_SIZE, (HFileBlock) buf, tc);
122 } else {
123 if (LOG.isDebugEnabled()) {
124 LOG.debug("MemcachedBlockCache can not cache Cacheable's of type "
125 + buf.getClass().toString());
126 }
127 }
128 }
129
130 @Override
131 public Cacheable getBlock(BlockCacheKey cacheKey, boolean caching,
132 boolean repeat, boolean updateCacheMetrics) {
133
134 HFileBlock result = null;
135
136 try (TraceScope traceScope = Trace.startSpan("MemcachedBlockCache.getBlock")) {
137 result = client.get(cacheKey.toString(), tc);
138 } catch (Exception e) {
139
140
141 if (LOG.isDebugEnabled()) {
142 LOG.debug("Exception pulling from memcached [ "
143 + cacheKey.toString()
144 + " ]. Treating as a miss.", e);
145 }
146 result = null;
147 } finally {
148
149 if (updateCacheMetrics) {
150 if (result == null) {
151 cacheStats.miss(caching, cacheKey.isPrimary(), cacheKey.getBlockType());
152 } else {
153 cacheStats.hit(caching, cacheKey.isPrimary(), cacheKey.getBlockType());
154 }
155 }
156 }
157
158 return result;
159 }
160
161 @Override
162 public boolean evictBlock(BlockCacheKey cacheKey) {
163 try {
164 cacheStats.evict();
165 return client.delete(cacheKey.toString()).get();
166 } catch (InterruptedException e) {
167 LOG.warn("Error deleting " + cacheKey.toString(), e);
168 Thread.currentThread().interrupt();
169 } catch (ExecutionException e) {
170 if (LOG.isDebugEnabled()) {
171 LOG.debug("Error deleting " + cacheKey.toString(), e);
172 }
173 }
174 return false;
175 }
176
177
178
179
180 @Override
181 public int evictBlocksByHfileName(String hfileName) {
182 return 0;
183 }
184
185 @Override
186 public CacheStats getStats() {
187 return cacheStats;
188 }
189
190 @Override
191 public void shutdown() {
192 client.shutdown();
193 }
194
195 @Override
196 public long size() {
197 return 0;
198 }
199
200 @Override
201 public long getMaxSize() {
202 return 0;
203 }
204
205 @Override
206 public long getFreeSize() {
207 return 0;
208 }
209
210 @Override
211 public long getCurrentSize() {
212 return 0;
213 }
214
215 @Override
216 public long getCurrentDataSize() {
217 return 0;
218 }
219
220 @Override
221 public long getBlockCount() {
222 return 0;
223 }
224
225 @Override
226 public long getDataBlockCount() {
227 return 0;
228 }
229
230 @Override
231 public Iterator<CachedBlock> iterator() {
232 return new Iterator<CachedBlock>() {
233 @Override
234 public boolean hasNext() {
235 return false;
236 }
237
238 @Override
239 public CachedBlock next() {
240 throw new NoSuchElementException("MemcachedBlockCache can't iterate over blocks.");
241 }
242
243 @Override
244 public void remove() {
245
246 }
247 };
248 }
249
250 @Override
251 public BlockCache[] getBlockCaches() {
252 return null;
253 }
254
255
256
257
258 private static class HFileBlockTranscoder implements Transcoder<HFileBlock> {
259
260 @Override
261 public boolean asyncDecode(CachedData d) {
262 return false;
263 }
264
265 @Override
266 public CachedData encode(HFileBlock block) {
267 ByteBuffer bb = ByteBuffer.allocate(block.getSerializedLength());
268 block.serialize(bb, true);
269 return new CachedData(0, bb.array(), CachedData.MAX_SIZE);
270 }
271
272 @Override
273 public HFileBlock decode(CachedData d) {
274 try {
275 ByteBuffer buf = ByteBuffer.wrap(d.getData());
276 return (HFileBlock) HFileBlock.BLOCK_DESERIALIZER.deserialize(buf, true);
277 } catch (IOException e) {
278 LOG.warn("Error deserializing data from memcached",e);
279 }
280 return null;
281 }
282
283 @Override
284 public int getMaxSize() {
285 return MAX_SIZE;
286 }
287 }
288
289 }