1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.client;
20
21 import java.io.IOException;
22 import java.net.UnknownHostException;
23
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26 import org.apache.hadoop.hbase.classification.InterfaceAudience;
27 import org.apache.hadoop.conf.Configuration;
28 import org.apache.hadoop.hbase.Cell;
29 import org.apache.hadoop.hbase.CellScanner;
30 import org.apache.hadoop.hbase.DoNotRetryIOException;
31 import org.apache.hadoop.hbase.HRegionInfo;
32 import org.apache.hadoop.hbase.HRegionLocation;
33 import org.apache.hadoop.hbase.KeyValueUtil;
34 import org.apache.hadoop.hbase.NotServingRegionException;
35 import org.apache.hadoop.hbase.RemoteExceptionHandler;
36 import org.apache.hadoop.hbase.TableName;
37 import org.apache.hadoop.hbase.UnknownScannerException;
38 import org.apache.hadoop.hbase.client.metrics.ScanMetrics;
39 import org.apache.hadoop.hbase.ipc.PayloadCarryingRpcController;
40 import org.apache.hadoop.hbase.ipc.RpcControllerFactory;
41 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
42 import org.apache.hadoop.hbase.protobuf.RequestConverter;
43 import org.apache.hadoop.hbase.protobuf.ResponseConverter;
44 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.ScanRequest;
45 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.ScanResponse;
46 import org.apache.hadoop.hbase.regionserver.RegionServerStoppedException;
47 import org.apache.hadoop.ipc.RemoteException;
48 import org.apache.hadoop.net.DNS;
49
50 import com.google.protobuf.ServiceException;
51 import com.google.protobuf.TextFormat;
52
53
54
55
56
57
58 @InterfaceAudience.Private
59 public class ScannerCallable extends RegionServerCallable<Result[]> {
60 public static final String LOG_SCANNER_LATENCY_CUTOFF
61 = "hbase.client.log.scanner.latency.cutoff";
62 public static final String LOG_SCANNER_ACTIVITY = "hbase.client.log.scanner.activity";
63
64 public static final Log LOG = LogFactory.getLog(ScannerCallable.class);
65 private long scannerId = -1L;
66 protected boolean instantiated = false;
67 protected boolean closed = false;
68 protected boolean renew = false;
69 private Scan scan;
70 private int caching = 1;
71 protected ScanMetrics scanMetrics;
72 private boolean logScannerActivity = false;
73 private int logCutOffLatency = 1000;
74 private static String myAddress;
75 static {
76 try {
77 myAddress = DNS.getDefaultHost("default", "default");
78 } catch (UnknownHostException uhe) {
79 LOG.error("cannot determine my address", uhe);
80 }
81 }
82
83
84 protected boolean isRegionServerRemote = true;
85 private long nextCallSeq = 0;
86 protected PayloadCarryingRpcController controller;
87
88
89
90
91
92
93
94
95
96 public ScannerCallable (HConnection connection, TableName tableName, Scan scan,
97 ScanMetrics scanMetrics, PayloadCarryingRpcController controller) {
98 super(connection, tableName, scan.getStartRow());
99 this.scan = scan;
100 this.scanMetrics = scanMetrics;
101 Configuration conf = connection.getConfiguration();
102 logScannerActivity = conf.getBoolean(LOG_SCANNER_ACTIVITY, false);
103 logCutOffLatency = conf.getInt(LOG_SCANNER_LATENCY_CUTOFF, 1000);
104 this.controller = controller;
105 }
106
107
108
109
110
111 @Deprecated
112 public ScannerCallable (HConnection connection, final byte [] tableName, Scan scan,
113 ScanMetrics scanMetrics) {
114 this(connection, TableName.valueOf(tableName), scan, scanMetrics, RpcControllerFactory
115 .instantiate(connection.getConfiguration()).newController());
116 }
117
118
119
120
121
122 @Override
123 public void prepare(boolean reload) throws IOException {
124 if (!instantiated || reload) {
125 super.prepare(reload);
126 checkIfRegionServerIsRemote();
127 instantiated = true;
128 }
129
130
131
132
133 if (reload && this.scanMetrics != null) {
134 this.scanMetrics.countOfRPCRetries.incrementAndGet();
135 if (isRegionServerRemote) {
136 this.scanMetrics.countOfRemoteRPCRetries.incrementAndGet();
137 }
138 }
139 }
140
141
142
143
144
145 protected void checkIfRegionServerIsRemote() {
146 if (getLocation().getHostname().equalsIgnoreCase(myAddress)) {
147 isRegionServerRemote = false;
148 } else {
149 isRegionServerRemote = true;
150 }
151 }
152
153
154
155
156 @SuppressWarnings("deprecation")
157 public Result [] call() throws IOException {
158 if (controller == null) {
159 controller = RpcControllerFactory.instantiate(connection.getConfiguration())
160 .newController();
161 }
162 if (closed) {
163 if (scannerId != -1) {
164 close();
165 }
166 } else {
167 if (scannerId == -1L) {
168 this.scannerId = openScanner();
169 } else {
170 Result [] rrs = null;
171 ScanRequest request = null;
172 try {
173 incRPCcallsMetrics();
174 request =
175 RequestConverter.buildScanRequest(scannerId, caching, false, nextCallSeq, renew);
176 ScanResponse response = null;
177 try {
178 controller.setPriority(getTableName());
179 response = getStub().scan(controller, request);
180
181
182
183
184
185
186
187
188
189 nextCallSeq++;
190 long timestamp = System.currentTimeMillis();
191
192 CellScanner cellScanner = controller.cellScanner();
193 rrs = ResponseConverter.getResults(cellScanner, response);
194 if (logScannerActivity) {
195 long now = System.currentTimeMillis();
196 if (now - timestamp > logCutOffLatency) {
197 int rows = rrs == null ? 0 : rrs.length;
198 LOG.info("Took " + (now-timestamp) + "ms to fetch "
199 + rows + " rows from scanner=" + scannerId);
200 }
201 }
202
203 if (response.hasMoreResults() && !response.getMoreResults()) {
204 scannerId = -1L;
205 closed = true;
206
207 return null;
208 }
209
210
211 if (response.hasMoreResultsInRegion()) {
212
213 setHasMoreResultsContext(true);
214 setServerHasMoreResults(response.getMoreResultsInRegion());
215 } else {
216
217 setHasMoreResultsContext(false);
218 }
219 } catch (ServiceException se) {
220 throw ProtobufUtil.getRemoteException(se);
221 }
222 updateResultsMetrics(rrs);
223 } catch (IOException e) {
224 if (logScannerActivity) {
225 LOG.info("Got exception making request " + TextFormat.shortDebugString(request)
226 + " to " + getLocation(), e);
227 }
228 IOException ioe = e;
229 if (e instanceof RemoteException) {
230 ioe = RemoteExceptionHandler.decodeRemoteException((RemoteException)e);
231 }
232 if (logScannerActivity && (ioe instanceof UnknownScannerException)) {
233 try {
234 HRegionLocation location =
235 getConnection().relocateRegion(getTableName(), scan.getStartRow());
236 LOG.info("Scanner=" + scannerId
237 + " expired, current region location is " + location.toString());
238 } catch (Throwable t) {
239 LOG.info("Failed to relocate region", t);
240 }
241 }
242
243
244
245
246
247
248 if (ioe instanceof NotServingRegionException) {
249
250
251
252 if (this.scanMetrics != null) {
253 this.scanMetrics.countOfNSRE.incrementAndGet();
254 }
255 throw new DoNotRetryIOException("Resetting the scanner -- see exception cause", ioe);
256 } else if (ioe instanceof RegionServerStoppedException) {
257
258
259 throw new DoNotRetryIOException("Resetting the scanner -- see exception cause", ioe);
260 } else {
261
262 throw ioe;
263 }
264 }
265 return rrs;
266 }
267 }
268 return null;
269 }
270
271 private void incRPCcallsMetrics() {
272 if (this.scanMetrics == null) {
273 return;
274 }
275 this.scanMetrics.countOfRPCcalls.incrementAndGet();
276 if (isRegionServerRemote) {
277 this.scanMetrics.countOfRemoteRPCcalls.incrementAndGet();
278 }
279 }
280
281 protected void updateResultsMetrics(Result[] rrs) {
282 if (this.scanMetrics == null || rrs == null || rrs.length == 0) {
283 return;
284 }
285 long resultSize = 0;
286 for (Result rr : rrs) {
287 for (Cell kv : rr.rawCells()) {
288
289 resultSize += KeyValueUtil.ensureKeyValue(kv).getLength();
290 }
291 }
292 this.scanMetrics.countOfBytesInResults.addAndGet(resultSize);
293 if (isRegionServerRemote) {
294 this.scanMetrics.countOfBytesInRemoteResults.addAndGet(resultSize);
295 }
296 }
297
298 private void close() {
299 if (this.scannerId == -1L) {
300 return;
301 }
302 try {
303 incRPCcallsMetrics();
304 ScanRequest request =
305 RequestConverter.buildScanRequest(this.scannerId, 0, true);
306 try {
307 getStub().scan(controller, request);
308 } catch (ServiceException se) {
309 throw ProtobufUtil.getRemoteException(se);
310 }
311 } catch (IOException e) {
312 LOG.warn("Ignore, probably already closed", e);
313 }
314 this.scannerId = -1L;
315 }
316
317 protected long openScanner() throws IOException {
318 incRPCcallsMetrics();
319 ScanRequest request =
320 RequestConverter.buildScanRequest(
321 getLocation().getRegionInfo().getRegionName(),
322 this.scan, 0, false);
323 try {
324 ScanResponse response = getStub().scan(controller, request);
325 long id = response.getScannerId();
326 if (logScannerActivity) {
327 LOG.info("Open scanner=" + id + " for scan=" + scan.toString()
328 + " on region " + getLocation().toString());
329 }
330 return id;
331 } catch (ServiceException se) {
332 throw ProtobufUtil.getRemoteException(se);
333 }
334 }
335
336 protected Scan getScan() {
337 return scan;
338 }
339
340
341
342
343 public void setClose() {
344 this.closed = true;
345 }
346
347 public void setRenew(boolean val) {
348 this.renew = val;
349 }
350
351
352
353
354 public HRegionInfo getHRegionInfo() {
355 if (!instantiated) {
356 return null;
357 }
358 return getLocation().getRegionInfo();
359 }
360
361
362
363
364
365 public int getCaching() {
366 return caching;
367 }
368
369
370
371
372
373 public void setCaching(int caching) {
374 this.caching = caching;
375 }
376 }