Skip to content

Commit 96e425b

Browse files
committed
Merge pull request #37 from carterpage/test-batch
Test happy path with batch puts, gets, and deletes.
2 parents 2426af3 + 02d0153 commit 96e425b

File tree

2 files changed

+330
-19
lines changed

2 files changed

+330
-19
lines changed
Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
1+
/*
2+
* Copyright (c) 2014 Google Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5+
* in compliance with the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License
10+
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11+
* or implied. See the License for the specific language governing permissions and limitations under
12+
* the License.
13+
*/
14+
package com.google.cloud.anviltop.hbase;
15+
16+
import org.apache.commons.lang.ArrayUtils;
17+
import org.apache.hadoop.hbase.CellUtil;
18+
import org.apache.hadoop.hbase.client.Append;
19+
import org.apache.hadoop.hbase.client.Delete;
20+
import org.apache.hadoop.hbase.client.Get;
21+
import org.apache.hadoop.hbase.client.HTableInterface;
22+
import org.apache.hadoop.hbase.client.Increment;
23+
import org.apache.hadoop.hbase.client.Put;
24+
import org.apache.hadoop.hbase.client.Result;
25+
import org.apache.hadoop.hbase.client.RetriesExhaustedWithDetailsException;
26+
import org.apache.hadoop.hbase.client.Row;
27+
import org.apache.hadoop.hbase.client.RowMutations;
28+
import org.apache.hadoop.hbase.regionserver.NoSuchColumnFamilyException;
29+
import org.apache.hadoop.hbase.util.Bytes;
30+
import org.junit.Assert;
31+
import org.junit.Test;
32+
33+
import java.io.IOException;
34+
import java.util.ArrayList;
35+
import java.util.List;
36+
import java.util.Random;
37+
38+
public class TestBatch extends AbstractTest {
39+
/**
40+
* Requirement 8.1 - Batch performs a collection of Deletes, Gets, Puts, Increments, and Appends
41+
* on multiple rows, returning results in the same order as the requested actions.
42+
*
43+
* Requirement 8.5 - A batch() should return an empty Result object for successful put/delete
44+
* operations and get operations with no matching column.
45+
*
46+
* Requirement 8.6 - Get operations with matching values should return populated Result object in
47+
* a batch() operation.
48+
*/
49+
@Test
50+
public void testBatchPutGetAndDelete() throws IOException, InterruptedException {
51+
// Initialize data
52+
HTableInterface table = connection.getTable(TABLE_NAME);
53+
byte[] rowKey1 = dataHelper.randomData("testrow-");
54+
byte[] qual1 = dataHelper.randomData("qual-");
55+
byte[] value1 = dataHelper.randomData("value-");
56+
byte[] rowKey2 = dataHelper.randomData("testrow-");
57+
byte[] qual2 = dataHelper.randomData("qual-");
58+
byte[] value2 = dataHelper.randomData("value-");
59+
byte[] emptyRowKey = dataHelper.randomData("testrow-");
60+
61+
Put put1 = new Put(rowKey1).add(COLUMN_FAMILY, qual1, value1);
62+
Put put2 = new Put(rowKey2).add(COLUMN_FAMILY, qual2, value2);
63+
List<Row> batch = new ArrayList<Row>(2);
64+
Object[] results = new Object[3];
65+
batch.add(put1);
66+
batch.add(put2);
67+
table.batch(batch, results);
68+
Assert.assertTrue("Should be a Result", results[0] instanceof Result);
69+
Assert.assertTrue("Should be a Result", results[1] instanceof Result);
70+
Assert.assertTrue("Should be empty", ((Result) results[0]).isEmpty());
71+
Assert.assertTrue("Should be empty", ((Result) results[1]).isEmpty());
72+
Assert.assertNull("Last result should be null", results[2]);
73+
Assert.assertEquals("Batch should not have been cleared", 2, batch.size());
74+
75+
// Check values
76+
Get get1 = new Get(rowKey1);
77+
Get get2 = new Get(rowKey2);
78+
Get get3 = new Get(emptyRowKey);
79+
batch.clear();
80+
batch.add(get1);
81+
batch.add(get2);
82+
batch.add(get3);
83+
table.batch(batch, results);
84+
Assert.assertTrue("Should be Result", results[0] instanceof Result);
85+
Assert.assertTrue("Should be Result", results[1] instanceof Result);
86+
Assert.assertTrue("Should be Result", results[2] instanceof Result);
87+
Assert.assertEquals("Should be one value", 1, ((Result) results[0]).size());
88+
Assert.assertEquals("Should be one value", 1, ((Result) results[1]).size());
89+
Assert.assertEquals("Should be empty", 0, ((Result) results[2]).size());
90+
Assert.assertArrayEquals("Should be value1", value1,
91+
CellUtil.cloneValue(((Result) results[0]).getColumnLatestCell(COLUMN_FAMILY, qual1)));
92+
Assert.assertArrayEquals("Should be value2", value2,
93+
CellUtil.cloneValue(((Result) results[1]).getColumnLatestCell(COLUMN_FAMILY, qual2)));
94+
95+
// Delete values
96+
Delete delete1 = new Delete(rowKey1);
97+
Delete delete2 = new Delete(rowKey2);
98+
batch.clear();
99+
batch.add(delete1);
100+
batch.add(delete2);
101+
table.batch(batch, results);
102+
Assert.assertTrue("Should be a Result", results[0] instanceof Result);
103+
Assert.assertTrue("Should be a Result", results[1] instanceof Result);
104+
Assert.assertTrue("Should be empty", ((Result) results[0]).isEmpty());
105+
Assert.assertTrue("Should be empty", ((Result) results[1]).isEmpty());
106+
107+
// Check that delete succeeded
108+
batch.clear();
109+
batch.add(get1);
110+
batch.add(get2);
111+
table.batch(batch, results);
112+
Assert.assertTrue("Should be empty", ((Result) results[0]).isEmpty());
113+
Assert.assertTrue("Should be empty", ((Result) results[1]).isEmpty());
114+
115+
table.close();
116+
}
117+
118+
/**
119+
* Requirement 8.1
120+
*/
121+
@Test
122+
public void testBatchIncrement() throws IOException, InterruptedException {
123+
// Initialize data
124+
HTableInterface table = connection.getTable(TABLE_NAME);
125+
byte[] rowKey1 = dataHelper.randomData("testrow-");
126+
byte[] qual1 = dataHelper.randomData("qual-");
127+
Random random = new Random();
128+
long value1 = random.nextLong();
129+
byte[] rowKey2 = dataHelper.randomData("testrow-");
130+
byte[] qual2 = dataHelper.randomData("qual-");
131+
long value2 = random.nextLong();
132+
133+
// Put
134+
Put put1 = new Put(rowKey1).add(COLUMN_FAMILY, qual1, Bytes.toBytes(value1));
135+
Put put2 = new Put(rowKey2).add(COLUMN_FAMILY, qual2, Bytes.toBytes(value2));
136+
List<Row> batch = new ArrayList<Row>(2);
137+
batch.add(put1);
138+
batch.add(put2);
139+
table.batch(batch, null);
140+
141+
// Increment
142+
Increment increment1 = new Increment(rowKey1).addColumn(COLUMN_FAMILY, qual1, 1L);
143+
Increment increment2 = new Increment(rowKey2).addColumn(COLUMN_FAMILY, qual2, 1L);
144+
batch.clear();
145+
batch.add(increment1);
146+
batch.add(increment2);
147+
Object[] results = new Object[2];
148+
table.batch(batch, results);
149+
Assert.assertEquals("Should be value1 + 1", value1 + 1, Bytes.toLong(
150+
CellUtil.cloneValue(((Result) results[0]).getColumnLatestCell(COLUMN_FAMILY, qual1))));
151+
Assert.assertEquals("Should be value2 + 1", value2 + 1, Bytes.toLong(
152+
CellUtil.cloneValue(((Result) results[1]).getColumnLatestCell(COLUMN_FAMILY, qual2))));
153+
154+
table.close();
155+
}
156+
157+
/**
158+
* Requirement 8.1
159+
*/
160+
@Test
161+
public void testBatchAppend() throws IOException, InterruptedException {
162+
// Initialize data
163+
HTableInterface table = connection.getTable(TABLE_NAME);
164+
byte[] rowKey1 = dataHelper.randomData("testrow-");
165+
byte[] qual1 = dataHelper.randomData("qual-");
166+
byte[] value1_1 = dataHelper.randomData("value-");
167+
byte[] value1_2 = dataHelper.randomData("value-");
168+
byte[] rowKey2 = dataHelper.randomData("testrow-");
169+
byte[] qual2 = dataHelper.randomData("qual-");
170+
byte[] value2_1 = dataHelper.randomData("value-");
171+
byte[] value2_2 = dataHelper.randomData("value-");
172+
173+
// Put
174+
Put put1 = new Put(rowKey1).add(COLUMN_FAMILY, qual1, value1_1);
175+
Put put2 = new Put(rowKey2).add(COLUMN_FAMILY, qual2, value2_1);
176+
List<Row> batch = new ArrayList<Row>(2);
177+
batch.add(put1);
178+
batch.add(put2);
179+
table.batch(batch, null);
180+
181+
// Increment
182+
Append append1 = new Append(rowKey1).add(COLUMN_FAMILY, qual1, value1_2);
183+
Append append2 = new Append(rowKey2).add(COLUMN_FAMILY, qual2, value2_2);
184+
batch.clear();
185+
batch.add(append1);
186+
batch.add(append2);
187+
Object[] results = new Object[2];
188+
table.batch(batch, results);
189+
Assert.assertArrayEquals("Should be value1_1 + value1_2", ArrayUtils.addAll(value1_1, value1_2),
190+
CellUtil.cloneValue(((Result) results[0]).getColumnLatestCell(COLUMN_FAMILY, qual1)));
191+
Assert.assertArrayEquals("Should be value1_1 + value1_2", ArrayUtils.addAll(value2_1, value2_2),
192+
CellUtil.cloneValue(((Result) results[1]).getColumnLatestCell(COLUMN_FAMILY, qual2)));
193+
194+
table.close();
195+
}
196+
197+
/**
198+
* Requirement 8.2 - Batch throws an exception if any of the calls failed. Any successful
199+
* mutations that did not throw an exception will succeed.
200+
*
201+
* Requirement 8.7 - Server errors should populate those return elements in a batch() call with
202+
* corresponding Throwables.
203+
*/
204+
@Test
205+
public void testBatchWithException() throws IOException, InterruptedException {
206+
// Initialize data
207+
HTableInterface table = connection.getTable(TABLE_NAME);
208+
byte[][] rowKeys = dataHelper.randomData("testrow-", 5);
209+
byte[][] quals = dataHelper.randomData("qual-", 5);
210+
byte[][] values = dataHelper.randomData("value-", 5);
211+
212+
Put put0 = new Put(rowKeys[0]).add(COLUMN_FAMILY, quals[0], values[0]);
213+
Put put1 = new Put(rowKeys[1]).add(Bytes.toBytes("NO SUCH FAMILY"), quals[1], values[1]);
214+
Put put2 = new Put(rowKeys[2]).add(COLUMN_FAMILY, quals[2], values[2]);
215+
Put put3 = new Put(rowKeys[3]).add(Bytes.toBytes("NO SUCH FAMILY"), quals[3], values[3]);
216+
Put put4 = new Put(rowKeys[4]).add(COLUMN_FAMILY, quals[4], values[4]);
217+
List<Row> batch = new ArrayList<Row>(5);
218+
Object[] results = new Object[5];
219+
batch.add(put0);
220+
batch.add(put1); // This one is bad
221+
batch.add(put2);
222+
batch.add(put3); // So's this one
223+
batch.add(put4);
224+
RetriesExhaustedWithDetailsException exception = null;
225+
try {
226+
table.batch(batch, results);
227+
} catch (RetriesExhaustedWithDetailsException e) {
228+
exception = e;
229+
}
230+
Assert.assertNotNull("Exception should have been thrown", exception);
231+
Assert.assertEquals("There should have been two exceptions", 2, exception.getNumExceptions());
232+
Assert.assertTrue("Cause should be NoSuchColumnFamilyException",
233+
exception.getCause(0) instanceof NoSuchColumnFamilyException);
234+
Assert.assertArrayEquals("Row key should be #1", rowKeys[1], exception.getRow(0).getRow());
235+
Assert.assertTrue("Cause should be NoSuchColumnFamilyException",
236+
exception.getCause(1) instanceof NoSuchColumnFamilyException);
237+
Assert.assertArrayEquals("Row key should be #3", rowKeys[3], exception.getRow(1).getRow());
238+
Assert.assertTrue("#0 should be a Result", results[0] instanceof Result);
239+
Assert.assertTrue("#1 should be the exception cause",
240+
results[1] instanceof NoSuchColumnFamilyException);
241+
Assert.assertTrue("#2 should be a Result", results[2] instanceof Result);
242+
Assert.assertTrue("#3 should be the exception cause",
243+
results[3] instanceof NoSuchColumnFamilyException);
244+
Assert.assertTrue("#4 should be a Result", results[4] instanceof Result);
245+
246+
// Check values. The good puts should have worked.
247+
List<Get> gets = new ArrayList<Get>(5);
248+
for (int i = 0; i < 5; ++i) {
249+
gets.add(new Get(rowKeys[i]));
250+
}
251+
Result[] getResults = table.get(gets);
252+
Assert.assertArrayEquals("Row #0 should have value #0", values[0],
253+
CellUtil.cloneValue(getResults[0].getColumnLatestCell(COLUMN_FAMILY, quals[0])));
254+
Assert.assertTrue("Row #1 should be empty", getResults[1].isEmpty());
255+
Assert.assertArrayEquals("Row #2 should have value #2", values[2],
256+
CellUtil.cloneValue(getResults[2].getColumnLatestCell(COLUMN_FAMILY, quals[2])));
257+
Assert.assertTrue("Row #3 should be empty", getResults[3].isEmpty());
258+
Assert.assertArrayEquals("Row #4 should have value #4", values[4],
259+
CellUtil.cloneValue(getResults[4].getColumnLatestCell(COLUMN_FAMILY, quals[4])));
260+
261+
table.close();
262+
}
263+
264+
/**
265+
* Requirement 8.3 - MutateRow performs a combination of Put and Delete operations for a single
266+
* row.
267+
*/
268+
@Test
269+
public void testRowMutations() throws IOException, InterruptedException {
270+
// Initialize data
271+
HTableInterface table = connection.getTable(TABLE_NAME);
272+
byte[] rowKey = dataHelper.randomData("testrow-");
273+
byte[][] quals = dataHelper.randomData("qual-", 3);
274+
byte[][] values = dataHelper.randomData("value-", 3);
275+
276+
// Put a couple of values
277+
Put put0 = new Put(rowKey).add(COLUMN_FAMILY, quals[0], values[0]);
278+
Put put1 = new Put(rowKey).add(COLUMN_FAMILY, quals[1], values[1]);
279+
RowMutations rm = new RowMutations(rowKey);
280+
rm.add(put0);
281+
rm.add(put1);
282+
table.mutateRow(rm);
283+
284+
// Check
285+
Result result = table.get(new Get(rowKey));
286+
Assert.assertEquals("Should have two values", 2, result.size());
287+
Assert.assertArrayEquals("Value #0 should exist", values[0],
288+
CellUtil.cloneValue(result.getColumnLatestCell(COLUMN_FAMILY, quals[0])));
289+
Assert.assertArrayEquals("Value #1 should exist", values[1],
290+
CellUtil.cloneValue(result.getColumnLatestCell(COLUMN_FAMILY, quals[1])));
291+
292+
// Now delete the value #1 and insert value #3
293+
Delete delete = new Delete(rowKey).deleteColumn(COLUMN_FAMILY, quals[1]);
294+
Put put2 = new Put(rowKey).add(COLUMN_FAMILY, quals[2], values[2]);
295+
rm = new RowMutations(rowKey);
296+
rm.add(delete);
297+
rm.add(put2);
298+
table.mutateRow(rm);
299+
300+
// Check
301+
result = table.get(new Get(rowKey));
302+
Assert.assertEquals("Should have two values", 2, result.size());
303+
Assert.assertArrayEquals("Value #0 should exist", values[0],
304+
CellUtil.cloneValue(result.getColumnLatestCell(COLUMN_FAMILY, quals[0])));
305+
Assert.assertArrayEquals("Value #2 should exist", values[2],
306+
CellUtil.cloneValue(result.getColumnLatestCell(COLUMN_FAMILY, quals[2])));
307+
308+
table.close();
309+
}
310+
}

