This is an automated email from the ASF dual-hosted git repository. ctubbsii 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 208204bdd6 Add validate method to TabletMetadata (#5340) 208204bdd6 is described below commit 208204bdd6392348acd741b1f44e393fd56953be Author: Arbaaz Khan <bazzy...@yahoo.com> AuthorDate: Thu May 15 15:07:52 2025 -0400 Add validate method to TabletMetadata (#5340) Add a validate method to TabletMetadata to ensure tablet files have ranges that match the tablet in which they are located. --- .../core/metadata/schema/TabletMetadata.java | 25 ++++++++++++- .../core/metadata/schema/TabletMetadataTest.java | 43 ++++++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/apache/accumulo/core/metadata/schema/TabletMetadata.java b/core/src/main/java/org/apache/accumulo/core/metadata/schema/TabletMetadata.java index 8d0f84a60d..b51890b6ca 100644 --- a/core/src/main/java/org/apache/accumulo/core/metadata/schema/TabletMetadata.java +++ b/core/src/main/java/org/apache/accumulo/core/metadata/schema/TabletMetadata.java @@ -63,6 +63,7 @@ import org.apache.accumulo.core.clientImpl.TabletAvailabilityUtil; import org.apache.accumulo.core.data.ArrayByteSequence; import org.apache.accumulo.core.data.ByteSequence; import org.apache.accumulo.core.data.Key; +import org.apache.accumulo.core.data.Range; import org.apache.accumulo.core.data.TableId; import org.apache.accumulo.core.data.Value; import org.apache.accumulo.core.dataImpl.KeyExtent; @@ -846,6 +847,26 @@ public class TabletMetadata { .map(address -> new TServerInstance(address, stat.getEphemeralOwner())); } + public static void validate(TabletMetadata tm) { + if (tm.files.isEmpty() || !tm.fetchedCols.contains(ColumnType.PREV_ROW)) { + return; + } + + Collection<StoredTabletFile> files = tm.getFiles(); + Range tabletRange = tm.getExtent().toDataRange(); + + for (StoredTabletFile file : files) { + if (!isFileRangeValid(file, tabletRange)) { + throw new IllegalStateException( + "File range " + file.getRange() + " does not overlap with tablet range " + tabletRange); + } + } + } + + private static boolean isFileRangeValid(StoredTabletFile file, Range tabletRange) { + return !file.hasRange() || tabletRange.clip(file.getRange(), true) != null; + } + static class Builder { private TableId tableId; private Text prevEndRow; @@ -1006,7 +1027,9 @@ public class TabletMetadata { TabletMetadata build(EnumSet<ColumnType> fetchedCols) { this.fetchedCols = fetchedCols; - return new TabletMetadata(this); + TabletMetadata tm = new TabletMetadata(this); + validate(tm); + return tm; } } } diff --git a/core/src/test/java/org/apache/accumulo/core/metadata/schema/TabletMetadataTest.java b/core/src/test/java/org/apache/accumulo/core/metadata/schema/TabletMetadataTest.java index c974493f6d..2256ab5ca3 100644 --- a/core/src/test/java/org/apache/accumulo/core/metadata/schema/TabletMetadataTest.java +++ b/core/src/test/java/org/apache/accumulo/core/metadata/schema/TabletMetadataTest.java @@ -35,9 +35,11 @@ import static org.apache.accumulo.core.metadata.schema.TabletMetadata.ColumnType import static org.apache.accumulo.core.metadata.schema.TabletMetadata.ColumnType.LAST; import static org.apache.accumulo.core.metadata.schema.TabletMetadata.ColumnType.LOCATION; import static org.apache.accumulo.core.metadata.schema.TabletMetadata.ColumnType.MERGED; +import static org.apache.accumulo.core.metadata.schema.TabletMetadata.ColumnType.PREV_ROW; import static org.apache.accumulo.core.metadata.schema.TabletMetadata.ColumnType.SUSPEND; import static org.apache.accumulo.core.metadata.schema.TabletMetadata.ColumnType.UNSPLITTABLE; import static org.apache.accumulo.core.metadata.schema.TabletMetadata.ColumnType.USER_COMPACTION_REQUESTED; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; @@ -374,6 +376,45 @@ public class TabletMetadataTest { assertTrue(closeCalled.get()); } + @Test + public void testValidateWithNonOverlappingFileRange() { + KeyExtent extent = new KeyExtent(TableId.of("1"), new Text("d"), new Text("b")); + + Range fileRange = new Range(new Text("x\0"), true, new Text("z\0"), false); + StoredTabletFile file = + StoredTabletFile.of(new Path("file:///accumulo/tables/t-0/b-0/f1.rf"), fileRange); + TabletMetadataBuilder builder = + TabletMetadata.builder(extent).putFile(file, new DataFileValue(0, 0, 0)); + + assertThrows(IllegalStateException.class, () -> builder.build(ColumnType.values())); + } + + @Test + public void testValidateWithOverlappingFileRange() { + KeyExtent extent = new KeyExtent(TableId.of("2"), new Text("m"), new Text("a")); + + Range fileRange = new Range(new Text("c\0"), true, new Text("e\0"), false); + StoredTabletFile file = + StoredTabletFile.of(new Path("file:///accumulo/tables/t-0/b-0/f2.rf"), fileRange); + TabletMetadataBuilder builder = + TabletMetadata.builder(extent).putFile(file, new DataFileValue(0, 0, 0)); + + assertDoesNotThrow(() -> builder.build(ColumnType.values())); + } + + @Test + public void testValidateWithNoFileRange() { + KeyExtent extent = new KeyExtent(TableId.of("3"), new Text("d"), new Text("b")); + + Range emptyRange = new Range(); + StoredTabletFile file = + StoredTabletFile.of(new Path("file:///accumulo/tables/t-0/b-0/f3.rf"), emptyRange); + TabletMetadataBuilder builder = + TabletMetadata.builder(extent).putFile(file, new DataFileValue(0, 0, 0)); + + assertDoesNotThrow(() -> builder.build(ColumnType.values())); + } + @Test public void testTmBuilderImmutable() { TabletMetadata.Builder b = new Builder(); @@ -414,6 +455,7 @@ public class TabletMetadataTest { .add(FateId.from(FateInstanceType.USER, UUID.randomUUID()))); // Set some data in the collections and very they are not empty but still immutable + b.table(TableId.of("4")); b.extCompaction(ecid, ecMeta); b.file(stf, new DataFileValue(0, 0, 0)); b.log(LogEntry.fromPath("localhost+8020/" + UUID.randomUUID())); @@ -422,6 +464,7 @@ public class TabletMetadataTest { b.compacted(FateId.from(FateInstanceType.USER, UUID.randomUUID())); b.userCompactionsRequested(FateId.from(FateInstanceType.USER, UUID.randomUUID())); b.keyValue(new AbstractMap.SimpleImmutableEntry<>(new Key(), new Value())); + b.sawPrevEndRow(true); var tm2 = b.build(EnumSet.allOf(ColumnType.class)); assertEquals(1, tm2.getExternalCompactions().size());