1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.mapreduce;
19
20 import java.io.IOException;
21 import java.lang.reflect.Method;
22 import java.util.Map;
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.hbase.classification.InterfaceStability;
28 import org.apache.hadoop.conf.Configuration;
29 import org.apache.hadoop.hbase.client.HTable;
30 import org.apache.hadoop.hbase.client.Result;
31 import org.apache.hadoop.hbase.client.ResultScanner;
32 import org.apache.hadoop.hbase.client.Scan;
33 import org.apache.hadoop.hbase.client.ScannerCallable;
34 import org.apache.hadoop.hbase.client.Table;
35 import org.apache.hadoop.hbase.client.metrics.ScanMetrics;
36 import org.apache.hadoop.hbase.DoNotRetryIOException;
37 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
38 import org.apache.hadoop.hbase.util.Bytes;
39 import org.apache.hadoop.mapreduce.Counter;
40 import org.apache.hadoop.mapreduce.InputSplit;
41 import org.apache.hadoop.mapreduce.TaskAttemptContext;
42 import org.apache.hadoop.util.StringUtils;
43
44
45
46
47
48 @InterfaceAudience.Public
49 @InterfaceStability.Stable
50 public class TableRecordReaderImpl {
51 public static final String LOG_PER_ROW_COUNT
52 = "hbase.mapreduce.log.scanner.rowcount";
53
54 private static final Log LOG = LogFactory.getLog(TableRecordReaderImpl.class);
55
56
57 private static final String HBASE_COUNTER_GROUP_NAME =
58 "HBase Counters";
59 private ResultScanner scanner = null;
60 private Scan scan = null;
61 private Scan currentScan = null;
62 private Table htable = null;
63 private byte[] lastSuccessfulRow = null;
64 private ImmutableBytesWritable key = null;
65 private Result value = null;
66 private TaskAttemptContext context = null;
67 private Method getCounter = null;
68 private long numRestarts = 0;
69 private long numStale = 0;
70 private long timestamp;
71 private int rowcount;
72 private boolean logScannerActivity = false;
73 private int logPerRowCount = 100;
74
75
76
77
78
79
80
81 public void restart(byte[] firstRow) throws IOException {
82 currentScan = new Scan(scan);
83 currentScan.setStartRow(firstRow);
84 currentScan.setScanMetricsEnabled(true);
85 if (this.scanner != null) {
86 if (logScannerActivity) {
87 LOG.info("Closing the previously opened scanner object.");
88 }
89 this.scanner.close();
90 }
91 this.scanner = this.htable.getScanner(currentScan);
92 if (logScannerActivity) {
93 LOG.info("Current scan=" + currentScan.toString());
94 timestamp = System.currentTimeMillis();
95 rowcount = 0;
96 }
97 }
98
99
100
101
102
103
104
105 protected static Method retrieveGetCounterWithStringsParams(TaskAttemptContext context)
106 throws IOException {
107 Method m = null;
108 try {
109 m = context.getClass().getMethod("getCounter",
110 new Class [] {String.class, String.class});
111 } catch (SecurityException e) {
112 throw new IOException("Failed test for getCounter", e);
113 } catch (NoSuchMethodException e) {
114
115 }
116 return m;
117 }
118
119
120
121
122
123
124 public void setHTable(Table htable) {
125 Configuration conf = htable.getConfiguration();
126 logScannerActivity = conf.getBoolean(
127 ScannerCallable.LOG_SCANNER_ACTIVITY, false);
128 logPerRowCount = conf.getInt(LOG_PER_ROW_COUNT, 100);
129 this.htable = htable;
130 }
131
132
133
134
135
136
137
138 @Deprecated
139 public void setHTable(HTable htable) {
140 Configuration conf = htable.getConfiguration();
141 logScannerActivity = conf.getBoolean(
142 ScannerCallable.LOG_SCANNER_ACTIVITY, false);
143 logPerRowCount = conf.getInt(LOG_PER_ROW_COUNT, 100);
144 this.htable = htable;
145 }
146
147
148
149
150
151
152 public void setScan(Scan scan) {
153 this.scan = scan;
154 }
155
156
157
158
159
160
161
162 public void initialize(InputSplit inputsplit,
163 TaskAttemptContext context) throws IOException,
164 InterruptedException {
165 if (context != null) {
166 this.context = context;
167 getCounter = retrieveGetCounterWithStringsParams(context);
168 }
169 restart(scan.getStartRow());
170 }
171
172
173
174
175
176
177 public void close() {
178 this.scanner.close();
179 try {
180 this.htable.close();
181 } catch (IOException ioe) {
182 LOG.warn("Error closing table", ioe);
183 }
184 }
185
186
187
188
189
190
191
192
193 public ImmutableBytesWritable getCurrentKey() throws IOException,
194 InterruptedException {
195 return key;
196 }
197
198
199
200
201
202
203
204
205 public Result getCurrentValue() throws IOException, InterruptedException {
206 return value;
207 }
208
209
210
211
212
213
214
215
216
217 public boolean nextKeyValue() throws IOException, InterruptedException {
218 if (key == null) key = new ImmutableBytesWritable();
219 if (value == null) value = new Result();
220 try {
221 try {
222 value = this.scanner.next();
223 if (value != null && value.isStale()) numStale++;
224 if (logScannerActivity) {
225 rowcount ++;
226 if (rowcount >= logPerRowCount) {
227 long now = System.currentTimeMillis();
228 LOG.info("Mapper took " + (now-timestamp)
229 + "ms to process " + rowcount + " rows");
230 timestamp = now;
231 rowcount = 0;
232 }
233 }
234 } catch (IOException e) {
235
236 if (e instanceof DoNotRetryIOException) {
237 throw e;
238 }
239
240
241 LOG.info("recovered from " + StringUtils.stringifyException(e));
242 if (lastSuccessfulRow == null) {
243 LOG.warn("We are restarting the first next() invocation," +
244 " if your mapper has restarted a few other times like this" +
245 " then you should consider killing this job and investigate" +
246 " why it's taking so long.");
247 }
248 if (lastSuccessfulRow == null) {
249 restart(scan.getStartRow());
250 } else {
251 restart(lastSuccessfulRow);
252 scanner.next();
253 }
254 value = scanner.next();
255 if (value != null && value.isStale()) numStale++;
256 numRestarts++;
257 }
258 if (value != null && value.size() > 0) {
259 key.set(value.getRow());
260 lastSuccessfulRow = key.get();
261 return true;
262 }
263
264 updateCounters();
265 return false;
266 } catch (IOException ioe) {
267 if (logScannerActivity) {
268 long now = System.currentTimeMillis();
269 LOG.info("Mapper took " + (now-timestamp)
270 + "ms to process " + rowcount + " rows");
271 LOG.info(ioe);
272 String lastRow = lastSuccessfulRow == null ?
273 "null" : Bytes.toStringBinary(lastSuccessfulRow);
274 LOG.info("lastSuccessfulRow=" + lastRow);
275 }
276 throw ioe;
277 }
278 }
279
280
281
282
283
284
285
286
287 private void updateCounters() throws IOException {
288 ScanMetrics scanMetrics = this.scan.getScanMetrics();
289 if (scanMetrics == null) {
290 return;
291 }
292
293 updateCounters(scanMetrics, numRestarts, getCounter, context, numStale);
294 }
295
296 protected static void updateCounters(ScanMetrics scanMetrics, long numScannerRestarts,
297 Method getCounter, TaskAttemptContext context, long numStale) {
298
299 if (getCounter == null) {
300 return;
301 }
302
303 try {
304 for (Map.Entry<String, Long> entry:scanMetrics.getMetricsMap().entrySet()) {
305 Counter ct = (Counter)getCounter.invoke(context,
306 HBASE_COUNTER_GROUP_NAME, entry.getKey());
307
308 ct.increment(entry.getValue());
309 }
310 ((Counter) getCounter.invoke(context, HBASE_COUNTER_GROUP_NAME,
311 "NUM_SCANNER_RESTARTS")).increment(numScannerRestarts);
312 ((Counter) getCounter.invoke(context, HBASE_COUNTER_GROUP_NAME,
313 "NUM_SCAN_RESULTS_STALE")).increment(numStale);
314 } catch (Exception e) {
315 LOG.debug("can't update counter." + StringUtils.stringifyException(e));
316 }
317 }
318
319
320
321
322
323 @Deprecated
324 protected static void updateCounters(ScanMetrics scanMetrics, long numScannerRestarts,
325 Method getCounter, TaskAttemptContext context) {
326
327 if (getCounter == null) {
328 return;
329 }
330
331 try {
332 for (Map.Entry<String, Long> entry:scanMetrics.getMetricsMap().entrySet()) {
333 Counter ct = (Counter)getCounter.invoke(context,
334 HBASE_COUNTER_GROUP_NAME, entry.getKey());
335
336 ct.increment(entry.getValue());
337 }
338 ((Counter) getCounter.invoke(context, HBASE_COUNTER_GROUP_NAME,
339 "NUM_SCANNER_RESTARTS")).increment(numScannerRestarts);
340 } catch (Exception e) {
341 LOG.debug("can't update counter." + StringUtils.stringifyException(e));
342 }
343 }
344
345
346
347
348
349
350 public float getProgress() {
351
352 return 0;
353 }
354
355 }