Copilot commented on code in PR #7023:
URL: https://github.com/apache/ignite-3/pull/7023#discussion_r2545266157


##########
modules/storage-page-memory/src/test/java/org/apache/ignite/internal/storage/pagememory/PersistentPageMemoryNoLoadTest.java:
##########
@@ -450,6 +453,111 @@ void testPageReplacement(@WorkDirectory Path workDir) 
throws Exception {
         }
     }
 
+    /**
+     * Tests that {@link PersistentPageMemory#acquirePage(int, long)} works 
correctly when multiple threads try to acquire the same page
+     * using different {@code padeId} values, assuming that one of the threads 
simply has in invalid identifier from some obsolete source.
+     */
+    @Test
+    void testAcquireRace(@WorkDirectory Path workDir) throws Exception {
+        int pages = 100;
+
+        // Step 1. Start the region, allocate a number of pages, checkpoint 
them to the storage, stop the region.
+        FilePageStoreManager filePageStoreManager = 
createFilePageStoreManager(workDir);
+        PartitionMetaManager partitionMetaManager = new 
PartitionMetaManager(ioRegistry, PAGE_SIZE, StoragePartitionMeta.FACTORY);
+        Collection<DataRegion<PersistentPageMemory>> dataRegions = new 
ArrayList<>();
+
+        CheckpointManager checkpointManager = createCheckpointManager(
+                CheckpointConfiguration.builder().build(),
+                filePageStoreManager,
+                partitionMetaManager,
+                dataRegions
+        );
+
+        int systemPageSize = PAGE_SIZE + PAGE_OVERHEAD;
+
+        dataRegionSize = 1024 * systemPageSize;
+
+        PersistentPageMemory pageMemory = createPageMemory(
+                new long[]{dataRegionSize},
+                128 * systemPageSize,
+                filePageStoreManager,
+                checkpointManager,
+                shouldNotHappenFlushDirtyPageForReplacement()
+        );
+
+        dataRegions.add(() -> pageMemory);
+
+        filePageStoreManager.start();
+        checkpointManager.start();
+        pageMemory.start();
+
+        try {
+            initGroupFilePageStores(filePageStoreManager, 
partitionMetaManager, checkpointManager, pageMemory);
+
+            checkpointManager.checkpointTimeoutLock().checkpointReadLock();
+
+            try {
+                for (int i = 0; i < pages; i++) {
+                    createDirtyPage(pageMemory);
+                }
+            } finally {
+                
checkpointManager.checkpointTimeoutLock().checkpointReadUnlock();
+            }
+
+            CompletableFuture<Void> checkpointFuture = checkpointManager
+                    .forceCheckpoint("before-stopping-in-test")
+                    .futureFor(FINISHED);
+
+            assertThat(checkpointFuture, willCompleteSuccessfully());
+        } finally {
+            closeAll(
+                    () -> pageMemory.stop(true),
+                    checkpointManager::stop,
+                    filePageStoreManager::stop
+            );
+        }
+
+        // Step 2. Start a new region over the same persistence. The goal here 
is to test "acquirePage" that will actually read data from
+        // the storage instead of hitting the cached page.
+        PersistentPageMemory pageMemory2 = createPageMemory(
+                new long[]{dataRegionSize},
+                128 * systemPageSize,
+                filePageStoreManager,
+                checkpointManager,
+                shouldNotHappenFlushDirtyPageForReplacement()
+        );
+
+        filePageStoreManager.start();
+        checkpointManager.start();
+        pageMemory2.start();
+
+        try {
+            initGroupFilePageStores(filePageStoreManager, 
partitionMetaManager, checkpointManager, pageMemory2);
+
+            for (int i = 0; i < pages; i++) {
+                // We skip meta page, that's why we add 1 to i.
+                long fakePageId = PageIdUtils.pageId(PARTITION_ID, 
PageIdAllocator.FLAG_AUX, i + 1);
+                long realPageId = PageIdUtils.pageId(PARTITION_ID, 
PageIdAllocator.FLAG_DATA, i + 1);
+
+                // Step 3. Run the race for all pages in the partition.
+                IgniteTestUtils.runRace(
+                        () -> pageMemory2.acquirePage(GRP_ID, fakePageId),
+                        () -> {
+                            long page = pageMemory2.acquirePage(GRP_ID, 
realPageId);
+
+                            assertNotEquals(0L, pageMemory2.readLock(GRP_ID, 
realPageId, page));

Review Comment:
   Resource leak: The read lock acquired at line 548 is never released. After 
verifying the lock is non-zero, you should call `pageMemory2.readUnlock(GRP_ID, 
realPageId, page)` to release the lock. Consider wrapping in a try-finally 
block to ensure proper cleanup.
   ```suggestion
                               long lock = pageMemory2.readLock(GRP_ID, 
realPageId, page);
                               try {
                                   assertNotEquals(0L, lock);
                               } finally {
                                   if (lock != 0L) {
                                       pageMemory2.readUnlock(GRP_ID, 
realPageId, page);
                                   }
                               }
   ```



##########
modules/storage-page-memory/src/test/java/org/apache/ignite/internal/storage/pagememory/PersistentPageMemoryNoLoadTest.java:
##########
@@ -450,6 +453,111 @@ void testPageReplacement(@WorkDirectory Path workDir) 
throws Exception {
         }
     }
 
+    /**
+     * Tests that {@link PersistentPageMemory#acquirePage(int, long)} works 
correctly when multiple threads try to acquire the same page
+     * using different {@code padeId} values, assuming that one of the threads 
simply has in invalid identifier from some obsolete source.

Review Comment:
   Spelling errors: "padeId" should be "pageId" and "in invalid" should be "an 
invalid".
   ```suggestion
        * using different {@code pageId} values, assuming that one of the 
threads simply has an invalid identifier from some obsolete source.
   ```



##########
modules/page-memory/src/main/java/org/apache/ignite/internal/pagememory/persistence/PersistentPageMemory.java:
##########
@@ -732,28 +725,21 @@ grpId, hexLong(pageId)
                         partGen
                 );
 
-                long pageAddr = absPtr + PAGE_OVERHEAD;
-
-                if (!restore) {
-                    delayedPageReplacementTracker.waitUnlock(fullId);
+                delayedPageReplacementTracker.waitUnlock(fullId);
 
-                    readPageFromStore = true;
-                } else {
-                    zeroMemory(absPtr + PAGE_OVERHEAD, pageSize());
+                readPageFromStore = true;
 
-                    // Must init page ID in order to ensure RWLock tag 
consistency.
-                    setPageId(pageAddr, pageId);
-                }
+                // Mare page header as invalid. We have not yet read the real 
value of "pageId" from the page, thus the state of "rwLock"

Review Comment:
   Spelling error: "Mare" should be "Mark".
   ```suggestion
                   // Mark page header as invalid. We have not yet read the 
real value of "pageId" from the page, thus the state of "rwLock"
   ```



##########
modules/storage-page-memory/src/test/java/org/apache/ignite/internal/storage/pagememory/PersistentPageMemoryNoLoadTest.java:
##########
@@ -450,6 +453,111 @@ void testPageReplacement(@WorkDirectory Path workDir) 
throws Exception {
         }
     }
 
+    /**
+     * Tests that {@link PersistentPageMemory#acquirePage(int, long)} works 
correctly when multiple threads try to acquire the same page
+     * using different {@code padeId} values, assuming that one of the threads 
simply has in invalid identifier from some obsolete source.
+     */
+    @Test
+    void testAcquireRace(@WorkDirectory Path workDir) throws Exception {
+        int pages = 100;
+
+        // Step 1. Start the region, allocate a number of pages, checkpoint 
them to the storage, stop the region.
+        FilePageStoreManager filePageStoreManager = 
createFilePageStoreManager(workDir);
+        PartitionMetaManager partitionMetaManager = new 
PartitionMetaManager(ioRegistry, PAGE_SIZE, StoragePartitionMeta.FACTORY);
+        Collection<DataRegion<PersistentPageMemory>> dataRegions = new 
ArrayList<>();
+
+        CheckpointManager checkpointManager = createCheckpointManager(
+                CheckpointConfiguration.builder().build(),
+                filePageStoreManager,
+                partitionMetaManager,
+                dataRegions
+        );
+
+        int systemPageSize = PAGE_SIZE + PAGE_OVERHEAD;
+
+        dataRegionSize = 1024 * systemPageSize;
+
+        PersistentPageMemory pageMemory = createPageMemory(
+                new long[]{dataRegionSize},
+                128 * systemPageSize,
+                filePageStoreManager,
+                checkpointManager,
+                shouldNotHappenFlushDirtyPageForReplacement()
+        );
+
+        dataRegions.add(() -> pageMemory);
+
+        filePageStoreManager.start();
+        checkpointManager.start();
+        pageMemory.start();
+
+        try {
+            initGroupFilePageStores(filePageStoreManager, 
partitionMetaManager, checkpointManager, pageMemory);
+
+            checkpointManager.checkpointTimeoutLock().checkpointReadLock();
+
+            try {
+                for (int i = 0; i < pages; i++) {
+                    createDirtyPage(pageMemory);
+                }
+            } finally {
+                
checkpointManager.checkpointTimeoutLock().checkpointReadUnlock();
+            }
+
+            CompletableFuture<Void> checkpointFuture = checkpointManager
+                    .forceCheckpoint("before-stopping-in-test")
+                    .futureFor(FINISHED);
+
+            assertThat(checkpointFuture, willCompleteSuccessfully());
+        } finally {
+            closeAll(
+                    () -> pageMemory.stop(true),
+                    checkpointManager::stop,
+                    filePageStoreManager::stop
+            );
+        }
+
+        // Step 2. Start a new region over the same persistence. The goal here 
is to test "acquirePage" that will actually read data from
+        // the storage instead of hitting the cached page.
+        PersistentPageMemory pageMemory2 = createPageMemory(
+                new long[]{dataRegionSize},
+                128 * systemPageSize,
+                filePageStoreManager,
+                checkpointManager,
+                shouldNotHappenFlushDirtyPageForReplacement()
+        );
+
+        filePageStoreManager.start();
+        checkpointManager.start();
+        pageMemory2.start();
+
+        try {
+            initGroupFilePageStores(filePageStoreManager, 
partitionMetaManager, checkpointManager, pageMemory2);
+
+            for (int i = 0; i < pages; i++) {
+                // We skip meta page, that's why we add 1 to i.
+                long fakePageId = PageIdUtils.pageId(PARTITION_ID, 
PageIdAllocator.FLAG_AUX, i + 1);
+                long realPageId = PageIdUtils.pageId(PARTITION_ID, 
PageIdAllocator.FLAG_DATA, i + 1);
+
+                // Step 3. Run the race for all pages in the partition.
+                IgniteTestUtils.runRace(
+                        () -> pageMemory2.acquirePage(GRP_ID, fakePageId),
+                        () -> {
+                            long page = pageMemory2.acquirePage(GRP_ID, 
realPageId);
+
+                            assertNotEquals(0L, pageMemory2.readLock(GRP_ID, 
realPageId, page));

Review Comment:
   Resource leak: The pages acquired via `acquirePage` at lines 544 and 546 are 
never released. After using the acquired pages, you should call 
`pageMemory2.releasePage(GRP_ID, pageId, page)` to properly release them. 
Consider wrapping the acquisition in a try-finally block to ensure pages are 
released even if assertions fail.
   ```suggestion
                           () -> {
                               long page = pageMemory2.acquirePage(GRP_ID, 
fakePageId);
                               try {
                                   // No operation, just acquire and release.
                               } finally {
                                   pageMemory2.releasePage(GRP_ID, fakePageId, 
page);
                               }
                           },
                           () -> {
                               long page = pageMemory2.acquirePage(GRP_ID, 
realPageId);
                               try {
                                   assertNotEquals(0L, 
pageMemory2.readLock(GRP_ID, realPageId, page));
                               } finally {
                                   pageMemory2.releasePage(GRP_ID, realPageId, 
page);
                               }
   ```



-- 
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