src/test/java/com/google/cloud/anviltop/hbase/TestIncrement.java

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ public class TestIncrement extends AbstractTest {
4444

4545
/**
4646
* Requirement 6.1 - Increment on or more columns in a given row by given amounts.
47+
* Requirement 6.4 - Return post-increment value(s)
4748
*/
4849
@Test
4950
public void testIncrement() throws IOException {
@@ -89,24 +90,17 @@ public void testIncrement() throws IOException {
8990
* Requirement 6.2 - Specify a timerange (min ts, inclusive + max ts, exclusive). This will create
9091
* a new value that is an increment of the first value within this range, and will otherwise
9192
* create a new value.
92-
*
9393
* Note: This is pretty weird. Not sure who would use it, or if we need to support it.
94-
*
95-
* Here's the test:
96-
* 1. Create a cell with three explicit versions: 101, 102, and 103.
97-
* The values are set to the same so we can track the original versions.
98-
* 2. Call an increment (+1) with a time range of [101,103)
99-
* 3. It should have created a fourth version with the current timestamp that
100-
* incremented version 102. We can ensure this be looking at the new value
101-
* (103) and the number of versions.
102-
* 4. Now increment (+1) with a time range outside of all versions [100000, 200000)
103-
* 5. It should have create a new version with the current timestamp and value of 1.
104-
* 6. Check all versions for the cell. You should have, in descending version order:
105-
* a. A value of 1 with a recent timestamp.
106-
* b. A value of 103 with a recent timestamp.
107-
* c. A value of 103 with a timestamp of 103.
108-
* d. A value of 102 with a timestamp of 102.
109-
* e. A value of 101 with a timestamp of 101.
94+
* Here's the test: 1. Create a cell with three explicit versions: 101, 102, and 103. The values
95+
* are set to the same so we can track the original versions. 2. Call an increment (+1) with a
96+
* time range of [101,103) 3. It should have created a fourth version with the current timestamp
97+
* that incremented version 102. We can ensure this be looking at the new value (103) and the
98+
* number of versions. 4. Now increment (+1) with a time range outside of all versions [100000,
99+
* 200000) 5. It should have create a new version with the current timestamp and value of 1. 6.
100+
* Check all versions for the cell. You should have, in descending version order: a. A value of 1
101+
* with a recent timestamp. b. A value of 103 with a recent timestamp. c. A value of 103 with a
102+
* timestamp of 103. d. A value of 102 with a timestamp of 102. e. A value of 101 with a timestamp
103+
* of 101.
110104
*/
111105
@Test
112106
public void testIncrementWithTimerange() throws IOException {
@@ -170,7 +164,8 @@ public void testIncrementWithTimerange() throws IOException {
170164
}
171165

172166
/**
173-
* Test that increment uses the current time as the default timestamp for new versions.
167+
* Requirement 6.3 - Test that increment uses the current time as the default timestamp for new
168+
* versions.
174169
*/
175170
@Test
176171
public void testDefaultTimestamp() throws IOException {
@@ -207,6 +202,9 @@ public void testDefaultTimestamp() throws IOException {
207202
table.close();
208203
}
209204

205+
/**
206+
* Requirement 6.6 - Increment should fail on non-64-bit values, and succeed on any 64-bit value.
207+
*/
210208
@Test
211209
public void testFailOnIncrementInt() throws IOException {
212210
// Initialize
@@ -224,6 +222,9 @@ public void testFailOnIncrementInt() throws IOException {
224222
table.increment(increment);
225223
}
226224

225+
/**
226+
* Requirement 6.6
227+
*/
227228
@Test
228229
public void testFailOnIncrementString() throws IOException {
229230
// Initialize
@@ -242,7 +243,7 @@ public void testFailOnIncrementString() throws IOException {
242243
}
243244

244245
/**
245-
* HBase should increment an 8-byte array just like it would a long.
246+
* Requirement 6.6
246247
*/
247248
@Test
248249
public void testIncrementEightBytes() throws IOException {

0 commit comments

Comments
 (0)