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 com.google.protobuf.ServiceException;
22 import com.google.protobuf.TextFormat;
23
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26 import org.apache.hadoop.classification.InterfaceAudience;
27 import org.apache.hadoop.classification.InterfaceStability;
28 import org.apache.hadoop.conf.Configuration;
29 import org.apache.hadoop.hbase.HRegionInfo;
30 import org.apache.hadoop.hbase.HRegionLocation;
31 import org.apache.hadoop.hbase.RemoteExceptionHandler;
32 import org.apache.hadoop.hbase.client.metrics.ScanMetrics;
33 import org.apache.hadoop.hbase.exceptions.DoNotRetryIOException;
34 import org.apache.hadoop.hbase.exceptions.NotServingRegionException;
35 import org.apache.hadoop.hbase.exceptions.RegionServerStoppedException;
36 import org.apache.hadoop.hbase.exceptions.UnknownScannerException;
37 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
38 import org.apache.hadoop.hbase.protobuf.RequestConverter;
39 import org.apache.hadoop.hbase.protobuf.ResponseConverter;
40 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.ScanRequest;
41 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.ScanResponse;
42 import org.apache.hadoop.ipc.RemoteException;
43 import org.apache.hadoop.net.DNS;
44
45 import java.io.IOException;
46 import java.net.UnknownHostException;
47
48
49
50
51
52 @InterfaceAudience.Public
53 @InterfaceStability.Stable
54 public class ScannerCallable extends ServerCallable<Result[]> {
55 public static final String LOG_SCANNER_LATENCY_CUTOFF
56 = "hbase.client.log.scanner.latency.cutoff";
57 public static final String LOG_SCANNER_ACTIVITY = "hbase.client.log.scanner.activity";
58
59 public static final Log LOG = LogFactory.getLog(ScannerCallable.class);
60 private long scannerId = -1L;
61 private boolean instantiated = false;
62 private boolean closed = false;
63 private Scan scan;
64 private int caching = 1;
65 private ScanMetrics scanMetrics;
66 private boolean logScannerActivity = false;
67 private int logCutOffLatency = 1000;
68
69
70 private boolean isRegionServerRemote = true;
71 private long nextCallSeq = 0;
72
73
74
75
76
77
78
79
80 public ScannerCallable (HConnection connection, byte [] tableName, Scan scan,
81 ScanMetrics scanMetrics) {
82 super(connection, tableName, scan.getStartRow());
83 this.scan = scan;
84 this.scanMetrics = scanMetrics;
85 Configuration conf = connection.getConfiguration();
86 logScannerActivity = conf.getBoolean(LOG_SCANNER_ACTIVITY, false);
87 logCutOffLatency = conf.getInt(LOG_SCANNER_LATENCY_CUTOFF, 1000);
88 }
89
90
91
92
93
94 @Override
95 public void connect(boolean reload) throws IOException {
96 if (!instantiated || reload) {
97 super.connect(reload);
98 checkIfRegionServerIsRemote();
99 instantiated = true;
100 }
101
102
103
104
105 if (reload && this.scanMetrics != null) {
106 this.scanMetrics.countOfRPCRetries.incrementAndGet();
107 if (isRegionServerRemote) {
108 this.scanMetrics.countOfRemoteRPCRetries.incrementAndGet();
109 }
110 }
111 }
112
113
114
115
116
117
118 private void checkIfRegionServerIsRemote() throws UnknownHostException {
119 String myAddress = DNS.getDefaultHost("default", "default");
120 if (this.location.getHostname().equalsIgnoreCase(myAddress)) {
121 isRegionServerRemote = false;
122 } else {
123 isRegionServerRemote = true;
124 }
125 }
126
127
128
129
130 public Result [] call() throws IOException {
131 if (closed) {
132 if (scannerId != -1) {
133 close();
134 }
135 } else {
136 if (scannerId == -1L) {
137 this.scannerId = openScanner();
138 } else {
139 Result [] rrs = null;
140 ScanRequest request = null;
141 try {
142 incRPCcallsMetrics();
143 request =
144 RequestConverter.buildScanRequest(scannerId, caching, false, nextCallSeq);
145 ScanResponse response = null;
146 try {
147 response = server.scan(null, request);
148
149
150
151
152
153
154
155
156
157 nextCallSeq++;
158 long timestamp = System.currentTimeMillis();
159 rrs = ResponseConverter.getResults(response);
160 if (logScannerActivity) {
161 long now = System.currentTimeMillis();
162 if (now - timestamp > logCutOffLatency) {
163 int rows = rrs == null ? 0 : rrs.length;
164 LOG.info("Took " + (now-timestamp) + "ms to fetch "
165 + rows + " rows from scanner=" + scannerId);
166 }
167 }
168 if (response.hasMoreResults()
169 && !response.getMoreResults()) {
170 scannerId = -1L;
171 closed = true;
172 return null;
173 }
174 } catch (ServiceException se) {
175 throw ProtobufUtil.getRemoteException(se);
176 }
177 updateResultsMetrics(response);
178 } catch (IOException e) {
179 if (logScannerActivity) {
180 LOG.info("Got exception making request " + TextFormat.shortDebugString(request), e);
181 }
182 IOException ioe = e;
183 if (e instanceof RemoteException) {
184 ioe = RemoteExceptionHandler.decodeRemoteException((RemoteException)e);
185 }
186 if (logScannerActivity && (ioe instanceof UnknownScannerException)) {
187 try {
188 HRegionLocation location =
189 connection.relocateRegion(tableName, scan.getStartRow());
190 LOG.info("Scanner=" + scannerId
191 + " expired, current region location is " + location.toString()
192 + " ip:" + location.getHostnamePort());
193 } catch (Throwable t) {
194 LOG.info("Failed to relocate region", t);
195 }
196 }
197 if (ioe instanceof NotServingRegionException) {
198
199
200
201 if (this.scanMetrics != null) {
202 this.scanMetrics.countOfNSRE.incrementAndGet();
203 }
204 throw new DoNotRetryIOException("Reset scanner", ioe);
205 } else if (ioe instanceof RegionServerStoppedException) {
206
207
208
209 throw new DoNotRetryIOException("Reset scanner", ioe);
210 } else {
211
212 throw ioe;
213 }
214 }
215 return rrs;
216 }
217 }
218 return null;
219 }
220
221 private void incRPCcallsMetrics() {
222 if (this.scanMetrics == null) {
223 return;
224 }
225 this.scanMetrics.countOfRPCcalls.incrementAndGet();
226 if (isRegionServerRemote) {
227 this.scanMetrics.countOfRemoteRPCcalls.incrementAndGet();
228 }
229 }
230
231 private void updateResultsMetrics(ScanResponse response) {
232 if (this.scanMetrics == null || !response.hasResultSizeBytes()) {
233 return;
234 }
235 long value = response.getResultSizeBytes();
236 this.scanMetrics.countOfBytesInResults.addAndGet(value);
237 if (isRegionServerRemote) {
238 this.scanMetrics.countOfBytesInRemoteResults.addAndGet(value);
239 }
240 }
241
242 private void close() {
243 if (this.scannerId == -1L) {
244 return;
245 }
246 try {
247 incRPCcallsMetrics();
248 ScanRequest request =
249 RequestConverter.buildScanRequest(this.scannerId, 0, true);
250 try {
251 server.scan(null, request);
252 } catch (ServiceException se) {
253 throw ProtobufUtil.getRemoteException(se);
254 }
255 } catch (IOException e) {
256 LOG.warn("Ignore, probably already closed", e);
257 }
258 this.scannerId = -1L;
259 }
260
261 protected long openScanner() throws IOException {
262 incRPCcallsMetrics();
263 ScanRequest request =
264 RequestConverter.buildScanRequest(
265 this.location.getRegionInfo().getRegionName(),
266 this.scan, 0, false);
267 try {
268 ScanResponse response = server.scan(null, request);
269 long id = response.getScannerId();
270 if (logScannerActivity) {
271 LOG.info("Open scanner=" + id + " for scan=" + scan.toString()
272 + " on region " + this.location.toString() + " ip:"
273 + this.location.getHostnamePort());
274 }
275 return id;
276 } catch (ServiceException se) {
277 throw ProtobufUtil.getRemoteException(se);
278 }
279 }
280
281 protected Scan getScan() {
282 return scan;
283 }
284
285
286
287
288 public void setClose() {
289 this.closed = true;
290 }
291
292
293
294
295 public HRegionInfo getHRegionInfo() {
296 if (!instantiated) {
297 return null;
298 }
299 return location.getRegionInfo();
300 }
301
302
303
304
305
306 public int getCaching() {
307 return caching;
308 }
309
310
311
312
313
314 public void setCaching(int caching) {
315 this.caching = caching;
316 }
317 }