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());

Reply via email to