1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.mapreduce;
20
21 import static org.junit.Assert.assertTrue;
22 import static org.junit.Assert.fail;
23
24 import java.io.IOException;
25 import java.util.Iterator;
26 import java.util.Map;
27 import java.util.NavigableMap;
28
29 import org.apache.commons.logging.Log;
30 import org.apache.hadoop.conf.Configuration;
31 import org.apache.hadoop.hbase.CategoryBasedTimeout;
32 import org.apache.hadoop.hbase.Cell;
33 import org.apache.hadoop.hbase.CellUtil;
34 import org.apache.hadoop.hbase.HBaseTestingUtility;
35 import org.apache.hadoop.hbase.HConstants;
36 import org.apache.hadoop.hbase.TableName;
37 import org.apache.hadoop.hbase.client.HTable;
38 import org.apache.hadoop.hbase.client.Put;
39 import org.apache.hadoop.hbase.client.Result;
40 import org.apache.hadoop.hbase.client.ResultScanner;
41 import org.apache.hadoop.hbase.client.Scan;
42 import org.apache.hadoop.hbase.client.Table;
43 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
44 import org.apache.hadoop.hbase.util.Bytes;
45 import org.junit.AfterClass;
46 import org.junit.BeforeClass;
47 import org.junit.Rule;
48 import org.junit.Test;
49 import org.junit.rules.TestRule;
50
51
52
53
54
55
56
57 public abstract class TestTableMapReduceBase {
58 @Rule public final TestRule timeout = CategoryBasedTimeout.builder().
59 withTimeout(this.getClass()).withLookingForStuckThread(true).build();
60 protected static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
61 protected static final TableName MULTI_REGION_TABLE_NAME = TableName.valueOf("mrtest");
62 protected static final TableName TABLE_FOR_NEGATIVE_TESTS = TableName.valueOf("testfailuretable");
63 protected static final byte[] INPUT_FAMILY = Bytes.toBytes("contents");
64 protected static final byte[] OUTPUT_FAMILY = Bytes.toBytes("text");
65
66 protected static final byte[][] columns = new byte[][] {
67 INPUT_FAMILY,
68 OUTPUT_FAMILY
69 };
70
71
72
73
74 protected abstract Log getLog();
75
76
77
78
79 protected abstract void runTestOnTable(HTable table) throws IOException;
80
81 @BeforeClass
82 public static void beforeClass() throws Exception {
83 UTIL.setJobWithoutMRCluster();
84 UTIL.startMiniCluster();
85 HTable table =
86 UTIL.createMultiRegionTable(MULTI_REGION_TABLE_NAME, new byte[][] { INPUT_FAMILY,
87 OUTPUT_FAMILY });
88 UTIL.loadTable(table, INPUT_FAMILY, false);
89 UTIL.createTable(TABLE_FOR_NEGATIVE_TESTS, new byte[][] { INPUT_FAMILY, OUTPUT_FAMILY });
90 }
91
92 @AfterClass
93 public static void afterClass() throws Exception {
94 UTIL.deleteTable(TABLE_FOR_NEGATIVE_TESTS);
95 UTIL.shutdownMiniCluster();
96 }
97
98
99
100
101
102 @Test
103 public void testMultiRegionTable() throws IOException {
104 runTestOnTable(new HTable(UTIL.getConfiguration(), MULTI_REGION_TABLE_NAME));
105 }
106
107 @Test
108 public void testCombiner() throws IOException {
109 Configuration conf = new Configuration(UTIL.getConfiguration());
110
111 conf.setInt("mapreduce.map.combine.minspills", 1);
112 runTestOnTable(new HTable(conf, MULTI_REGION_TABLE_NAME));
113 }
114
115
116
117
118 protected static Put map(ImmutableBytesWritable key, Result value) throws IOException {
119 if (value.size() != 1) {
120 throw new IOException("There should only be one input column");
121 }
122 Map<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>>
123 cf = value.getMap();
124 if(!cf.containsKey(INPUT_FAMILY)) {
125 throw new IOException("Wrong input columns. Missing: '" +
126 Bytes.toString(INPUT_FAMILY) + "'.");
127 }
128
129
130
131 String originalValue = Bytes.toString(value.getValue(INPUT_FAMILY, INPUT_FAMILY));
132 StringBuilder newValue = new StringBuilder(originalValue);
133 newValue.reverse();
134
135
136
137 Put outval = new Put(key.get());
138 outval.add(OUTPUT_FAMILY, null, Bytes.toBytes(newValue.toString()));
139 return outval;
140 }
141
142 protected void verify(TableName tableName) throws IOException {
143 Table table = new HTable(UTIL.getConfiguration(), tableName);
144 boolean verified = false;
145 long pause = UTIL.getConfiguration().getLong("hbase.client.pause", 5 * 1000);
146 int numRetries = UTIL.getConfiguration().getInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 5);
147 for (int i = 0; i < numRetries; i++) {
148 try {
149 getLog().info("Verification attempt #" + i);
150 verifyAttempt(table);
151 verified = true;
152 break;
153 } catch (NullPointerException e) {
154
155
156 getLog().debug("Verification attempt failed: " + e.getMessage());
157 }
158 try {
159 Thread.sleep(pause);
160 } catch (InterruptedException e) {
161
162 }
163 }
164 assertTrue(verified);
165 }
166
167
168
169
170
171
172
173
174 private void verifyAttempt(final Table table) throws IOException, NullPointerException {
175 Scan scan = new Scan();
176 TableInputFormat.addColumns(scan, columns);
177 ResultScanner scanner = table.getScanner(scan);
178 try {
179 Iterator<Result> itr = scanner.iterator();
180 assertTrue(itr.hasNext());
181 while(itr.hasNext()) {
182 Result r = itr.next();
183 if (getLog().isDebugEnabled()) {
184 if (r.size() > 2 ) {
185 throw new IOException("Too many results, expected 2 got " +
186 r.size());
187 }
188 }
189 byte[] firstValue = null;
190 byte[] secondValue = null;
191 int count = 0;
192 for(Cell kv : r.listCells()) {
193 if (count == 0) {
194 firstValue = CellUtil.cloneValue(kv);
195 }
196 if (count == 1) {
197 secondValue = CellUtil.cloneValue(kv);
198 }
199 count++;
200 if (count == 2) {
201 break;
202 }
203 }
204
205
206 if (firstValue == null) {
207 throw new NullPointerException(Bytes.toString(r.getRow()) +
208 ": first value is null");
209 }
210 String first = Bytes.toString(firstValue);
211
212 if (secondValue == null) {
213 throw new NullPointerException(Bytes.toString(r.getRow()) +
214 ": second value is null");
215 }
216 byte[] secondReversed = new byte[secondValue.length];
217 for (int i = 0, j = secondValue.length - 1; j >= 0; j--, i++) {
218 secondReversed[i] = secondValue[j];
219 }
220 String second = Bytes.toString(secondReversed);
221
222 if (first.compareTo(second) != 0) {
223 if (getLog().isDebugEnabled()) {
224 getLog().debug("second key is not the reverse of first. row=" +
225 Bytes.toStringBinary(r.getRow()) + ", first value=" + first +
226 ", second value=" + second);
227 }
228 fail();
229 }
230 }
231 } finally {
232 scanner.close();
233 }
234 }
235 }