View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.mapreduce;
19  
20  import static org.junit.Assert.assertEquals;
21  
22  import java.util.Arrays;
23  
24  import org.apache.commons.lang.ArrayUtils;
25  import org.apache.commons.logging.Log;
26  import org.apache.commons.logging.LogFactory;
27  import org.apache.hadoop.fs.FileSystem;
28  import org.apache.hadoop.fs.Path;
29  import org.apache.hadoop.hbase.CategoryBasedTimeout;
30  import org.apache.hadoop.hbase.Cell;
31  import org.apache.hadoop.hbase.CellUtil;
32  import org.apache.hadoop.hbase.HBaseTestingUtility;
33  import org.apache.hadoop.hbase.TableName;
34  import org.apache.hadoop.hbase.client.Put;
35  import org.apache.hadoop.hbase.client.Result;
36  import org.apache.hadoop.hbase.client.ResultScanner;
37  import org.apache.hadoop.hbase.client.Scan;
38  import org.apache.hadoop.hbase.client.Table;
39  import org.apache.hadoop.hbase.mapreduce.SyncTable.SyncMapper.Counter;
40  import org.apache.hadoop.hbase.testclassification.LargeTests;
41  import org.apache.hadoop.hbase.util.Bytes;
42  import org.apache.hadoop.mapreduce.Counters;
43  import org.junit.AfterClass;
44  import org.junit.Assert;
45  import org.junit.BeforeClass;
46  import org.junit.Rule;
47  import org.junit.Test;
48  import org.junit.experimental.categories.Category;
49  import org.junit.rules.TestRule;
50  
51  import com.google.common.base.Throwables;
52  
53  /**
54   * Basic test for the SyncTable M/R tool
55   */
56  @Category(LargeTests.class)
57  public class TestSyncTable {
58    @Rule public final TestRule timeout = CategoryBasedTimeout.builder().
59        withTimeout(this.getClass()).withLookingForStuckThread(true).build();
60    private static final Log LOG = LogFactory.getLog(TestSyncTable.class);
61    
62    private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();  
63    
64    @BeforeClass
65    public static void beforeClass() throws Exception {
66      TEST_UTIL.setJobWithoutMRCluster();
67      TEST_UTIL.startMiniCluster(3);
68    }
69    
70    @AfterClass
71    public static void afterClass() throws Exception {
72      TEST_UTIL.cleanupDataTestDirOnTestFS();
73      TEST_UTIL.shutdownMiniCluster();
74    }
75    
76    private static byte[][] generateSplits(int numRows, int numRegions) {
77      byte[][] splitRows = new byte[numRegions-1][];
78      for (int i = 1; i < numRegions; i++) {
79        splitRows[i-1] = Bytes.toBytes(numRows * i / numRegions);
80      }
81      return splitRows;
82    }
83    
84    @Test
85    public void testSyncTable() throws Exception {
86      final TableName sourceTableName = TableName.valueOf("testSourceTable");
87      final TableName targetTableName = TableName.valueOf("testTargetTable");
88      Path testDir = TEST_UTIL.getDataTestDirOnTestFS("testSyncTable");
89      
90      writeTestData(sourceTableName, targetTableName);
91      hashSourceTable(sourceTableName, testDir);
92      Counters syncCounters = syncTables(sourceTableName, targetTableName, testDir);
93      assertEqualTables(90, sourceTableName, targetTableName, false);
94      
95      assertEquals(60, syncCounters.findCounter(Counter.ROWSWITHDIFFS).getValue());
96      assertEquals(10, syncCounters.findCounter(Counter.SOURCEMISSINGROWS).getValue());
97      assertEquals(10, syncCounters.findCounter(Counter.TARGETMISSINGROWS).getValue());
98      assertEquals(50, syncCounters.findCounter(Counter.SOURCEMISSINGCELLS).getValue());
99      assertEquals(50, syncCounters.findCounter(Counter.TARGETMISSINGCELLS).getValue());
100     assertEquals(20, syncCounters.findCounter(Counter.DIFFERENTCELLVALUES).getValue());
101     
102     TEST_UTIL.deleteTable(sourceTableName);
103     TEST_UTIL.deleteTable(targetTableName);
104   }
105 
106   @Test
107   public void testSyncTableDoDeletesFalse() throws Exception {
108     final TableName sourceTableName = TableName.valueOf("testSyncTableDoDeletesFalse_source");
109     final TableName targetTableName = TableName.valueOf("testSyncTableDoDeletesFalse_target");
110     Path testDir = TEST_UTIL.getDataTestDirOnTestFS("testSyncTableDoDeletesFalse");
111 
112     writeTestData(sourceTableName, targetTableName);
113     hashSourceTable(sourceTableName, testDir);
114     Counters syncCounters = syncTables(sourceTableName, targetTableName,
115         testDir, "--doDeletes=false");
116     assertTargetDoDeletesFalse(100, sourceTableName, targetTableName);
117 
118     assertEquals(60, syncCounters.findCounter(Counter.ROWSWITHDIFFS).getValue());
119     assertEquals(10, syncCounters.findCounter(Counter.SOURCEMISSINGROWS).getValue());
120     assertEquals(10, syncCounters.findCounter(Counter.TARGETMISSINGROWS).getValue());
121     assertEquals(50, syncCounters.findCounter(Counter.SOURCEMISSINGCELLS).getValue());
122     assertEquals(50, syncCounters.findCounter(Counter.TARGETMISSINGCELLS).getValue());
123     assertEquals(20, syncCounters.findCounter(Counter.DIFFERENTCELLVALUES).getValue());
124 
125     TEST_UTIL.deleteTable(sourceTableName);
126     TEST_UTIL.deleteTable(targetTableName);
127   }
128 
129   @Test
130   public void testSyncTableDoPutsFalse() throws Exception {
131     final TableName sourceTableName = TableName.valueOf("testSyncTableDoPutsFalse_source");
132     final TableName targetTableName = TableName.valueOf("testSyncTableDoPutsFalse_target");
133     Path testDir = TEST_UTIL.getDataTestDirOnTestFS("testSyncTableDoPutsFalse");
134 
135     writeTestData(sourceTableName, targetTableName);
136     hashSourceTable(sourceTableName, testDir);
137     Counters syncCounters = syncTables(sourceTableName, targetTableName,
138         testDir, "--doPuts=false");
139     assertTargetDoPutsFalse(70, sourceTableName, targetTableName);
140 
141     assertEquals(60, syncCounters.findCounter(Counter.ROWSWITHDIFFS).getValue());
142     assertEquals(10, syncCounters.findCounter(Counter.SOURCEMISSINGROWS).getValue());
143     assertEquals(10, syncCounters.findCounter(Counter.TARGETMISSINGROWS).getValue());
144     assertEquals(50, syncCounters.findCounter(Counter.SOURCEMISSINGCELLS).getValue());
145     assertEquals(50, syncCounters.findCounter(Counter.TARGETMISSINGCELLS).getValue());
146     assertEquals(20, syncCounters.findCounter(Counter.DIFFERENTCELLVALUES).getValue());
147 
148     TEST_UTIL.deleteTable(sourceTableName);
149     TEST_UTIL.deleteTable(targetTableName);
150   }
151 
152   @Test
153   public void testSyncTableIgnoreTimestampsTrue() throws Exception {
154     final TableName sourceTableName = TableName.valueOf("testSyncTableIgnoreTimestampsTrue_source");
155     final TableName targetTableName = TableName.valueOf("testSyncTableIgnoreTimestampsTrue_target");
156     Path testDir = TEST_UTIL.getDataTestDirOnTestFS("testSyncTableIgnoreTimestampsTrue");
157     long current = System.currentTimeMillis();
158     writeTestData(sourceTableName, targetTableName, current - 1000, current);
159     hashSourceTable(sourceTableName, testDir, "--ignoreTimestamps=true");
160     Counters syncCounters = syncTables(sourceTableName, targetTableName,
161       testDir, "--ignoreTimestamps=true");
162     assertEqualTables(90, sourceTableName, targetTableName, true);
163 
164     assertEquals(50, syncCounters.findCounter(Counter.ROWSWITHDIFFS).getValue());
165     assertEquals(10, syncCounters.findCounter(Counter.SOURCEMISSINGROWS).getValue());
166     assertEquals(10, syncCounters.findCounter(Counter.TARGETMISSINGROWS).getValue());
167     assertEquals(30, syncCounters.findCounter(Counter.SOURCEMISSINGCELLS).getValue());
168     assertEquals(30, syncCounters.findCounter(Counter.TARGETMISSINGCELLS).getValue());
169     assertEquals(20, syncCounters.findCounter(Counter.DIFFERENTCELLVALUES).getValue());
170 
171     TEST_UTIL.deleteTable(sourceTableName);
172     TEST_UTIL.deleteTable(targetTableName);
173   }
174 
175   private void assertEqualTables(int expectedRows, TableName sourceTableName,
176       TableName targetTableName, boolean ignoreTimestamps) throws Exception {
177     Table sourceTable = TEST_UTIL.getConnection().getTable(sourceTableName);
178     Table targetTable = TEST_UTIL.getConnection().getTable(targetTableName);
179     
180     ResultScanner sourceScanner = sourceTable.getScanner(new Scan());
181     ResultScanner targetScanner = targetTable.getScanner(new Scan());
182     
183     for (int i = 0; i < expectedRows; i++) {
184       Result sourceRow = sourceScanner.next();
185       Result targetRow = targetScanner.next();
186       
187       LOG.debug("SOURCE row: " + (sourceRow == null ? "null" : Bytes.toInt(sourceRow.getRow()))
188           + " cells:" + sourceRow);
189       LOG.debug("TARGET row: " + (targetRow == null ? "null" : Bytes.toInt(targetRow.getRow()))
190           + " cells:" + targetRow);
191       
192       if (sourceRow == null) {
193         Assert.fail("Expected " + expectedRows
194             + " source rows but only found " + i); 
195       }
196       if (targetRow == null) {
197         Assert.fail("Expected " + expectedRows
198             + " target rows but only found " + i); 
199       }
200       Cell[] sourceCells = sourceRow.rawCells();
201       Cell[] targetCells = targetRow.rawCells();
202       if (sourceCells.length != targetCells.length) {
203         LOG.debug("Source cells: " + Arrays.toString(sourceCells));
204         LOG.debug("Target cells: " + Arrays.toString(targetCells));
205         Assert.fail("Row " + Bytes.toInt(sourceRow.getRow())
206             + " has " + sourceCells.length
207             + " cells in source table but " + targetCells.length
208             + " cells in target table");
209       }
210       for (int j = 0; j < sourceCells.length; j++) {
211         Cell sourceCell = sourceCells[j];
212         Cell targetCell = targetCells[j];
213         try {
214           if (!CellUtil.matchingRow(sourceCell, targetCell)) {
215             Assert.fail("Rows don't match");
216           }
217           if (!CellUtil.matchingFamily(sourceCell, targetCell)) {
218             Assert.fail("Families don't match");
219           }
220           if (!CellUtil.matchingQualifier(sourceCell, targetCell)) {
221             Assert.fail("Qualifiers don't match");
222           }
223           if (!ignoreTimestamps && !CellUtil.matchingTimestamp(sourceCell, targetCell)) {
224             Assert.fail("Timestamps don't match");
225           }
226           if (!CellUtil.matchingValue(sourceCell, targetCell)) {
227             Assert.fail("Values don't match");
228           }
229         } catch (Throwable t) {
230           LOG.debug("Source cell: " + sourceCell + " target cell: " + targetCell);
231           Throwables.propagate(t);
232         }
233       }
234     }
235     Result sourceRow = sourceScanner.next();
236     if (sourceRow != null) {
237       Assert.fail("Source table has more than " + expectedRows
238           + " rows.  Next row: " + Bytes.toInt(sourceRow.getRow()));
239     }
240     Result targetRow = targetScanner.next();
241     if (targetRow != null) {
242       Assert.fail("Target table has more than " + expectedRows
243           + " rows.  Next row: " + Bytes.toInt(targetRow.getRow()));
244     }
245     sourceScanner.close();
246     targetScanner.close();
247     sourceTable.close();
248     targetTable.close();
249   }
250 
251   private void assertTargetDoDeletesFalse(int expectedRows, TableName
252       sourceTableName,
253       TableName targetTableName)
254       throws Exception {
255     Table sourceTable = TEST_UTIL.getConnection().getTable(sourceTableName);
256     Table targetTable = TEST_UTIL.getConnection().getTable(targetTableName);
257 
258     ResultScanner sourceScanner = sourceTable.getScanner(new Scan());
259     ResultScanner targetScanner = targetTable.getScanner(new Scan());
260     Result targetRow = targetScanner.next();
261     Result sourceRow = sourceScanner.next();
262     int rowsCount = 0;
263     while (targetRow!=null) {
264       rowsCount++;
265       //only compares values for existing rows, skipping rows existing on
266       //target only that were not deleted given --doDeletes=false
267       if (Bytes.toInt(sourceRow.getRow()) != Bytes.toInt(targetRow.getRow())) {
268         targetRow = targetScanner.next();
269         continue;
270       }
271 
272       LOG.debug("SOURCE row: " + (sourceRow == null ? "null"
273           : Bytes.toInt(sourceRow.getRow()))
274           + " cells:" + sourceRow);
275       LOG.debug("TARGET row: " + (targetRow == null ? "null"
276           : Bytes.toInt(targetRow.getRow()))
277           + " cells:" + targetRow);
278 
279       Cell[] sourceCells = sourceRow.rawCells();
280       Cell[] targetCells = targetRow.rawCells();
281       int targetRowKey = Bytes.toInt(targetRow.getRow());
282       if (targetRowKey >= 70 && targetRowKey < 80) {
283         if (sourceCells.length == targetCells.length) {
284           LOG.debug("Source cells: " + Arrays.toString(sourceCells));
285           LOG.debug("Target cells: " + Arrays.toString(targetCells));
286           Assert.fail("Row " + targetRowKey + " should have more cells in "
287               + "target than in source");
288         }
289 
290       } else {
291         if (sourceCells.length != targetCells.length) {
292           LOG.debug("Source cells: " + Arrays.toString(sourceCells));
293           LOG.debug("Target cells: " + Arrays.toString(targetCells));
294           Assert.fail("Row " + Bytes.toInt(sourceRow.getRow())
295               + " has " + sourceCells.length
296               + " cells in source table but " + targetCells.length
297               + " cells in target table");
298         }
299       }
300       for (int j = 0; j < sourceCells.length; j++) {
301         Cell sourceCell = sourceCells[j];
302         Cell targetCell = targetCells[j];
303         try {
304           if (!CellUtil.matchingRow(sourceCell, targetCell)) {
305             Assert.fail("Rows don't match");
306           }
307           if (!CellUtil.matchingFamily(sourceCell, targetCell)) {
308             Assert.fail("Families don't match");
309           }
310           if (!CellUtil.matchingQualifier(sourceCell, targetCell)) {
311             Assert.fail("Qualifiers don't match");
312           }
313           if(targetRowKey < 80 && targetRowKey >= 90){
314             if (!CellUtil.matchingTimestamp(sourceCell, targetCell)) {
315               Assert.fail("Timestamps don't match");
316             }
317           }
318           if (!CellUtil.matchingValue(sourceCell, targetCell)) {
319             Assert.fail("Values don't match");
320           }
321         } catch (Throwable t) {
322           LOG.debug("Source cell: " + sourceCell + " target cell: "
323               + targetCell);
324           Throwables.propagate(t);
325         }
326       }
327       targetRow = targetScanner.next();
328       sourceRow = sourceScanner.next();
329     }
330     assertEquals("Target expected rows does not match.",expectedRows,
331         rowsCount);
332     sourceScanner.close();
333     targetScanner.close();
334     sourceTable.close();
335     targetTable.close();
336   }
337 
338   private void assertTargetDoPutsFalse(int expectedRows, TableName
339       sourceTableName,
340       TableName targetTableName)
341       throws Exception {
342     Table sourceTable = TEST_UTIL.getConnection().getTable(sourceTableName);
343     Table targetTable = TEST_UTIL.getConnection().getTable(targetTableName);
344 
345     ResultScanner sourceScanner = sourceTable.getScanner(new Scan());
346     ResultScanner targetScanner = targetTable.getScanner(new Scan());
347     Result targetRow = targetScanner.next();
348     Result sourceRow = sourceScanner.next();
349     int rowsCount = 0;
350 
351     while (targetRow!=null) {
352       //only compares values for existing rows, skipping rows existing on
353       //source only that were not added to target given --doPuts=false
354       if (Bytes.toInt(sourceRow.getRow()) != Bytes.toInt(targetRow.getRow())) {
355         sourceRow = sourceScanner.next();
356         continue;
357       }
358 
359       LOG.debug("SOURCE row: " + (sourceRow == null ?
360           "null" :
361           Bytes.toInt(sourceRow.getRow()))
362           + " cells:" + sourceRow);
363       LOG.debug("TARGET row: " + (targetRow == null ?
364           "null" :
365           Bytes.toInt(targetRow.getRow()))
366           + " cells:" + targetRow);
367 
368       LOG.debug("rowsCount: " + rowsCount);
369 
370       Cell[] sourceCells = sourceRow.rawCells();
371       Cell[] targetCells = targetRow.rawCells();
372       int targetRowKey = Bytes.toInt(targetRow.getRow());
373       if (targetRowKey >= 40 && targetRowKey < 60) {
374         LOG.debug("Source cells: " + Arrays.toString(sourceCells));
375         LOG.debug("Target cells: " + Arrays.toString(targetCells));
376         Assert.fail("There shouldn't exist any rows between 40 and 60, since "
377             + "Puts are disabled and Deletes are enabled.");
378       } else if (targetRowKey >= 60 && targetRowKey < 70) {
379         if (sourceCells.length == targetCells.length) {
380           LOG.debug("Source cells: " + Arrays.toString(sourceCells));
381           LOG.debug("Target cells: " + Arrays.toString(targetCells));
382           Assert.fail("Row " + Bytes.toInt(sourceRow.getRow())
383               + " shouldn't have same number of cells.");
384         }
385       } else if (targetRowKey >= 80 && targetRowKey < 90) {
386         LOG.debug("Source cells: " + Arrays.toString(sourceCells));
387         LOG.debug("Target cells: " + Arrays.toString(targetCells));
388         Assert.fail("There should be no rows between 80 and 90 on target, as "
389             + "these had different timestamps and should had been deleted.");
390       } else if (targetRowKey >= 90 && targetRowKey < 100) {
391         for (int j = 0; j < sourceCells.length; j++) {
392           Cell sourceCell = sourceCells[j];
393           Cell targetCell = targetCells[j];
394           if (CellUtil.matchingValue(sourceCell, targetCell)) {
395             Assert.fail("Cells values should not match for rows between "
396                 + "90 and 100. Target row id: " + (Bytes.toInt(targetRow
397                 .getRow())));
398           }
399         }
400       } else {
401         for (int j = 0; j < sourceCells.length; j++) {
402           Cell sourceCell = sourceCells[j];
403           Cell targetCell = targetCells[j];
404           try {
405             if (!CellUtil.matchingRow(sourceCell, targetCell)) {
406               Assert.fail("Rows don't match");
407             }
408             if (!CellUtil.matchingFamily(sourceCell, targetCell)) {
409               Assert.fail("Families don't match");
410             }
411             if (!CellUtil.matchingQualifier(sourceCell, targetCell)) {
412               Assert.fail("Qualifiers don't match");
413             }
414             if (!CellUtil.matchingTimestamp(sourceCell, targetCell)) {
415               Assert.fail("Timestamps don't match");
416             }
417             if (!CellUtil.matchingValue(sourceCell, targetCell)) {
418               Assert.fail("Values don't match");
419             }
420           } catch (Throwable t) {
421             LOG.debug(
422                 "Source cell: " + sourceCell + " target cell: " + targetCell);
423             Throwables.propagate(t);
424           }
425         }
426       }
427       rowsCount++;
428       targetRow = targetScanner.next();
429       sourceRow = sourceScanner.next();
430     }
431     assertEquals("Target expected rows does not match.",expectedRows,
432         rowsCount);
433     sourceScanner.close();
434     targetScanner.close();
435     sourceTable.close();
436     targetTable.close();
437   }
438 
439   private Counters syncTables(TableName sourceTableName, TableName targetTableName,
440       Path testDir, String... options) throws Exception {
441     SyncTable syncTable = new SyncTable(TEST_UTIL.getConfiguration());
442     String[] args = Arrays.copyOf(options, options.length+3);
443     args[options.length] = testDir.toString();
444     args[options.length+1] = sourceTableName.getNameAsString();
445     args[options.length+2] = targetTableName.getNameAsString();
446     int code = syncTable.run(args);
447     assertEquals("sync table job failed", 0, code);
448     
449     LOG.info("Sync tables completed");
450     return syncTable.counters;
451   }
452 
453   private void hashSourceTable(TableName sourceTableName, Path testDir, String... options)
454       throws Exception {
455     int numHashFiles = 3;
456     long batchSize = 100;  // should be 2 batches per region
457     int scanBatch = 1;
458     HashTable hashTable = new HashTable(TEST_UTIL.getConfiguration());
459     String[] args = Arrays.copyOf(options, options.length + 5);
460     args[options.length] = "--batchsize=" + batchSize;
461     args[options.length + 1] = "--numhashfiles=" + numHashFiles;
462     args[options.length + 2] = "--scanbatch=" + scanBatch;
463     args[options.length + 3] = sourceTableName.getNameAsString();
464     args[options.length + 4] = testDir.toString();
465     int code = hashTable.run(args);
466     assertEquals("hash table job failed", 0, code);
467     
468     FileSystem fs = TEST_UTIL.getTestFileSystem();
469     
470     HashTable.TableHash tableHash = HashTable.TableHash.read(fs.getConf(), testDir);
471     assertEquals(sourceTableName.getNameAsString(), tableHash.tableName);
472     assertEquals(batchSize, tableHash.batchSize);
473     assertEquals(numHashFiles, tableHash.numHashFiles);
474     assertEquals(numHashFiles - 1, tableHash.partitions.size());
475 
476     LOG.info("Hash table completed");
477   }
478 
479   private void writeTestData(TableName sourceTableName, TableName targetTableName,
480       long... timestamps) throws Exception {
481     final byte[] family = Bytes.toBytes("family");
482     final byte[] column1 = Bytes.toBytes("c1");
483     final byte[] column2 = Bytes.toBytes("c2");
484     final byte[] value1 = Bytes.toBytes("val1");
485     final byte[] value2 = Bytes.toBytes("val2");
486     final byte[] value3 = Bytes.toBytes("val3");
487     
488     int numRows = 100;
489     int sourceRegions = 10;
490     int targetRegions = 6;
491     if (ArrayUtils.isEmpty(timestamps)) {
492       long current = System.currentTimeMillis();
493       timestamps = new long[]{current,current};
494     }
495     Table sourceTable = TEST_UTIL.createTable(sourceTableName,
496       family, generateSplits(numRows, sourceRegions));
497 
498     Table targetTable = TEST_UTIL.createTable(targetTableName,
499       family, generateSplits(numRows, targetRegions));
500 
501     int rowIndex = 0;
502     // a bunch of identical rows
503     for (; rowIndex < 40; rowIndex++) {
504       Put sourcePut = new Put(Bytes.toBytes(rowIndex));
505       sourcePut.addColumn(family, column1, timestamps[0], value1);
506       sourcePut.addColumn(family, column2, timestamps[0], value2);
507       sourceTable.put(sourcePut);
508      
509       Put targetPut = new Put(Bytes.toBytes(rowIndex));
510       targetPut.addColumn(family, column1, timestamps[1], value1);
511       targetPut.addColumn(family, column2, timestamps[1], value2);
512       targetTable.put(targetPut);
513     }
514     // some rows only in the source table
515     // ROWSWITHDIFFS: 10
516     // TARGETMISSINGROWS: 10
517     // TARGETMISSINGCELLS: 20
518     for (; rowIndex < 50; rowIndex++) {
519       Put put = new Put(Bytes.toBytes(rowIndex));
520       put.addColumn(family, column1, timestamps[0], value1);
521       put.addColumn(family, column2, timestamps[0], value2);
522       sourceTable.put(put);
523     }
524     // some rows only in the target table
525     // ROWSWITHDIFFS: 10
526     // SOURCEMISSINGROWS: 10
527     // SOURCEMISSINGCELLS: 20
528     for (; rowIndex < 60; rowIndex++) {
529       Put put = new Put(Bytes.toBytes(rowIndex));
530       put.addColumn(family, column1, timestamps[1], value1);
531       put.addColumn(family, column2, timestamps[1], value2);
532       targetTable.put(put);
533     }
534     // some rows with 1 missing cell in target table
535     // ROWSWITHDIFFS: 10
536     // TARGETMISSINGCELLS: 10
537     for (; rowIndex < 70; rowIndex++) {
538       Put sourcePut = new Put(Bytes.toBytes(rowIndex));
539       sourcePut.addColumn(family, column1, timestamps[0], value1);
540       sourcePut.addColumn(family, column2, timestamps[0], value2);
541       sourceTable.put(sourcePut);
542 
543       Put targetPut = new Put(Bytes.toBytes(rowIndex));
544       targetPut.addColumn(family, column1, timestamps[1], value1);
545       targetTable.put(targetPut);
546     }
547     // some rows with 1 missing cell in source table
548     // ROWSWITHDIFFS: 10
549     // SOURCEMISSINGCELLS: 10
550     for (; rowIndex < 80; rowIndex++) {
551       Put sourcePut = new Put(Bytes.toBytes(rowIndex));
552       sourcePut.addColumn(family, column1, timestamps[0], value1);
553       sourceTable.put(sourcePut);
554 
555       Put targetPut = new Put(Bytes.toBytes(rowIndex));
556       targetPut.addColumn(family, column1, timestamps[1], value1);
557       targetPut.addColumn(family, column2, timestamps[1], value2);
558       targetTable.put(targetPut);
559     }
560     // some rows differing only in timestamp
561     // ROWSWITHDIFFS: 10
562     // SOURCEMISSINGCELLS: 20
563     // TARGETMISSINGCELLS: 20
564     for (; rowIndex < 90; rowIndex++) {
565       Put sourcePut = new Put(Bytes.toBytes(rowIndex));
566       sourcePut.addColumn(family, column1, timestamps[0], column1);
567       sourcePut.addColumn(family, column2, timestamps[0], value2);
568       sourceTable.put(sourcePut);
569 
570       Put targetPut = new Put(Bytes.toBytes(rowIndex));
571       targetPut.addColumn(family, column1, timestamps[1]+1, column1);
572       targetPut.addColumn(family, column2, timestamps[1]-1, value2);
573       targetTable.put(targetPut);
574     }
575     // some rows with different values
576     // ROWSWITHDIFFS: 10
577     // DIFFERENTCELLVALUES: 20
578     for (; rowIndex < numRows; rowIndex++) {
579       Put sourcePut = new Put(Bytes.toBytes(rowIndex));
580       sourcePut.addColumn(family, column1, timestamps[0], value1);
581       sourcePut.addColumn(family, column2, timestamps[0], value2);
582       sourceTable.put(sourcePut);
583       
584       Put targetPut = new Put(Bytes.toBytes(rowIndex));
585       targetPut.addColumn(family, column1, timestamps[1], value3);
586       targetPut.addColumn(family, column2, timestamps[1], value3);
587       targetTable.put(targetPut);
588     }
589     
590     sourceTable.close();
591     targetTable.close();
592   }
593   
594 
595 }