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;
21
22 import java.io.DataInputStream;
23 import java.io.FileInputStream;
24 import java.io.IOException;
25 import java.io.RandomAccessFile;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.List;
29 import java.util.Random;
30 import java.util.concurrent.Callable;
31 import java.util.concurrent.atomic.AtomicLong;
32 import java.util.concurrent.atomic.AtomicReference;
33
34 import org.apache.commons.logging.Log;
35 import org.apache.commons.logging.LogFactory;
36 import org.apache.hadoop.hbase.client.Get;
37 import org.apache.hadoop.hbase.client.HBaseAdmin;
38 import org.apache.hadoop.hbase.client.HTable;
39 import org.apache.hadoop.hbase.client.Put;
40 import org.apache.hadoop.hbase.client.Result;
41 import org.apache.hadoop.hbase.util.Bytes;
42 import org.apache.hadoop.hbase.util.InfoServer;
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61 public class VerifiableEditor {
62 protected static final Log LOG = LogFactory.getLog(VerifiableEditor.class.getName());
63
64 private static final int NUM_WRITE_THREADS = 40;
65
66 public static final byte [] TABLE_NAME = Bytes.toBytes("VerifiableEditor");
67 public static final byte [] FAMILY_NAME = Bytes.toBytes("info");
68 public static final byte [] QUALIFIER_NAME = Bytes.toBytes("data");
69
70 protected static final HTableDescriptor TABLE_DESCRIPTOR;
71 static {
72 TABLE_DESCRIPTOR = new HTableDescriptor(TABLE_NAME);
73 TABLE_DESCRIPTOR.addFamily(new HColumnDescriptor(FAMILY_NAME));
74 }
75
76 volatile HBaseConfiguration conf;
77
78
79
80
81
82 public VerifiableEditor(final HBaseConfiguration c) {
83 this.conf = c;
84 }
85
86 private byte [] getDataToWrite(String clientId, long curWrite) {
87 return Bytes.toBytes(String.valueOf(curWrite) + "<" + clientId + ">");
88 }
89
90 private class Writer implements Callable<Integer> {
91 private AtomicLong randomSeedGenerator;
92
93 private ThreadLocal<Long> randomSeed = new ThreadLocal<Long>() {
94 protected Long initialValue() {
95 return randomSeedGenerator.incrementAndGet();
96 }
97 };
98
99 public Writer(String args[]) {
100 this.randomSeedGenerator = new AtomicLong(System.currentTimeMillis());
101 }
102
103 private RandomAccessFile openLocalRecorder() throws IOException {
104 RandomAccessFile localRecorder = new RandomAccessFile(
105 "/dev/shm/hbase-verifiableeditor-" + randomSeed.get(),
106 "rws");
107 localRecorder.seek(0);
108 localRecorder.writeLong(randomSeed.get());
109 localRecorder.writeLong(-1);
110 return localRecorder;
111 }
112
113 private void recordIteration(RandomAccessFile raf,
114 long iteration) throws IOException {
115 raf.seek(8);
116 raf.writeLong(iteration);
117 }
118
119
120 public Integer call() throws IOException {
121 createTableIfMissing();
122
123 final AtomicReference<Throwable> err = new AtomicReference<Throwable>();
124 List<Thread> threads = new ArrayList<Thread>();
125
126 for (int i = 0; i < NUM_WRITE_THREADS; i++) {
127 Thread thr = new Thread() {
128 public void run() {
129 try {
130 doWrites();
131 } catch (Throwable t) {
132 err.set(t);
133 }
134 }
135 };
136 threads.add(thr);
137 thr.start();
138 }
139 for (Thread thr : threads) {
140 try { thr.join(); } catch (InterruptedException ie) {}
141 }
142 if (err.get() != null) {
143 throw new RuntimeException(err.get());
144 }
145
146 return 0;
147 }
148
149 private void doWrites() throws IOException {
150 RandomAccessFile recorder = openLocalRecorder();
151 HTable table = new HTable(conf, TABLE_NAME);
152 Random r = new Random(randomSeed.get());
153 boolean stop = false;
154 long iteration = 0;
155 String clientId = String.valueOf(randomSeed.get());
156 while (!stop) {
157 int curWrite = r.nextInt();
158 byte[] curData = getDataToWrite(clientId, curWrite);
159 Put p = new Put(curData
160 p.add(FAMILY_NAME, QUALIFIER_NAME, curData);
161 table.put(p);
162
163 recordIteration(recorder, iteration);
164 iteration++;
165
166 if (iteration % 1000 == 0) {
167 LOG.info("Client " + clientId + " written " + iteration + " iterations");
168 }
169 }
170 }
171 }
172
173 private class Verifier implements Callable<Integer> {
174 private final long randomSeed;
175 private final long verifyUpTo;
176
177 public Verifier(List<String> args) throws IOException {
178 if (args.size() != 1) {
179 printUsage();
180 throw new RuntimeException("bad usage");
181 }
182
183 DataInputStream in = new DataInputStream(new FileInputStream(args.get(0)));
184 try {
185 randomSeed = in.readLong();
186 verifyUpTo = in.readLong();
187 } finally {
188 in.close();
189 }
190 }
191
192 public Integer call() throws IOException {
193 final Random r = new Random(randomSeed);
194 final String clientId = String.valueOf(randomSeed);
195
196 List<Thread> threads = new ArrayList<Thread>();
197 final AtomicReference<Throwable> err = new AtomicReference<Throwable>();
198
199 final AtomicLong curIteration = new AtomicLong();
200
201 for (int i = 0; i < 10; i++) {
202 Thread thr = new Thread() {
203 public void run() {
204 try {
205 HTable table = new HTable(conf, TABLE_NAME);
206 while (curIteration.get() < verifyUpTo) {
207 int curWrite;
208 long myIteration;
209 synchronized (r) {
210 curWrite = r.nextInt();
211 myIteration = curIteration.getAndIncrement();
212 }
213
214 byte[] curData = getDataToWrite(clientId, curWrite);
215 Get g = new Get(curData);
216 Result res = table.get(g);
217 byte[] gotValue = res.getValue(FAMILY_NAME, QUALIFIER_NAME);
218 if (! Bytes.equals(curData, gotValue)) {
219 throw new RuntimeException("VERIFICATION FAILED. " +
220 "iteration=" + myIteration + "/" + verifyUpTo +
221 " seed=" + randomSeed +
222 " expected=" + (curData != null ? Bytes.toStringBinary(curData) : "null") +
223 " got=" + (gotValue != null ? Bytes.toStringBinary(gotValue) : "null"));
224 }
225 }
226 } catch (Throwable t) {
227 err.set(t);
228 }
229 }
230 };
231 threads.add(thr);
232 thr.start();
233 }
234 for (Thread thr : threads) {
235 try { thr.join(); } catch (InterruptedException ie) {}
236 }
237 if (err.get() != null) {
238 throw new RuntimeException(err.get());
239 }
240
241 LOG.info("Successfully verified " + verifyUpTo + " writes from " + randomSeed);
242
243 return 0;
244 }
245 }
246
247
248 protected void printUsage() {
249 printUsage(null);
250 }
251
252 protected void printUsage(final String message) {
253 if (message != null && message.length() > 0) {
254 System.err.println(message);
255 }
256 System.err.println("Usage: java " + this.getClass().getName() + " \\");
257 System.err.println(" [writer | verify <writerlog>]");
258 }
259
260 private void createTableIfMissing() throws IOException {
261 try {
262 HBaseAdmin admin = new HBaseAdmin(conf);
263 admin.createTable(TABLE_DESCRIPTOR);
264 LOG.info("Created table!");
265 } catch (TableExistsException tee) {
266 }
267 }
268
269
270
271 public int doCommandLine(String args[]) {
272 if (args.length < 1) {
273 printUsage();
274 return 1;
275 }
276
277 List<String> toolArgs = Arrays.<String>asList(args).subList(1, args.length);
278
279 InfoServer infoServer = null;
280 try {
281 infoServer = new InfoServer(
282 "static", "0.0.0.0", 0, true);
283 infoServer.start();
284
285 Callable<Integer> tool = null;
286
287
288 if (args[0].equals("writer")) {
289 tool = new Writer(args);
290 } else if (args[0].equals("verify")) {
291 tool = new Verifier(toolArgs);
292 } else {
293 printUsage("unknown tool: " + args[0]);
294 return 1;
295 }
296
297 return tool.call();
298 } catch (Exception e) {
299 throw new RuntimeException(e);
300 } finally {
301 try {
302 if (infoServer != null)
303 infoServer.stop();
304 } catch (Exception e) {
305 LOG.error("Couldn't stop info server", e);
306 }
307 }
308 }
309
310
311
312
313
314 public static void main(final String[] args) {
315 HBaseConfiguration c = new HBaseConfiguration();
316 System.exit(new VerifiableEditor(c).doCommandLine(args));
317 }
318 }