blob: 47c41267d181017996046f48724c1196f6601727 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.accumulo.test;
import static org.apache.accumulo.core.Constants.MAX_TABLE_NAME_LEN;
import static org.apache.accumulo.fate.util.UtilWaitThread.sleepUninterruptibly;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.TimeUnit;
import org.apache.accumulo.core.client.Accumulo;
import org.apache.accumulo.core.client.AccumuloClient;
import org.apache.accumulo.core.client.AccumuloException;
import org.apache.accumulo.core.client.AccumuloSecurityException;
import org.apache.accumulo.core.client.BatchWriter;
import org.apache.accumulo.core.client.IteratorSetting;
import org.apache.accumulo.core.client.Scanner;
import org.apache.accumulo.core.client.TableExistsException;
import org.apache.accumulo.core.client.TableNotFoundException;
import org.apache.accumulo.core.client.admin.DiskUsage;
import org.apache.accumulo.core.client.admin.TableOperations;
import org.apache.accumulo.core.clientImpl.ClientContext;
import org.apache.accumulo.core.conf.Property;
import org.apache.accumulo.core.constraints.DefaultKeySizeConstraint;
import org.apache.accumulo.core.data.Key;
import org.apache.accumulo.core.data.Mutation;
import org.apache.accumulo.core.data.PartialKey;
import org.apache.accumulo.core.data.Value;
import org.apache.accumulo.core.security.Authorizations;
import org.apache.accumulo.core.security.TablePermission;
import org.apache.accumulo.harness.AccumuloClusterHarness;
import org.apache.accumulo.test.functional.BadIterator;
import org.apache.accumulo.test.functional.FunctionalTestUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.hadoop.io.Text;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.google.common.collect.Sets;
public class TableOperationsIT extends AccumuloClusterHarness {
private AccumuloClient accumuloClient;
@Override
public int defaultTimeoutSeconds() {
return 90;
}
@Before
public void setup() {
accumuloClient = Accumulo.newClient().from(getClientProps()).build();
}
@After
public void checkForDanglingFateLocks() {
if (getClusterType() == ClusterType.MINI) {
FunctionalTestUtils.assertNoDanglingFateLocks((ClientContext) accumuloClient, getCluster());
accumuloClient.close();
}
}
@Test
public void getDiskUsageErrors() throws TableExistsException, AccumuloException,
AccumuloSecurityException, TableNotFoundException {
String tableName = getUniqueNames(1)[0];
accumuloClient.tableOperations().create(tableName);
List<DiskUsage> diskUsage =
accumuloClient.tableOperations().getDiskUsage(Collections.singleton(tableName));
assertEquals(1, diskUsage.size());
assertEquals(0, (long) diskUsage.get(0).getUsage());
assertEquals(tableName, diskUsage.get(0).getTables().iterator().next());
accumuloClient.securityOperations().revokeTablePermission(getAdminPrincipal(), tableName,
TablePermission.READ);
assertThrows(AccumuloSecurityException.class,
() -> accumuloClient.tableOperations().getDiskUsage(Collections.singleton(tableName)));
accumuloClient.tableOperations().delete(tableName);
assertThrows(TableNotFoundException.class,
() -> accumuloClient.tableOperations().getDiskUsage(Collections.singleton(tableName)));
}
@Test
public void getDiskUsage() throws TableExistsException, AccumuloException,
AccumuloSecurityException, TableNotFoundException {
final String[] names = getUniqueNames(2);
String tableName = names[0];
accumuloClient.tableOperations().create(tableName);
// verify 0 disk usage
List<DiskUsage> diskUsages =
accumuloClient.tableOperations().getDiskUsage(Collections.singleton(tableName));
assertEquals(1, diskUsages.size());
assertEquals(1, diskUsages.get(0).getTables().size());
assertEquals(Long.valueOf(0), diskUsages.get(0).getUsage());
assertEquals(tableName, diskUsages.get(0).getTables().first());
// add some data
try (BatchWriter bw = accumuloClient.createBatchWriter(tableName)) {
Mutation m = new Mutation("a");
m.put("b", "c", new Value("abcde"));
bw.addMutation(m);
bw.flush();
}
accumuloClient.tableOperations().compact(tableName, new Text("A"), new Text("z"), true, true);
// verify we have usage
diskUsages = accumuloClient.tableOperations().getDiskUsage(Collections.singleton(tableName));
assertEquals(1, diskUsages.size());
assertEquals(1, diskUsages.get(0).getTables().size());
assertTrue(diskUsages.get(0).getUsage() > 0);
assertEquals(tableName, diskUsages.get(0).getTables().first());
String newTable = names[1];
// clone table
accumuloClient.tableOperations().clone(tableName, newTable, false, null, null);
// verify tables are exactly the same
Set<String> tables = new HashSet<>();
tables.add(tableName);
tables.add(newTable);
diskUsages = accumuloClient.tableOperations().getDiskUsage(tables);
assertEquals(1, diskUsages.size());
assertEquals(2, diskUsages.get(0).getTables().size());
assertTrue(diskUsages.get(0).getUsage() > 0);
accumuloClient.tableOperations().compact(tableName, new Text("A"), new Text("z"), true, true);
accumuloClient.tableOperations().compact(newTable, new Text("A"), new Text("z"), true, true);
// verify tables have differences
diskUsages = accumuloClient.tableOperations().getDiskUsage(tables);
assertEquals(2, diskUsages.size());
assertEquals(1, diskUsages.get(0).getTables().size());
assertEquals(1, diskUsages.get(1).getTables().size());
assertTrue(diskUsages.get(0).getUsage() > 0);
assertTrue(diskUsages.get(1).getUsage() > 0);
accumuloClient.tableOperations().delete(tableName);
}
@Test
public void createTable() throws TableExistsException, AccumuloException,
AccumuloSecurityException, TableNotFoundException {
String tableName = getUniqueNames(1)[0];
accumuloClient.tableOperations().create(tableName);
Iterable<Map.Entry<String,String>> itrProps =
accumuloClient.tableOperations().getProperties(tableName);
Map<String,String> props = propsToMap(itrProps);
assertEquals(DefaultKeySizeConstraint.class.getName(),
props.get(Property.TABLE_CONSTRAINT_PREFIX + "1"));
accumuloClient.tableOperations().delete(tableName);
}
@Test
public void createTableWithTableNameLengthLimit()
throws AccumuloException, AccumuloSecurityException, TableExistsException {
TableOperations tableOps = accumuloClient.tableOperations();
String t0 = StringUtils.repeat('a', MAX_TABLE_NAME_LEN - 1);
tableOps.create(t0);
assertTrue(tableOps.exists(t0));
String t1 = StringUtils.repeat('b', MAX_TABLE_NAME_LEN);
tableOps.create(t1);
assertTrue(tableOps.exists(t1));
String t2 = StringUtils.repeat('c', MAX_TABLE_NAME_LEN + 1);
assertThrows(IllegalArgumentException.class, () -> tableOps.create(t2));
assertFalse(tableOps.exists(t2));
}
@Test
public void createMergeClonedTable() throws Exception {
String[] names = getUniqueNames(2);
String originalTable = names[0];
TableOperations tops = accumuloClient.tableOperations();
TreeSet<Text> splits =
Sets.newTreeSet(Arrays.asList(new Text("a"), new Text("b"), new Text("c"), new Text("d")));
tops.create(originalTable);
tops.addSplits(originalTable, splits);
try (BatchWriter bw = accumuloClient.createBatchWriter(originalTable)) {
for (Text row : splits) {
Mutation m = new Mutation(row);
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
m.put(Integer.toString(i), Integer.toString(j), Integer.toString(i + j));
}
}
bw.addMutation(m);
}
}
String clonedTable = names[1];
tops.clone(originalTable, clonedTable, true, null, null);
tops.merge(clonedTable, null, new Text("b"));
Map<String,Integer> rowCounts = new HashMap<>();
try (Scanner s = accumuloClient.createScanner(clonedTable, new Authorizations())) {
for (Entry<Key,Value> entry : s) {
final Key key = entry.getKey();
String row = key.getRow().toString();
String cf = key.getColumnFamily().toString(), cq = key.getColumnQualifier().toString();
String value = entry.getValue().toString();
if (rowCounts.containsKey(row)) {
rowCounts.put(row, rowCounts.get(row) + 1);
} else {
rowCounts.put(row, 1);
}
assertEquals(Integer.parseInt(cf) + Integer.parseInt(cq), Integer.parseInt(value));
}
}
Collection<Text> clonedSplits = tops.listSplits(clonedTable);
Set<Text> expectedSplits = Sets.newHashSet(new Text("b"), new Text("c"), new Text("d"));
for (Text clonedSplit : clonedSplits) {
assertTrue("Encountered unexpected split on the cloned table: " + clonedSplit,
expectedSplits.remove(clonedSplit));
}
assertTrue("Did not find all expected splits on the cloned table: " + expectedSplits,
expectedSplits.isEmpty());
}
private Map<String,String> propsToMap(Iterable<Map.Entry<String,String>> props) {
Map<String,String> map = new HashMap<>();
for (Map.Entry<String,String> prop : props) {
map.put(prop.getKey(), prop.getValue());
}
return map;
}
/** Compare only the row, column family and column qualifier. */
static class KeyRowColFColQComparator implements Comparator<Key> {
@Override
public int compare(Key k1, Key k2) {
return k1.compareTo(k2, PartialKey.ROW_COLFAM_COLQUAL);
}
}
static final KeyRowColFColQComparator COMPARE_KEY_TO_COLQ = new KeyRowColFColQComparator();
/** Test recovery from bad majc iterator via compaction cancel. */
@Test
public void testCompactEmptyTablesWithBadIterator_FailsAndCancel() throws TableExistsException,
AccumuloException, AccumuloSecurityException, TableNotFoundException {
String tableName = getUniqueNames(1)[0];
accumuloClient.tableOperations().create(tableName);
List<IteratorSetting> list = new ArrayList<>();
list.add(new IteratorSetting(15, BadIterator.class));
accumuloClient.tableOperations().compact(tableName, null, null, list, true, false); // don't
// block
sleepUninterruptibly(2, TimeUnit.SECONDS); // start compaction
accumuloClient.tableOperations().cancelCompaction(tableName);
try (Scanner scanner = accumuloClient.createScanner(tableName, Authorizations.EMPTY)) {
Map<Key,Value> actual = new TreeMap<>();
for (Map.Entry<Key,Value> entry : scanner)
actual.put(entry.getKey(), entry.getValue());
assertTrue("Should be empty. Actual is " + actual, actual.isEmpty());
accumuloClient.tableOperations().delete(tableName);
}
}
}