diff --git a/community/io/src/main/java/org/neo4j/io/pagecache/PageCache.java b/community/io/src/main/java/org/neo4j/io/pagecache/PageCache.java
index 450402372f10..faa88f46488f 100644
--- a/community/io/src/main/java/org/neo4j/io/pagecache/PageCache.java
+++ b/community/io/src/main/java/org/neo4j/io/pagecache/PageCache.java
@@ -24,6 +24,7 @@
import java.nio.file.NoSuchFileException;
import java.nio.file.OpenOption;
import java.nio.file.StandardOpenOption;
+import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
@@ -69,7 +70,7 @@ public interface PageCache extends AutoCloseable
* If no mapping exist for this file, then returned {@link Optional} will report {@link Optional#isPresent()}
* false.
*
- * NOTE! User is responsible for closing the returned paged file.
+ * NOTE: The calling code is responsible for closing the returned paged file, if any.
*
* @param file The file to try to get the mapped paged file for.
* @return {@link Optional} containing the {@link PagedFile} mapped by this {@link PageCache} for given file, or an
@@ -78,11 +79,26 @@ public interface PageCache extends AutoCloseable
*/
Optional getExistingMapping( File file ) throws IOException;
- /** Flush all dirty pages */
+ /**
+ * List a snapshot of the current file mappings.
+ *
+ * The mappings can change as soon as this method returns. However, the returned {@link PagedFile}s will remain
+ * valid even if they are closed elsewhere.
+ *
+ * NOTE: The calling code is responsible for closing all the returned paged files.
+ *
+ * @throws IOException if page cache has been closed or page eviction problems occur.
+ */
+ List listExistingMappings() throws IOException;
+
+ /**
+ * Flush all dirty pages.
+ */
void flushAndForce() throws IOException;
/**
* Flush all dirty pages, but limit the rate of IO as advised by the given IOPSLimiter.
+ *
* @param limiter The {@link IOLimiter} that determines if pauses or sleeps should be injected into the flushing
* process to keep the IO rate down.
*/
@@ -92,21 +108,22 @@ public interface PageCache extends AutoCloseable
* Close the page cache to prevent any future mapping of files.
* This also releases any internal resources, including the {@link PageSwapperFactory} through its
* {@link PageSwapperFactory#close() close} method.
+ *
* @throws IllegalStateException if not all files have been unmapped, with {@link PagedFile#close()}, prior to
* closing the page cache. In this case, the page cache WILL NOT be considered to be successfully closed.
* @throws RuntimeException if the {@link PageSwapperFactory#close()} method throws. In this case the page cache
* WILL BE considered to have been closed successfully.
- **/
+ */
void close() throws IllegalStateException;
/**
* The size in bytes of the pages managed by this cache.
- **/
+ */
int pageSize();
/**
* The max number of cached pages.
- **/
+ */
int maxCachedPages();
/**
diff --git a/community/io/src/main/java/org/neo4j/io/pagecache/impl/muninn/MuninnPageCache.java b/community/io/src/main/java/org/neo4j/io/pagecache/impl/muninn/MuninnPageCache.java
index 8c5f6dbec0ab..1e09c0a70b52 100644
--- a/community/io/src/main/java/org/neo4j/io/pagecache/impl/muninn/MuninnPageCache.java
+++ b/community/io/src/main/java/org/neo4j/io/pagecache/impl/muninn/MuninnPageCache.java
@@ -24,6 +24,7 @@
import java.nio.file.CopyOption;
import java.nio.file.OpenOption;
import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
@@ -476,6 +477,25 @@ private void assertNotMapped( File file, FileIsMappedException.Operation operati
}
}
+ @Override
+ public synchronized List listExistingMappings() throws IOException
+ {
+ assertHealthy();
+ ensureThreadsInitialised();
+
+ List list = new ArrayList<>();
+ FileMapping current = mappedFiles;
+
+ while ( current != null )
+ {
+ MuninnPagedFile pagedFile = current.pagedFile;
+ pagedFile.incrementRefCount();
+ list.add( pagedFile );
+ current = current.next;
+ }
+ return list;
+ }
+
/**
* Note: Must be called while synchronizing on the MuninnPageCache instance.
*/
diff --git a/community/io/src/test/java/org/neo4j/adversaries/pagecache/AdversarialPageCache.java b/community/io/src/test/java/org/neo4j/adversaries/pagecache/AdversarialPageCache.java
index 96c2b9c32477..fb4c13c8c8f7 100644
--- a/community/io/src/test/java/org/neo4j/adversaries/pagecache/AdversarialPageCache.java
+++ b/community/io/src/test/java/org/neo4j/adversaries/pagecache/AdversarialPageCache.java
@@ -27,6 +27,7 @@
import java.nio.file.NoSuchFileException;
import java.nio.file.OpenOption;
import java.nio.file.StandardOpenOption;
+import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;
@@ -83,6 +84,18 @@ public Optional getExistingMapping( File file ) throws IOException
return optional;
}
+ @Override
+ public List listExistingMappings() throws IOException
+ {
+ adversary.injectFailure( IOException.class, SecurityException.class );
+ List list = delegate.listExistingMappings();
+ for ( int i = 0; i < list.size(); i++ )
+ {
+ list.set( i, new AdversarialPagedFile( list.get( i ), adversary ) );
+ }
+ return list;
+ }
+
@Override
public void flushAndForce() throws IOException
{
diff --git a/community/io/src/test/java/org/neo4j/io/pagecache/DelegatingPageCache.java b/community/io/src/test/java/org/neo4j/io/pagecache/DelegatingPageCache.java
index 77d76e33102a..d9f0c99c1537 100644
--- a/community/io/src/test/java/org/neo4j/io/pagecache/DelegatingPageCache.java
+++ b/community/io/src/test/java/org/neo4j/io/pagecache/DelegatingPageCache.java
@@ -22,6 +22,7 @@
import java.io.File;
import java.io.IOException;
import java.nio.file.OpenOption;
+import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
@@ -45,6 +46,13 @@ public Optional getExistingMapping( File file ) throws IOException
return delegate.getExistingMapping( file );
}
+ @Override
+ public List listExistingMappings() throws IOException
+ {
+ return delegate.listExistingMappings();
+ }
+
+ @Override
public int pageSize()
{
return delegate.pageSize();
diff --git a/community/io/src/test/java/org/neo4j/io/pagecache/PageCacheTest.java b/community/io/src/test/java/org/neo4j/io/pagecache/PageCacheTest.java
index 625c53c67b52..44633c20466b 100644
--- a/community/io/src/test/java/org/neo4j/io/pagecache/PageCacheTest.java
+++ b/community/io/src/test/java/org/neo4j/io/pagecache/PageCacheTest.java
@@ -1151,29 +1151,6 @@ public void writeToPreviouslyBoundCursorAfterNextReturnsFalseMustThrow() throws
verifyOnWriteCursor( this::checkPreviouslyBoundWriteCursorAfterFailedNext );
}
- @Test
- public void tryMappedPagedFileShouldReportMappedFilePresent() throws Exception
- {
- PageCache cache = createStandardPageCache();
- final File file = file( "a" );
- try ( PagedFile pf = cache.map( file, filePageSize ) )
- {
- final Optional optional = cache.getExistingMapping( file );
- assertTrue( optional.isPresent() );
- final PagedFile actual = optional.get();
- assertThat( actual, sameInstance( pf ) );
- actual.close();
- }
- }
-
- @Test
- public void tryMappedPagedFileShouldReportNonMappedFileNotPresent() throws Exception
- {
- PageCache cache = createStandardPageCache();
- final Optional dont_exist = cache.getExistingMapping( new File( "dont_exist" ) );
- assertFalse( dont_exist.isPresent() );
- }
-
private void verifyOnReadCursor(
ThrowingConsumer testTemplate ) throws IOException
{
@@ -1310,6 +1287,62 @@ private void checkPreviouslyBoundWriteCursorAfterFailedNext( PageCursorAction ac
}
}
+ @Test
+ public void tryMappedPagedFileShouldReportMappedFilePresent() throws Exception
+ {
+ configureStandardPageCache();
+ final File file = file( "a" );
+ try ( PagedFile pf = pageCache.map( file, filePageSize ) )
+ {
+ final Optional optional = pageCache.getExistingMapping( file );
+ assertTrue( optional.isPresent() );
+ final PagedFile actual = optional.get();
+ assertThat( actual, sameInstance( pf ) );
+ actual.close();
+ }
+ }
+
+ @Test
+ public void tryMappedPagedFileShouldReportNonMappedFileNotPresent() throws Exception
+ {
+ configureStandardPageCache();
+ final Optional dontExist = pageCache.getExistingMapping( new File( "dont_exist" ) );
+ assertFalse( dontExist.isPresent() );
+ }
+
+ @Test
+ public void mustListExistingMappings() throws Exception
+ {
+ configureStandardPageCache();
+ File f1 = existingFile( "1" );
+ File f2 = existingFile( "2" );
+ File f3 = existingFile( "3" ); // Not mapped at the time of calling listExistingMappings.
+ existingFile( "4" ); // Never mapped.
+ try ( PagedFile pf1 = pageCache.map( f1, filePageSize );
+ PagedFile pf2 = pageCache.map( f2, filePageSize ) )
+ {
+ pageCache.map( f3, filePageSize ).close();
+ List existingMappings = pageCache.listExistingMappings();
+ assertThat( existingMappings.size(), is( 2 ) );
+ assertThat( existingMappings, containsInAnyOrder( pf1, pf2 ) );
+ for ( PagedFile existingMapping : existingMappings )
+ {
+ existingMapping.close();
+ }
+ }
+ }
+
+ @Test
+ public void listExistingMappingsMustThrowOnClosedPageCache() throws Exception
+ {
+ configureStandardPageCache();
+ T pc = pageCache;
+ pageCache = null;
+ pc.close();
+ expectedException.expect( IllegalStateException.class );
+ pc.listExistingMappings();
+ }
+
@Test( timeout = SHORT_TIMEOUT_MILLIS )
public void lastPageMustBeAccessibleWithNoGrowSpecified() throws IOException
{
diff --git a/enterprise/com/src/main/java/org/neo4j/com/storecopy/ExternallyManagedPageCache.java b/enterprise/com/src/main/java/org/neo4j/com/storecopy/ExternallyManagedPageCache.java
index 4dc924488cf2..8971d78dcd9a 100644
--- a/enterprise/com/src/main/java/org/neo4j/com/storecopy/ExternallyManagedPageCache.java
+++ b/enterprise/com/src/main/java/org/neo4j/com/storecopy/ExternallyManagedPageCache.java
@@ -22,6 +22,7 @@
import java.io.File;
import java.io.IOException;
import java.nio.file.OpenOption;
+import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
@@ -76,6 +77,12 @@ public Optional getExistingMapping( File file ) throws IOException
return delegate.getExistingMapping( file );
}
+ @Override
+ public List listExistingMappings() throws IOException
+ {
+ return delegate.listExistingMappings();
+ }
+
@Override
public void flushAndForce() throws IOException
{