This is an automated email from the ASF dual-hosted git repository.
cshannon pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/accumulo.git
The following commit(s) were added to refs/heads/main by this push:
new ce84c9d143 Add tests to VolumeIT for files with ranges (#3793)
ce84c9d143 is described below
commit ce84c9d1434d3dcf354f167b6dd6021e5c396d5d
Author: Christopher L. Shannon <[email protected]>
AuthorDate: Fri Oct 20 08:41:39 2023 -0400
Add tests to VolumeIT for files with ranges (#3793)
This updates the volume replacement tests in VolumeIT to make sure that
volume replacement works correctly with fenced files that have ranges
set in metadata
This addresses part of #3766
---
.../java/org/apache/accumulo/test/VolumeIT.java | 93 ++++++++++++++++++++--
.../accumulo/test/util/FileMetadataUtil.java | 38 ++++++---
2 files changed, 111 insertions(+), 20 deletions(-)
diff --git a/test/src/main/java/org/apache/accumulo/test/VolumeIT.java
b/test/src/main/java/org/apache/accumulo/test/VolumeIT.java
index 4f231deb6a..2e7dd8a5b0 100644
--- a/test/src/main/java/org/apache/accumulo/test/VolumeIT.java
+++ b/test/src/main/java/org/apache/accumulo/test/VolumeIT.java
@@ -36,6 +36,7 @@ import java.util.List;
import java.util.Map.Entry;
import java.util.SortedSet;
import java.util.TreeSet;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import org.apache.accumulo.core.Constants;
@@ -64,15 +65,20 @@ import org.apache.accumulo.core.dataImpl.KeyExtent;
import org.apache.accumulo.core.metadata.MetadataTable;
import org.apache.accumulo.core.metadata.RootTable;
import org.apache.accumulo.core.metadata.StoredTabletFile;
+import org.apache.accumulo.core.metadata.schema.DataFileValue;
import
org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.DataFileColumnFamily;
import org.apache.accumulo.core.security.Authorizations;
+import org.apache.accumulo.core.security.TablePermission;
import org.apache.accumulo.miniclusterImpl.MiniAccumuloConfigImpl;
+import org.apache.accumulo.server.ServerContext;
import org.apache.accumulo.server.init.Initialize;
import org.apache.accumulo.server.log.WalStateManager;
import org.apache.accumulo.server.log.WalStateManager.WalMarkerException;
import org.apache.accumulo.server.log.WalStateManager.WalState;
+import org.apache.accumulo.server.security.SystemCredentials;
import org.apache.accumulo.server.util.Admin;
import org.apache.accumulo.test.functional.ConfigurableMacBase;
+import org.apache.accumulo.test.util.FileMetadataUtil;
import org.apache.commons.configuration2.PropertiesConfiguration;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
@@ -278,6 +284,11 @@ public class VolumeIT extends ConfigurableMacBase {
private void verifyVolumesUsed(AccumuloClient client, String tableName,
boolean shouldExist,
Path... paths) throws Exception {
+ verifyVolumesUsed(client, tableName, shouldExist, false, paths);
+ }
+
+ private void verifyVolumesUsed(AccumuloClient client, String tableName,
boolean shouldExist,
+ boolean rangedFiles, Path... paths) throws Exception {
if (!client.tableOperations().exists(tableName)) {
assertFalse(shouldExist);
@@ -346,7 +357,10 @@ public class VolumeIT extends ConfigurableMacBase {
sum += count;
}
- assertEquals(100, sum);
+ // When ranged files exist we there should be twice as many
+ // as the test split each file into 2
+ int expectedCount = rangedFiles ? 200 : 100;
+ assertEquals(expectedCount, sum);
}
}
@@ -392,7 +406,8 @@ public class VolumeIT extends ConfigurableMacBase {
}
}
- private void testReplaceVolume(AccumuloClient client, boolean cleanShutdown)
throws Exception {
+ private void testReplaceVolume(AccumuloClient client, boolean cleanShutdown,
boolean rangedFiles)
+ throws Exception {
String[] tableNames = getUniqueNames(3);
verifyVolumesUsed(client, tableNames[0], false, v1, v2);
@@ -403,6 +418,13 @@ public class VolumeIT extends ConfigurableMacBase {
writeData(tableNames[1], c2);
}
+ // If flag is true then for each file split and create two files
+ // to verify volume replacement works on files with ranges
+ if (rangedFiles) {
+ splitFilesWithRange(client, tableNames[0]);
+ splitFilesWithRange(client, tableNames[1]);
+ }
+
if (cleanShutdown) {
assertEquals(0, cluster.exec(Admin.class,
"stopAll").getProcess().waitFor());
}
@@ -428,15 +450,16 @@ public class VolumeIT extends ConfigurableMacBase {
// start cluster and verify that volumes were replaced
cluster.start();
- verifyVolumesUsed(client, tableNames[0], true, v8, v9);
- verifyVolumesUsed(client, tableNames[1], true, v8, v9);
+ verifyVolumesUsed(client, tableNames[0], true, rangedFiles, v8, v9);
+ verifyVolumesUsed(client, tableNames[1], true, rangedFiles, v8, v9);
// verify writes to new dir
client.tableOperations().compact(tableNames[0], null, null, true, true);
client.tableOperations().compact(tableNames[1], null, null, true, true);
- verifyVolumesUsed(client, tableNames[0], true, v8, v9);
- verifyVolumesUsed(client, tableNames[1], true, v8, v9);
+ // Always pass false for ranged files as compaction will clean them up if
exist
+ verifyVolumesUsed(client, tableNames[0], true, false, v8, v9);
+ verifyVolumesUsed(client, tableNames[1], true, false, v8, v9);
client.tableOperations().compact(RootTable.NAME, new
CompactionConfig().setWait(true));
@@ -465,14 +488,28 @@ public class VolumeIT extends ConfigurableMacBase {
@Test
public void testCleanReplaceVolumes() throws Exception {
try (AccumuloClient client =
Accumulo.newClient().from(getClientProperties()).build()) {
- testReplaceVolume(client, true);
+ testReplaceVolume(client, true, false);
}
}
@Test
public void testDirtyReplaceVolumes() throws Exception {
try (AccumuloClient client =
Accumulo.newClient().from(getClientProperties()).build()) {
- testReplaceVolume(client, false);
+ testReplaceVolume(client, false, false);
+ }
+ }
+
+ @Test
+ public void testCleanReplaceVolumesWithRangedFiles() throws Exception {
+ try (AccumuloClient client =
Accumulo.newClient().from(getClientProperties()).build()) {
+ testReplaceVolume(client, true, true);
+ }
+ }
+
+ @Test
+ public void testDirtyReplaceVolumesWithRangedFiles() throws Exception {
+ try (AccumuloClient client =
Accumulo.newClient().from(getClientProperties()).build()) {
+ testReplaceVolume(client, false, true);
}
}
@@ -488,4 +525,44 @@ public class VolumeIT extends ConfigurableMacBase {
config.write(out);
}
}
+
+ // Go through each tablet file in metadata and split the files into two files
+ // by adding two new entries that covers half of the file. This will test
that
+ // files with ranges work properly with volume replacement
+ private void splitFilesWithRange(AccumuloClient client, String tableName)
throws Exception {
+
client.securityOperations().grantTablePermission(cluster.getConfig().getRootUserName(),
+ MetadataTable.NAME, TablePermission.WRITE);
+ final ServerContext ctx = getServerContext();
+ ctx.setCredentials(new
SystemCredentials(client.instanceOperations().getInstanceId(), "root",
+ new PasswordToken(ROOT_PASSWORD)));
+
+ AtomicInteger i = new AtomicInteger();
+ FileMetadataUtil.mutateTabletFiles(ctx, tableName, null, null, (tm,
mutator, file, value) -> {
+ i.incrementAndGet();
+
+ // Create a mutation to delete the existing file metadata entry with
infinite range
+ mutator.deleteFile(file);
+
+ // Find the midpoint and create two new files, each with a range
covering half the file
+ Text tabletMidPoint = getTabletMidPoint(tm.getExtent().endRow());
+ // Handle edge case for last tablet
+ if (tabletMidPoint == null) {
+ tabletMidPoint = new Text(
+ String.format("%06d",
Integer.parseInt(tm.getExtent().prevEndRow().toString()) + 50));
+ }
+
+ final DataFileValue newValue = new DataFileValue(Integer.max(1, (int)
(value.getSize() / 2)),
+ Integer.max(1, (int) (value.getNumEntries() / 2)));
+ mutator.putFile(StoredTabletFile.of(file.getPath(),
+ new Range(tm.getExtent().prevEndRow(), tabletMidPoint)), newValue);
+ mutator.putFile(
+ StoredTabletFile.of(file.getPath(), new Range(tabletMidPoint,
tm.getExtent().endRow())),
+ newValue);
+ });
+ }
+
+ private static Text getTabletMidPoint(Text row) {
+ return row != null ? new Text(String.format("%06d",
Integer.parseInt(row.toString()) - 50))
+ : null;
+ }
}
diff --git
a/test/src/main/java/org/apache/accumulo/test/util/FileMetadataUtil.java
b/test/src/main/java/org/apache/accumulo/test/util/FileMetadataUtil.java
index b4bc67d854..9502dca9d9 100644
--- a/test/src/main/java/org/apache/accumulo/test/util/FileMetadataUtil.java
+++ b/test/src/main/java/org/apache/accumulo/test/util/FileMetadataUtil.java
@@ -102,8 +102,25 @@ public class FileMetadataUtil {
public static void splitFilesIntoRanges(final ServerContext ctx, String
tableName,
Text tabletStartRow, Text tabletEndRow, Set<Range> fileRanges) throws
Exception {
+
Preconditions.checkArgument(!fileRanges.isEmpty(), "Ranges must not be
empty");
+ mutateTabletFiles(ctx, tableName, tabletStartRow, tabletEndRow, (tm,
mutator, file, value) -> {
+ // Create a mutation to delete the existing file metadata entry with
infinite range
+ mutator.deleteFile(file);
+
+ fileRanges.forEach(range -> {
+ final DataFileValue newValue =
+ new DataFileValue(Integer.max(1, (int) (value.getSize() /
fileRanges.size())),
+ Integer.max(1, (int) (value.getNumEntries() /
fileRanges.size())));
+ mutator.putFile(StoredTabletFile.of(file.getPath(), range), newValue);
+ });
+ });
+ }
+
+ public static void mutateTabletFiles(final ServerContext ctx, String
tableName,
+ Text tabletStartRow, Text tabletEndRow, FileMutator fileMutator) throws
Exception {
+
final TableId tableId =
TableId.of(ctx.tableOperations().tableIdMap().get(tableName));
// Bring tablet offline so we can modify file metadata
@@ -113,35 +130,32 @@ public class FileMetadataUtil {
ctx.getAmple().readTablets().forTable(tableId).overlapping(tabletStartRow,
tabletEndRow)
.fetch(ColumnType.FILES, ColumnType.PREV_ROW).build()) {
- // Read each file and split to 10 ranges
+ // Process each tablet in the given start/end row range
for (TabletMetadata tabletMetadata : tabletsMetadata) {
final KeyExtent ke = tabletMetadata.getExtent();
- // Create a mutation to delete the existing file metadata entry with
infinite range
TabletMutator mutator = ctx.getAmple().mutateTablet(ke);
- // Read each files and split into the given ranges
+ // Read each file and mutate
for (Entry<StoredTabletFile,DataFileValue> fileEntry :
tabletMetadata.getFilesMap()
.entrySet()) {
StoredTabletFile file = fileEntry.getKey();
DataFileValue value = fileEntry.getValue();
- // Create a mutation to delete the existing file metadata entry with
infinite range
- mutator.deleteFile(file);
-
- fileRanges.forEach(range -> {
- final DataFileValue newValue =
- new DataFileValue(Integer.max(1, (int) (value.getSize() /
fileRanges.size())),
- Integer.max(1, (int) (value.getNumEntries() /
fileRanges.size())));
- mutator.putFile(StoredTabletFile.of(file.getPath(), range),
newValue);
- });
+ // do any file mutations
+ fileMutator.mutate(tabletMetadata, mutator, file, value);
mutator.mutate();
}
}
}
+ // Bring back online after metadata updates
ctx.tableOperations().online(tableName, true);
}
+ public interface FileMutator {
+ void mutate(TabletMetadata tm, TabletMutator mutator, StoredTabletFile
file,
+ DataFileValue value);
+ }
}