Phillippko commented on code in PR #7748:
URL: https://github.com/apache/ignite-3/pull/7748#discussion_r2922971857


##########
modules/raft/src/test/java/org/apache/ignite/internal/raft/storage/segstore/GroupIndexMetaTest.java:
##########
@@ -317,17 +317,98 @@ void testOnIndexCompactedWithMultipleBlocks() {
         var compactedMeta1 = new IndexFileMeta(1, 100, 0, new 
FileProperties(0, 1));
         groupMeta.onIndexCompacted(new FileProperties(0), compactedMeta1);
 
-        assertThat(groupMeta.indexMeta(1), is(compactedMeta1));
-        assertThat(groupMeta.indexMeta(42), is(meta2));
-        assertThat(groupMeta.indexMeta(100), is(meta3));
+        assertThat(groupMeta.indexMetaByLogIndex(1), is(compactedMeta1));
+        assertThat(groupMeta.indexMetaByLogIndex(42), is(meta2));
+        assertThat(groupMeta.indexMetaByLogIndex(100), is(meta3));
 
         // Compact meta3 from the newer block.
         var compactedMeta3 = new IndexFileMeta(100, 200, 84, new 
FileProperties(2, 1));
         groupMeta.onIndexCompacted(new FileProperties(2), compactedMeta3);
 
-        assertThat(groupMeta.indexMeta(1), is(compactedMeta1));
-        assertThat(groupMeta.indexMeta(42), is(meta2));
-        assertThat(groupMeta.indexMeta(100), is(compactedMeta3));
+        assertThat(groupMeta.indexMetaByLogIndex(1), is(compactedMeta1));
+        assertThat(groupMeta.indexMetaByLogIndex(42), is(meta2));
+        assertThat(groupMeta.indexMetaByLogIndex(100), is(compactedMeta3));
+    }
+
+    @Test
+    void testIndexMetaByFileOrdinal() {
+        var meta1 = new IndexFileMeta(1, 50, 0, new FileProperties(1));
+        var meta2 = new IndexFileMeta(50, 100, 42, new FileProperties(2));
+        var meta3 = new IndexFileMeta(100, 150, 84, new FileProperties(3));
+
+        var groupMeta = new GroupIndexMeta(meta1);
+        groupMeta.addIndexMeta(meta2);
+        groupMeta.addIndexMeta(meta3);
+
+        assertThat(groupMeta.indexMetaByFileOrdinal(1), is(meta1));
+        assertThat(groupMeta.indexMetaByFileOrdinal(2), is(meta2));
+        assertThat(groupMeta.indexMetaByFileOrdinal(3), is(meta3));
+
+        // Ordinals before the first and after the last return null.
+        assertThat(groupMeta.indexMetaByFileOrdinal(0), is(nullValue()));
+        assertThat(groupMeta.indexMetaByFileOrdinal(4), is(nullValue()));
+    }
+
+    @Test
+    void testIndexMetaByFileOrdinalWithMultipleBlocks() {
+        // meta1 is in block 0.
+        var meta1 = new IndexFileMeta(1, 100, 0, new FileProperties(0));
+        var groupMeta = new GroupIndexMeta(meta1);
+
+        // meta2 overlaps meta1, creating a second deque block.
+        var meta2 = new IndexFileMeta(42, 100, 42, new FileProperties(1));
+        groupMeta.addIndexMeta(meta2);
+
+        // meta3 is appended to the second block (consecutive to meta2).
+        var meta3 = new IndexFileMeta(100, 150, 84, new FileProperties(2));
+        groupMeta.addIndexMeta(meta3);
+
+        assertThat(groupMeta.indexMetaByFileOrdinal(0), is(meta1));
+        assertThat(groupMeta.indexMetaByFileOrdinal(1), is(meta2));
+        assertThat(groupMeta.indexMetaByFileOrdinal(2), is(meta3));
+
+        // Ordinal after the last returns null.
+        assertThat(groupMeta.indexMetaByFileOrdinal(3), is(nullValue()));

Review Comment:
   could we check that ordinal before first also returns null, as in previous 
test? 



##########
modules/raft/src/test/java/org/apache/ignite/internal/raft/storage/segstore/GroupIndexMetaTest.java:
##########
@@ -317,17 +317,98 @@ void testOnIndexCompactedWithMultipleBlocks() {
         var compactedMeta1 = new IndexFileMeta(1, 100, 0, new 
FileProperties(0, 1));
         groupMeta.onIndexCompacted(new FileProperties(0), compactedMeta1);
 
-        assertThat(groupMeta.indexMeta(1), is(compactedMeta1));
-        assertThat(groupMeta.indexMeta(42), is(meta2));
-        assertThat(groupMeta.indexMeta(100), is(meta3));
+        assertThat(groupMeta.indexMetaByLogIndex(1), is(compactedMeta1));
+        assertThat(groupMeta.indexMetaByLogIndex(42), is(meta2));
+        assertThat(groupMeta.indexMetaByLogIndex(100), is(meta3));
 
         // Compact meta3 from the newer block.
         var compactedMeta3 = new IndexFileMeta(100, 200, 84, new 
FileProperties(2, 1));
         groupMeta.onIndexCompacted(new FileProperties(2), compactedMeta3);
 
-        assertThat(groupMeta.indexMeta(1), is(compactedMeta1));
-        assertThat(groupMeta.indexMeta(42), is(meta2));
-        assertThat(groupMeta.indexMeta(100), is(compactedMeta3));
+        assertThat(groupMeta.indexMetaByLogIndex(1), is(compactedMeta1));
+        assertThat(groupMeta.indexMetaByLogIndex(42), is(meta2));
+        assertThat(groupMeta.indexMetaByLogIndex(100), is(compactedMeta3));
+    }
+
+    @Test
+    void testIndexMetaByFileOrdinal() {
+        var meta1 = new IndexFileMeta(1, 50, 0, new FileProperties(1));
+        var meta2 = new IndexFileMeta(50, 100, 42, new FileProperties(2));
+        var meta3 = new IndexFileMeta(100, 150, 84, new FileProperties(3));
+
+        var groupMeta = new GroupIndexMeta(meta1);
+        groupMeta.addIndexMeta(meta2);
+        groupMeta.addIndexMeta(meta3);
+
+        assertThat(groupMeta.indexMetaByFileOrdinal(1), is(meta1));
+        assertThat(groupMeta.indexMetaByFileOrdinal(2), is(meta2));
+        assertThat(groupMeta.indexMetaByFileOrdinal(3), is(meta3));
+
+        // Ordinals before the first and after the last return null.
+        assertThat(groupMeta.indexMetaByFileOrdinal(0), is(nullValue()));
+        assertThat(groupMeta.indexMetaByFileOrdinal(4), is(nullValue()));
+    }
+
+    @Test
+    void testIndexMetaByFileOrdinalWithMultipleBlocks() {
+        // meta1 is in block 0.
+        var meta1 = new IndexFileMeta(1, 100, 0, new FileProperties(0));
+        var groupMeta = new GroupIndexMeta(meta1);
+
+        // meta2 overlaps meta1, creating a second deque block.
+        var meta2 = new IndexFileMeta(42, 100, 42, new FileProperties(1));
+        groupMeta.addIndexMeta(meta2);
+
+        // meta3 is appended to the second block (consecutive to meta2).
+        var meta3 = new IndexFileMeta(100, 150, 84, new FileProperties(2));
+        groupMeta.addIndexMeta(meta3);
+
+        assertThat(groupMeta.indexMetaByFileOrdinal(0), is(meta1));
+        assertThat(groupMeta.indexMetaByFileOrdinal(1), is(meta2));
+        assertThat(groupMeta.indexMetaByFileOrdinal(2), is(meta3));
+
+        // Ordinal after the last returns null.
+        assertThat(groupMeta.indexMetaByFileOrdinal(3), is(nullValue()));
+    }
+
+    @Test
+    void testIndexMetaByFileOrdinalAfterTruncatePrefix() {

Review Comment:
   Should we test case for files with overlapping metas, with multiple blocks? 



##########
modules/raft/src/test/java/org/apache/ignite/internal/raft/storage/segstore/RaftLogGarbageCollectorTest.java:
##########
@@ -155,35 +148,36 @@ void testCompactSegmentFileWithAllEntriesTruncated() 
throws Exception {
     }
 
     @Test
-    void testCompactSegmentFileWithSomeEntriesTruncated() throws Exception {
+    void testRunCompactionWithSomeEntriesTruncated() throws Exception {

Review Comment:
   ```suggestion
       void testRunCompactionWithFileNotTruncatedCompletely() throws Exception {
   ```
   Same, new name is confusing
   
    File is not compacted completely just because there is a second group in it 
- why also change size of batches? Let's reduce differences between test cases 
to avoid confusion
   



##########
modules/raft/src/test/java/org/apache/ignite/internal/raft/storage/segstore/RaftLogGarbageCollectorTest.java:
##########
@@ -119,16 +111,17 @@ void tearDown() throws Exception {
     }
 
     @Test
-    void testCompactSegmentFileWithAllEntriesTruncated() throws Exception {
+    void testRunCompactionWithAllEntriesTruncated() throws Exception {

Review Comment:
   ```suggestion
       void testRunCompactionWithFullyTruncatedFile() throws Exception {
   ```
   (or something else)
   
   I was confused by the test name, didn't understand why we truncate only part 
of the entries if we need to truncate all of them



##########
modules/raft/src/main/java/org/apache/ignite/internal/raft/storage/segstore/IndexFileMetaArray.java:
##########
@@ -208,6 +203,25 @@ IndexFileMetaArray onIndexCompacted(FileProperties 
oldProperties, IndexFileMeta
         return new IndexFileMetaArray(newArray, size);
     }
 
+    @Nullable
+    IndexFileMeta findByFileOrdinal(int fileOrdinal) {
+        int arrayIndex = arrayIndexByFileOrdinal(fileOrdinal);
+
+        return arrayIndex == -1 ? null : array[arrayIndex];
+    }
+
+    private int arrayIndexByFileOrdinal(int fileOrdinal) {
+        int smallestOrdinal = array[0].indexFileProperties().ordinal();
+
+        if (fileOrdinal < smallestOrdinal) {
+            return -1;
+        }
+
+        int arrayIndex = fileOrdinal - smallestOrdinal;
+
+        return arrayIndex >= size ? -1 : arrayIndex;

Review Comment:
   let's replace -1 with a constant



##########
modules/raft/src/main/java/org/apache/ignite/internal/raft/storage/segstore/RaftLogGarbageCollector.java:
##########
@@ -142,23 +137,61 @@ void cleanupLeftoverFiles() throws IOException {
         }
     }
 
-    // TODO: Optimize compaction of completely truncated files, see 
https://issues.apache.org/jira/browse/IGNITE-27964.
     @VisibleForTesting
-    void compactSegmentFile(SegmentFile segmentFile) throws IOException {
+    void runCompaction(SegmentFile segmentFile) throws IOException {
         LOG.info("Compacting segment file [path = {}].", segmentFile.path());
 
-        // Cache for avoiding excessive min/max log index computations.
-        var logStorageInfos = new Long2ObjectOpenHashMap<GroupInfo>();
+        Long2ObjectMap<GroupDescriptor> segmentFileDescription
+                = 
indexFileManager.describeSegmentFile(segmentFile.fileProperties().ordinal());
+
+        boolean canRemoveSegmentFile = segmentFileDescription.isEmpty();
+
+        Path indexFilePath = 
indexFileManager.indexFilePath(segmentFile.fileProperties());
+
+        long logSizeDelta;
+
+        if (canRemoveSegmentFile) {
+            logSizeDelta = Files.size(segmentFile.path()) + 
Files.size(indexFilePath);
+        } else {
+            logSizeDelta = compactSegmentFile(segmentFile, indexFilePath, 
segmentFileDescription);
+        }
 
+        // Remove the previous generation of the segment file and its index. 
This is safe to do, because we rely on the file system
+        // guarantees that other threads reading from the segment file will 
still be able to do that even if the file is deleted.
+        Files.delete(segmentFile.path());
+        Files.delete(indexFilePath);
+
+        long newLogSize = logSize.addAndGet(-logSizeDelta);
+
+        if (LOG.isInfoEnabled()) {
+            if (canRemoveSegmentFile) {
+                LOG.info(
+                        "Segment file removed (all entries are truncated) 
[path = {}, log size freed = {}, new log size = {}].",

Review Comment:
   ```suggestion
                           "Segment file removed (all entries are truncated) 
[path = {}, log size freed = {} bytes, new log size = {} bytes].",
   ```



##########
modules/raft/src/main/java/org/apache/ignite/internal/raft/storage/segstore/RaftLogGarbageCollector.java:
##########
@@ -142,23 +137,61 @@ void cleanupLeftoverFiles() throws IOException {
         }
     }
 
-    // TODO: Optimize compaction of completely truncated files, see 
https://issues.apache.org/jira/browse/IGNITE-27964.
     @VisibleForTesting
-    void compactSegmentFile(SegmentFile segmentFile) throws IOException {
+    void runCompaction(SegmentFile segmentFile) throws IOException {
         LOG.info("Compacting segment file [path = {}].", segmentFile.path());
 
-        // Cache for avoiding excessive min/max log index computations.
-        var logStorageInfos = new Long2ObjectOpenHashMap<GroupInfo>();
+        Long2ObjectMap<GroupDescriptor> segmentFileDescription
+                = 
indexFileManager.describeSegmentFile(segmentFile.fileProperties().ordinal());
+
+        boolean canRemoveSegmentFile = segmentFileDescription.isEmpty();
+
+        Path indexFilePath = 
indexFileManager.indexFilePath(segmentFile.fileProperties());
+
+        long logSizeDelta;
+
+        if (canRemoveSegmentFile) {
+            logSizeDelta = Files.size(segmentFile.path()) + 
Files.size(indexFilePath);
+        } else {
+            logSizeDelta = compactSegmentFile(segmentFile, indexFilePath, 
segmentFileDescription);
+        }
 
+        // Remove the previous generation of the segment file and its index. 
This is safe to do, because we rely on the file system
+        // guarantees that other threads reading from the segment file will 
still be able to do that even if the file is deleted.
+        Files.delete(segmentFile.path());
+        Files.delete(indexFilePath);
+
+        long newLogSize = logSize.addAndGet(-logSizeDelta);

Review Comment:
   ```suggestion
           long newLogSize = logSizeBytes.addAndGet(-logSizeDelta);
   ```
   or something else, now it's not obvious that size in bytes and not in log 
records or something



##########
modules/raft/src/test/java/org/apache/ignite/internal/raft/storage/segstore/RaftLogGarbageCollectorTest.java:
##########
@@ -199,66 +193,60 @@ void testCompactSegmentFileWithSomeEntriesTruncated() 
throws Exception {
     }
 
     @Test
-    void testCompactSegmentFileWithTruncationRecords() throws Exception {
+    void testRunCompactionWithTruncationRecords() throws Exception {
         List<byte[]> batches = createRandomData(FILE_SIZE / 4, 5);
         for (int i = 0; i < batches.size(); i++) {
             appendBytes(GROUP_ID_1, batches.get(i), i);
         }
 
-        fileManager.truncatePrefix(GROUP_ID_1, 2);
-        fileManager.truncateSuffix(GROUP_ID_1, 3);
-
-        await().until(this::indexFiles, hasSize(greaterThan(0)));
+        // Truncate both prefix and suffix.
+        fileManager.truncatePrefix(GROUP_ID_1, batches.size() / 2);
+        fileManager.truncateSuffix(GROUP_ID_1, batches.size() / 2);
 
         List<Path> segmentFiles = segmentFiles();
-        assertThat(segmentFiles, hasSize(greaterThan(1)));
 
-        // This is equivalent to prefix truncation up to the latest index.
-        when(groupInfoProvider.groupInfo(GROUP_ID_1)).thenReturn(new 
GroupInfo(batches.size() - 1, batches.size() - 1));
+        triggerAndAwaitCheckpoint(batches.size() / 2);
 
-        Path firstSegmentFile = segmentFiles.get(0);
+        for (Path segmentFilePath : segmentFiles) {
+            SegmentFile segmentFile = 
SegmentFile.openExisting(segmentFilePath, false);
 
-        SegmentFile segmentFile = SegmentFile.openExisting(firstSegmentFile, 
false);
-        try {
-            garbageCollector.compactSegmentFile(segmentFile);
-        } finally {
-            segmentFile.close();
-        }
+            try {
+                garbageCollector.runCompaction(segmentFile);
+            } finally {
+                segmentFile.close();
+            }
 
-        assertThat(Files.exists(firstSegmentFile), is(false));
+            assertThat(Files.exists(segmentFilePath), is(false));

Review Comment:
   ```suggestion
               assertThat(segmentFilePath, not(exists()));
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to