diff --git a/community/jmx/src/main/java/org/neo4j/jmx/StoreFile.java b/community/jmx/src/main/java/org/neo4j/jmx/StoreFile.java index bd96e09db8629..ec46776102834 100644 --- a/community/jmx/src/main/java/org/neo4j/jmx/StoreFile.java +++ b/community/jmx/src/main/java/org/neo4j/jmx/StoreFile.java @@ -20,7 +20,9 @@ package org.neo4j.jmx; @ManagementInterface( name = StoreFile.NAME ) -@Description( "Information about the sizes of the different parts of the Neo4j graph store" ) +@Description( "This bean is deprecated, use StoreSize bean instead; " + + "Information about the sizes of the different parts of the Neo4j graph store" ) +@Deprecated public interface StoreFile { String NAME = "Store file sizes"; @@ -37,8 +39,8 @@ public interface StoreFile @Description( "The amount of disk space used to store relationships, in bytes." ) long getRelationshipStoreSize(); - @Description( "The amount of disk space used to store properties " - + "(excluding string values and array values), in bytes." ) + @Description( "The amount of disk space used to store properties " + + "(excluding string values and array values), in bytes." ) long getPropertyStoreSize(); @Description( "The amount of disk space used to store string properties, in bytes." ) @@ -46,10 +48,4 @@ public interface StoreFile @Description( "The amount of disk space used to store array properties, in bytes." ) long getArrayStoreSize(); - - @Description( "The amount of disk space used by all logical logs, in bytes." ) - long getAllLogicalLogsSize(); - - @Description( "The amount of disk space used by all indices, in bytes" ) - long getIndexStoreSize(); } diff --git a/community/jmx/src/main/java/org/neo4j/jmx/StoreSize.java b/community/jmx/src/main/java/org/neo4j/jmx/StoreSize.java new file mode 100644 index 0000000000000..dec38e28ff6d9 --- /dev/null +++ b/community/jmx/src/main/java/org/neo4j/jmx/StoreSize.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2002-2017 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.jmx; + +@ManagementInterface( name = StoreSize.NAME ) +@Description( "Information about the disk space used by different parts of the Neo4j graph store" ) +public interface StoreSize +{ + String NAME = "Store sizes"; + + @Description( "Disk space used by the transaction logs, in bytes." ) + long getTransactionLogsSize(); + + @Description( "Disk space used to store nodes, in bytes." ) + long getNodeStoreSize(); + + @Description( "Disk space used to store relationships, in bytes." ) + long getRelationshipStoreSize(); + + @Description( "Disk space used to store properties (excluding string values and array values), in bytes." ) + long getPropertyStoreSize(); + + @Description( "Disk space used to store string properties, in bytes." ) + long getStringStoreSize(); + + @Description( "Disk space used to store array properties, in bytes." ) + long getArrayStoreSize(); + + @Description( "Disk space used to store labels, in bytes" ) + long getLabelStoreSize(); + + @Description( "Disk space used to store counters, in bytes" ) + long getCountStoreSize(); + + @Description( "Disk space used to store schemas (index and constrain declarations), in bytes" ) + long getSchemaStoreSize(); + + @Description( "Disk space used to store all indices, in bytes" ) + long getIndexStoreSize(); + + @Description( "Disk space used by whole store, in bytes." ) + long getTotalStoreSize(); +} diff --git a/community/jmx/src/main/java/org/neo4j/jmx/impl/StoreFileBean.java b/community/jmx/src/main/java/org/neo4j/jmx/impl/StoreFileBean.java index c685f0e445df2..13519f2ceb70e 100644 --- a/community/jmx/src/main/java/org/neo4j/jmx/impl/StoreFileBean.java +++ b/community/jmx/src/main/java/org/neo4j/jmx/impl/StoreFileBean.java @@ -28,14 +28,9 @@ import org.neo4j.io.fs.FileUtils; import org.neo4j.jmx.StoreFile; import org.neo4j.kernel.NeoStoreDataSource; -import org.neo4j.kernel.api.index.SchemaIndexProvider; -import org.neo4j.kernel.api.labelscan.LabelScanStore; -import org.neo4j.kernel.impl.api.LegacyIndexProviderLookup; import org.neo4j.kernel.impl.store.MetaDataStore; import org.neo4j.kernel.impl.transaction.log.LogFile; -import org.neo4j.kernel.impl.transaction.log.PhysicalLogFiles; import org.neo4j.kernel.impl.transaction.state.DataSourceManager; -import org.neo4j.kernel.spi.legacyindex.IndexImplementation; import static org.neo4j.kernel.impl.store.StoreFactory.NODE_STORE_NAME; import static org.neo4j.kernel.impl.store.StoreFactory.PROPERTY_ARRAYS_STORE_NAME; @@ -67,11 +62,7 @@ static class StoreFileImpl extends Neo4jMBean implements StoreFile private File storePath; private LogFile logFile; - private PhysicalLogFiles physicalLogFiles; private FileSystemAbstraction fs; - private LegacyIndexProviderLookup legacyIndexProviderLookup; - private SchemaIndexProvider schemaIndexProvider; - private LabelScanStore labelScanStore; StoreFileImpl( ManagementData management ) throws NotCompliantMBeanException { @@ -86,11 +77,6 @@ static class StoreFileImpl extends Neo4jMBean implements StoreFile public void registered( NeoStoreDataSource ds ) { logFile = resolveDependency( ds, LogFile.class ); - physicalLogFiles = resolveDependency( ds, PhysicalLogFiles.class ); - legacyIndexProviderLookup = resolveDependency( ds, LegacyIndexProviderLookup.class ); - schemaIndexProvider = resolveDependency( ds, SchemaIndexProvider.class ); - labelScanStore = resolveDependency( ds, LabelScanStore.class ); - storePath = resolvePath( ds ); } @@ -103,7 +89,6 @@ private T resolveDependency( NeoStoreDataSource ds, Class clazz ) public void unregistered( NeoStoreDataSource ds ) { logFile = null; - physicalLogFiles = null; storePath = null; } @@ -133,47 +118,12 @@ public long getLogicalLogSize() return logFile == null ? 0 : FileUtils.size( fs, logFile.currentLogFile() ); } - private long sizeOf( String name ) - { - return storePath == null ? 0 : FileUtils.size( fs, new File( storePath, name ) ); - } - @Override public long getArrayStoreSize() { return sizeOf( ARRAY_STORE ); } - @Override - public long getAllLogicalLogsSize() - { - TotalSizeVersionVisitor logVersionVisitor = new TotalSizeVersionVisitor( fs ); - - physicalLogFiles.accept( logVersionVisitor ); - - return logVersionVisitor.getTotalSize(); - } - - @Override - public long getIndexStoreSize() - { - long size = 0L; - - // Add legacy indices - for ( IndexImplementation index : legacyIndexProviderLookup.all() ) - { - size += FileUtils.size( fs, index.getIndexImplementationDirectory( storePath ) ); - } - - // Add schema index - size += FileUtils.size( fs, schemaIndexProvider.getSchemaIndexStoreDirectory( storePath ) ); - - // Add label index - size += FileUtils.size( fs, labelScanStore.getLabelScanStoreFile() ); - - return size; - } - @Override public long getNodeStoreSize() { @@ -198,26 +148,9 @@ public long getStringStoreSize() return sizeOf( STRING_STORE ); } - private static class TotalSizeVersionVisitor implements PhysicalLogFiles.LogVersionVisitor + private long sizeOf( String name ) { - private final FileSystemAbstraction fs; - private long totalSize; - - TotalSizeVersionVisitor( FileSystemAbstraction fs ) - { - this.fs = fs; - } - - long getTotalSize() - { - return totalSize; - } - - @Override - public void visit( File file, long logVersion ) - { - totalSize += FileUtils.size( fs, file ); - } + return storePath == null ? 0 : FileUtils.size( fs, new File( storePath, name ) ); } } } diff --git a/community/jmx/src/main/java/org/neo4j/jmx/impl/StoreSizeBean.java b/community/jmx/src/main/java/org/neo4j/jmx/impl/StoreSizeBean.java new file mode 100644 index 0000000000000..bbb41394392bc --- /dev/null +++ b/community/jmx/src/main/java/org/neo4j/jmx/impl/StoreSizeBean.java @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2002-2017 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.jmx.impl; + +import java.io.File; +import java.io.IOException; +import javax.management.NotCompliantMBeanException; + +import org.neo4j.helpers.Service; +import org.neo4j.io.fs.FileSystemAbstraction; +import org.neo4j.io.fs.FileUtils; +import org.neo4j.jmx.StoreSize; +import org.neo4j.kernel.NeoStoreDataSource; +import org.neo4j.kernel.api.index.SchemaIndexProvider; +import org.neo4j.kernel.api.labelscan.LabelScanStore; +import org.neo4j.kernel.impl.api.LegacyIndexProviderLookup; +import org.neo4j.kernel.impl.store.StoreFile; +import org.neo4j.kernel.impl.storemigration.StoreFileType; +import org.neo4j.kernel.impl.transaction.log.PhysicalLogFiles; +import org.neo4j.kernel.impl.transaction.state.DataSourceManager; +import org.neo4j.kernel.spi.legacyindex.IndexImplementation; + +import static org.neo4j.kernel.impl.store.StoreFile.COUNTS_STORE_LEFT; +import static org.neo4j.kernel.impl.store.StoreFile.COUNTS_STORE_RIGHT; +import static org.neo4j.kernel.impl.store.StoreFile.LABEL_TOKEN_NAMES_STORE; +import static org.neo4j.kernel.impl.store.StoreFile.LABEL_TOKEN_STORE; +import static org.neo4j.kernel.impl.store.StoreFile.NODE_LABEL_STORE; +import static org.neo4j.kernel.impl.store.StoreFile.NODE_STORE; +import static org.neo4j.kernel.impl.store.StoreFile.PROPERTY_ARRAY_STORE; +import static org.neo4j.kernel.impl.store.StoreFile.PROPERTY_KEY_TOKEN_NAMES_STORE; +import static org.neo4j.kernel.impl.store.StoreFile.PROPERTY_KEY_TOKEN_STORE; +import static org.neo4j.kernel.impl.store.StoreFile.PROPERTY_STORE; +import static org.neo4j.kernel.impl.store.StoreFile.PROPERTY_STRING_STORE; +import static org.neo4j.kernel.impl.store.StoreFile.RELATIONSHIP_GROUP_STORE; +import static org.neo4j.kernel.impl.store.StoreFile.RELATIONSHIP_STORE; +import static org.neo4j.kernel.impl.store.StoreFile.RELATIONSHIP_TYPE_TOKEN_NAMES_STORE; +import static org.neo4j.kernel.impl.store.StoreFile.RELATIONSHIP_TYPE_TOKEN_STORE; +import static org.neo4j.kernel.impl.store.StoreFile.SCHEMA_STORE; + +@Service.Implementation( ManagementBeanProvider.class ) +public final class StoreSizeBean extends ManagementBeanProvider +{ + StoreSizeBean() + { + super( StoreSize.class ); + } + + @Override + protected Neo4jMBean createMBean( ManagementData management ) throws NotCompliantMBeanException + { + return new StoreSizeImpl( management, false ); + } + + @Override + protected Neo4jMBean createMXBean( ManagementData management ) throws NotCompliantMBeanException + { + return new StoreSizeImpl( management, true ); + } + + static class StoreSizeImpl extends Neo4jMBean implements StoreSize + { + private final FileSystemAbstraction fs; + private final File storePath; + + private PhysicalLogFiles physicalLogFiles; + private LegacyIndexProviderLookup legacyIndexProviderLookup; + private SchemaIndexProvider schemaIndexProvider; + private LabelScanStore labelScanStore; + + StoreSizeImpl( ManagementData management, boolean isMXBean ) throws NotCompliantMBeanException + { + super( management, isMXBean ); + + fs = management.getKernelData().getFilesystemAbstraction(); + storePath = resolveStorePath( management ); + + DataSourceManager dataSourceManager = management.resolveDependency( DataSourceManager.class ); + dataSourceManager.addListener( new DataSourceManager.Listener() + { + @Override + public void registered( NeoStoreDataSource ds ) + { + physicalLogFiles = resolveDependency( ds, PhysicalLogFiles.class ); + legacyIndexProviderLookup = resolveDependency( ds, LegacyIndexProviderLookup.class ); + schemaIndexProvider = resolveDependency( ds, SchemaIndexProvider.class ); + labelScanStore = resolveDependency( ds, LabelScanStore.class ); + } + + private T resolveDependency( NeoStoreDataSource ds, Class clazz ) + { + return ds.getDependencyResolver().resolveDependency( clazz ); + } + + @Override + public void unregistered( NeoStoreDataSource ds ) + { + physicalLogFiles = null; + legacyIndexProviderLookup = null; + schemaIndexProvider = null; + labelScanStore = null; + } + } ); + } + + @Override + public long getTransactionLogsSize() + { + TotalSizeVersionVisitor logVersionVisitor = new TotalSizeVersionVisitor( fs ); + + physicalLogFiles.accept( logVersionVisitor ); + + return logVersionVisitor.getTotalSize(); + } + + @Override + public long getNodeStoreSize() + { + return sizeOfStoreFiles( NODE_STORE, NODE_LABEL_STORE ); + } + + @Override + public long getRelationshipStoreSize() + { + return sizeOfStoreFiles( RELATIONSHIP_STORE, RELATIONSHIP_GROUP_STORE, RELATIONSHIP_TYPE_TOKEN_STORE, + RELATIONSHIP_TYPE_TOKEN_NAMES_STORE ); + } + + @Override + public long getPropertyStoreSize() + { + return sizeOfStoreFiles( PROPERTY_STORE, PROPERTY_KEY_TOKEN_STORE, PROPERTY_KEY_TOKEN_NAMES_STORE ); + } + + @Override + public long getStringStoreSize() + { + return sizeOfStoreFiles( PROPERTY_STRING_STORE ); + } + + @Override + public long getArrayStoreSize() + { + return sizeOfStoreFiles( PROPERTY_ARRAY_STORE ); + } + + @Override + public long getLabelStoreSize() + { + return sizeOfStoreFiles( LABEL_TOKEN_STORE, LABEL_TOKEN_NAMES_STORE ); + } + + @Override + public long getCountStoreSize() + { + return sizeOfStoreFiles( COUNTS_STORE_LEFT, COUNTS_STORE_RIGHT ); + } + + @Override + public long getSchemaStoreSize() + { + return sizeOfStoreFiles( SCHEMA_STORE ); + } + + @Override + public long getIndexStoreSize() + { + long size = 0L; + + // Add legacy indices + for ( IndexImplementation index : legacyIndexProviderLookup.all() ) + { + size += FileUtils.size( fs, index.getIndexImplementationDirectory( storePath ) ); + } + + // Add schema index + size += FileUtils.size( fs, schemaIndexProvider.getSchemaIndexStoreDirectory( storePath ) ); + + // Add label index + size += FileUtils.size( fs, labelScanStore.getLabelScanStoreFile() ); + + return size; + } + + @Override + public long getTotalStoreSize() + { + return storePath == null ? 0L : FileUtils.size( fs, storePath ); + } + + private long sizeOf( String name ) + { + return storePath == null ? 0L : FileUtils.size( fs, new File( storePath, name ) ); + } + + private static class TotalSizeVersionVisitor implements PhysicalLogFiles.LogVersionVisitor + { + private final FileSystemAbstraction fs; + private long totalSize; + + TotalSizeVersionVisitor( FileSystemAbstraction fs ) + { + this.fs = fs; + } + + long getTotalSize() + { + return totalSize; + } + + @Override + public void visit( File file, long logVersion ) + { + totalSize += FileUtils.size( fs, file ); + } + } + + /** + * Count the total file size, including id files, of {@link StoreFile}s. + * Missing files will be counted as 0 bytes. + * + * @param files the file types to count + * @return the total size in bytes of the files + */ + private long sizeOfStoreFiles( StoreFile... files ) + { + long size = 0L; + for ( StoreFile file : files ) + { + // Get size of both store and id file + size += sizeOf( file.fileName( StoreFileType.STORE ) ); + if ( file.isRecordStore() ) + { + size += sizeOf( file.fileName( StoreFileType.ID ) ); + } + } + return size; + } + + private File resolveStorePath( ManagementData management ) + { + File storeDir = management.getKernelData().getStoreDir(); + try + { + return storeDir.getCanonicalFile().getAbsoluteFile(); + } + catch ( IOException e ) + { + return storeDir.getAbsoluteFile(); + } + } + } +} diff --git a/community/jmx/src/main/resources/META-INF/services/org.neo4j.jmx.impl.ManagementBeanProvider b/community/jmx/src/main/resources/META-INF/services/org.neo4j.jmx.impl.ManagementBeanProvider index 7a18f779a4848..469504e82ed6a 100644 --- a/community/jmx/src/main/resources/META-INF/services/org.neo4j.jmx.impl.ManagementBeanProvider +++ b/community/jmx/src/main/resources/META-INF/services/org.neo4j.jmx.impl.ManagementBeanProvider @@ -1,2 +1,3 @@ org.neo4j.jmx.impl.PrimitivesBean org.neo4j.jmx.impl.StoreFileBean +org.neo4j.jmx.impl.StoreSizeBean diff --git a/community/jmx/src/test/java/org/neo4j/jmx/impl/StoreFileBeanTest.java b/community/jmx/src/test/java/org/neo4j/jmx/impl/StoreFileBeanTest.java deleted file mode 100644 index eadd7c87c2c9f..0000000000000 --- a/community/jmx/src/test/java/org/neo4j/jmx/impl/StoreFileBeanTest.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (c) 2002-2017 "Neo Technology," - * Network Engine for Objects in Lund AB [http://neotechnology.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.jmx.impl; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import java.io.File; -import java.io.IOException; -import java.nio.ByteBuffer; - -import org.neo4j.graphdb.mockfs.EphemeralFileSystemAbstraction; -import org.neo4j.io.fs.FileSystemAbstraction; -import org.neo4j.io.fs.StoreChannel; -import org.neo4j.io.pagecache.PageCache; -import org.neo4j.jmx.StoreFile; -import org.neo4j.kernel.NeoStoreDataSource; -import org.neo4j.kernel.api.index.SchemaIndexProvider; -import org.neo4j.kernel.api.labelscan.LabelScanStore; -import org.neo4j.kernel.configuration.Config; -import org.neo4j.kernel.impl.api.LegacyIndexProviderLookup; -import org.neo4j.kernel.impl.transaction.log.LogFile; -import org.neo4j.kernel.impl.transaction.log.PhysicalLogFiles; -import org.neo4j.kernel.impl.transaction.state.DataSourceManager; -import org.neo4j.kernel.impl.util.Dependencies; -import org.neo4j.kernel.internal.DefaultKernelData; -import org.neo4j.kernel.internal.GraphDatabaseAPI; -import org.neo4j.kernel.internal.KernelData; -import org.neo4j.kernel.spi.legacyindex.IndexImplementation; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import static org.neo4j.helpers.collection.Iterables.iterable; - -public class StoreFileBeanTest -{ - private FileSystemAbstraction fs = new EphemeralFileSystemAbstraction(); - private File storeDir = new File( "" ); - private PhysicalLogFiles physicalLogFiles = new PhysicalLogFiles( storeDir, fs ); - private LegacyIndexProviderLookup legacyIndexProviderLookup = mock( LegacyIndexProviderLookup.class ); - private SchemaIndexProvider schemaIndexProvider = mock( SchemaIndexProvider.class ); - private LabelScanStore labelScanStore = mock( LabelScanStore.class ); - private StoreFile storeFileBean; - - @Before - public void setUp() throws Throwable - { - LogFile logFile = mock( LogFile.class ); - DataSourceManager dataSourceManager = new DataSourceManager(); - GraphDatabaseAPI db = mock( GraphDatabaseAPI.class ); - NeoStoreDataSource dataSource = mock( NeoStoreDataSource.class ); - - // Setup all dependencies - Dependencies dependencies = new Dependencies(); - dependencies.satisfyDependency( fs ); - dependencies.satisfyDependencies( dataSourceManager ); - dependencies.satisfyDependency( logFile ); - dependencies.satisfyDependency( physicalLogFiles ); - dependencies.satisfyDependency( legacyIndexProviderLookup ); - dependencies.satisfyDependency( schemaIndexProvider ); - dependencies.satisfyDependency( labelScanStore ); - when( db.getDependencyResolver() ).thenReturn( dependencies ); - when( dataSource.getDependencyResolver() ).thenReturn( dependencies ); - - // Start DataSourceManager - when( dataSource.getStoreDir() ).thenReturn( storeDir ); - dataSourceManager.register( dataSource ); - dataSourceManager.start(); - - // Create bean - KernelData kernelData = new DefaultKernelData( fs, mock( PageCache.class ), storeDir, Config.defaults(), db ); - ManagementData data = new ManagementData( new StoreFileBean(), kernelData, ManagementSupport.load() ); - storeFileBean = (StoreFile) new StoreFileBean().createMBean( data ); - } - - @Test - public void shouldCountAllLogFiles() throws Throwable - { - createFileOfSize( physicalLogFiles.getLogFileForVersion( 0 ), 10 ); - createFileOfSize( physicalLogFiles.getLogFileForVersion( 1 ), 20 ); - - assertEquals( 30, storeFileBean.getAllLogicalLogsSize() ); - } - - @Test - public void shouldCountAllIndexFiles() throws Exception - { - // Legacy index file - File legacyIndex = new File( storeDir, "legacyIndex" ); - createFileOfSize( legacyIndex, 10 ); - - IndexImplementation indexImplementation = mock( IndexImplementation.class ); - when( indexImplementation.getIndexImplementationDirectory( Mockito.any() ) ).thenReturn( legacyIndex ); - when( legacyIndexProviderLookup.all() ).thenReturn( iterable( indexImplementation ) ); - - // Legacy index file - File schemaIndex = new File( storeDir, "schemaIndex" ); - createFileOfSize( schemaIndex, 5 ); - when( schemaIndexProvider.getSchemaIndexStoreDirectory( Mockito.any() ) ).thenReturn( schemaIndex ); - - // Label scan store - File labelScan = new File( storeDir, "labelScanStore" ); - createFileOfSize( labelScan, 20 ); - when( labelScanStore.getLabelScanStoreFile() ).thenReturn( labelScan ); - - // Count all files - assertEquals( 35, storeFileBean.getIndexStoreSize() ); - } - - private void createFileOfSize( File file, int size ) throws IOException - { - try ( StoreChannel storeChannel = fs.create( file ) ) - { - byte[] bytes = new byte[size]; - ByteBuffer buffer = ByteBuffer.wrap( bytes ); - storeChannel.writeAll( buffer ); - } - } -} diff --git a/community/jmx/src/test/java/org/neo4j/jmx/impl/StoreSizeBeanTest.java b/community/jmx/src/test/java/org/neo4j/jmx/impl/StoreSizeBeanTest.java new file mode 100644 index 0000000000000..13473768057df --- /dev/null +++ b/community/jmx/src/test/java/org/neo4j/jmx/impl/StoreSizeBeanTest.java @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2002-2017 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.jmx.impl; + +import org.junit.Before; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; + +import org.neo4j.graphdb.mockfs.EphemeralFileSystemAbstraction; +import org.neo4j.io.fs.FileSystemAbstraction; +import org.neo4j.io.fs.StoreChannel; +import org.neo4j.io.pagecache.PageCache; +import org.neo4j.jmx.StoreSize; +import org.neo4j.kernel.NeoStoreDataSource; +import org.neo4j.kernel.api.index.SchemaIndexProvider; +import org.neo4j.kernel.api.labelscan.LabelScanStore; +import org.neo4j.kernel.configuration.Config; +import org.neo4j.kernel.impl.api.LegacyIndexProviderLookup; +import org.neo4j.kernel.impl.transaction.log.PhysicalLogFiles; +import org.neo4j.kernel.impl.transaction.state.DataSourceManager; +import org.neo4j.kernel.impl.util.Dependencies; +import org.neo4j.kernel.internal.DefaultKernelData; +import org.neo4j.kernel.internal.GraphDatabaseAPI; +import org.neo4j.kernel.internal.KernelData; +import org.neo4j.kernel.spi.legacyindex.IndexImplementation; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.neo4j.helpers.collection.Iterables.iterable; +import static org.neo4j.kernel.impl.store.StoreFile.COUNTS_STORE_LEFT; +import static org.neo4j.kernel.impl.store.StoreFile.COUNTS_STORE_RIGHT; +import static org.neo4j.kernel.impl.store.StoreFile.LABEL_TOKEN_NAMES_STORE; +import static org.neo4j.kernel.impl.store.StoreFile.LABEL_TOKEN_STORE; +import static org.neo4j.kernel.impl.store.StoreFile.NODE_LABEL_STORE; +import static org.neo4j.kernel.impl.store.StoreFile.NODE_STORE; +import static org.neo4j.kernel.impl.store.StoreFile.PROPERTY_ARRAY_STORE; +import static org.neo4j.kernel.impl.store.StoreFile.PROPERTY_KEY_TOKEN_NAMES_STORE; +import static org.neo4j.kernel.impl.store.StoreFile.PROPERTY_KEY_TOKEN_STORE; +import static org.neo4j.kernel.impl.store.StoreFile.PROPERTY_STORE; +import static org.neo4j.kernel.impl.store.StoreFile.PROPERTY_STRING_STORE; +import static org.neo4j.kernel.impl.store.StoreFile.RELATIONSHIP_GROUP_STORE; +import static org.neo4j.kernel.impl.store.StoreFile.RELATIONSHIP_STORE; +import static org.neo4j.kernel.impl.store.StoreFile.RELATIONSHIP_TYPE_TOKEN_NAMES_STORE; +import static org.neo4j.kernel.impl.store.StoreFile.RELATIONSHIP_TYPE_TOKEN_STORE; +import static org.neo4j.kernel.impl.store.StoreFile.SCHEMA_STORE; +import static org.neo4j.kernel.impl.storemigration.StoreFileType.ID; +import static org.neo4j.kernel.impl.storemigration.StoreFileType.STORE; + +public class StoreSizeBeanTest +{ + private FileSystemAbstraction fs = new EphemeralFileSystemAbstraction(); + private File storeDir = new File( "" ); + private PhysicalLogFiles physicalLogFiles = new PhysicalLogFiles( storeDir, fs ); + private LegacyIndexProviderLookup legacyIndexProviderLookup = mock( LegacyIndexProviderLookup.class ); + private SchemaIndexProvider schemaIndexProvider = mock( SchemaIndexProvider.class ); + private LabelScanStore labelScanStore = mock( LabelScanStore.class ); + private StoreSize storeSizeBean; + + @Before + public void setUp() throws Throwable + { + DataSourceManager dataSourceManager = new DataSourceManager(); + GraphDatabaseAPI db = mock( GraphDatabaseAPI.class ); + NeoStoreDataSource dataSource = mock( NeoStoreDataSource.class ); + + // Setup all dependencies + Dependencies dependencies = new Dependencies(); + dependencies.satisfyDependency( fs ); + dependencies.satisfyDependencies( dataSourceManager ); + dependencies.satisfyDependency( physicalLogFiles ); + dependencies.satisfyDependency( legacyIndexProviderLookup ); + dependencies.satisfyDependency( schemaIndexProvider ); + dependencies.satisfyDependency( labelScanStore ); + when( db.getDependencyResolver() ).thenReturn( dependencies ); + when( dataSource.getDependencyResolver() ).thenReturn( dependencies ); + + // Start DataSourceManager + dataSourceManager.register( dataSource ); + dataSourceManager.start(); + + // Create bean + KernelData kernelData = new DefaultKernelData( fs, mock( PageCache.class ), storeDir, Config.empty(), db ); + ManagementData data = new ManagementData( new StoreSizeBean(), kernelData, ManagementSupport.load() ); + storeSizeBean = (StoreSize) new StoreSizeBean().createMBean( data ); + + // Create a simulated file structure of the store + Map dummyStore = new HashMap<>(); + dummyStore.put( NODE_STORE.fileName( STORE ), 1 ); + dummyStore.put( NODE_STORE.fileName( ID ), 2 ); + dummyStore.put( NODE_LABEL_STORE.fileName( STORE ), 3 ); + dummyStore.put( NODE_LABEL_STORE.fileName( ID ), 4 ); + dummyStore.put( PROPERTY_STORE.fileName( STORE ), 5 ); + dummyStore.put( PROPERTY_STORE.fileName( ID ), 6 ); + dummyStore.put( PROPERTY_KEY_TOKEN_STORE.fileName( STORE ), 7 ); + dummyStore.put( PROPERTY_KEY_TOKEN_STORE.fileName( ID ), 8 ); + dummyStore.put( PROPERTY_KEY_TOKEN_NAMES_STORE.fileName( STORE ), 9 ); + dummyStore.put( PROPERTY_KEY_TOKEN_NAMES_STORE.fileName( ID ), 10 ); + dummyStore.put( PROPERTY_STRING_STORE.fileName( STORE ), 11 ); + dummyStore.put( PROPERTY_STRING_STORE.fileName( ID ), 12 ); + dummyStore.put( PROPERTY_ARRAY_STORE.fileName( STORE ), 13 ); + dummyStore.put( PROPERTY_ARRAY_STORE.fileName( ID ), 14 ); + dummyStore.put( RELATIONSHIP_STORE.fileName( STORE ), 15 ); + dummyStore.put( RELATIONSHIP_STORE.fileName( ID ), 16 ); + dummyStore.put( RELATIONSHIP_GROUP_STORE.fileName( STORE ), 17 ); + dummyStore.put( RELATIONSHIP_GROUP_STORE.fileName( ID ), 18 ); + dummyStore.put( RELATIONSHIP_TYPE_TOKEN_STORE.fileName( STORE ), 19 ); + dummyStore.put( RELATIONSHIP_TYPE_TOKEN_STORE.fileName( ID ), 20 ); + dummyStore.put( RELATIONSHIP_TYPE_TOKEN_NAMES_STORE.fileName( STORE ), 21 ); + dummyStore.put( RELATIONSHIP_TYPE_TOKEN_NAMES_STORE.fileName( ID ), 22 ); + dummyStore.put( LABEL_TOKEN_STORE.fileName( STORE ), 23 ); + dummyStore.put( LABEL_TOKEN_STORE.fileName( ID ), 24 ); + dummyStore.put( LABEL_TOKEN_NAMES_STORE.fileName( STORE ), 25 ); + dummyStore.put( LABEL_TOKEN_NAMES_STORE.fileName( ID ), 26 ); + dummyStore.put( SCHEMA_STORE.fileName( STORE ), 27 ); + dummyStore.put( SCHEMA_STORE.fileName( ID ), 28 ); + dummyStore.put( COUNTS_STORE_LEFT.fileName( STORE ), 29 ); + // COUNTS_STORE_RIGHT is created in the test + + File storeDirAbsolute = storeDir.getCanonicalFile().getAbsoluteFile(); + for ( Map.Entry dummyFile : dummyStore.entrySet() ) + { + createFileOfSize( new File( storeDirAbsolute, dummyFile.getKey() ), dummyFile.getValue() ); + } + } + + @Test + public void sumAllNodeRelatedFiles() throws Exception + { + assertEquals( getExpected(1, 4 ), storeSizeBean.getNodeStoreSize() ); + } + + @Test + public void sumAllPropertyRelatedFiles() throws Exception + { + assertEquals( getExpected( 5, 10 ), storeSizeBean.getPropertyStoreSize() ); + } + + @Test + public void sumAllStringRelatedFiles() throws Exception + { + assertEquals( getExpected(11, 12 ), storeSizeBean.getStringStoreSize() ); + } + + @Test + public void sumAllArrayRelatedFiles() throws Exception + { + assertEquals( getExpected(13, 14 ), storeSizeBean.getArrayStoreSize() ); + } + + @Test + public void sumAllRelationshipRelatedFiles() throws Exception + { + assertEquals( getExpected( 15, 22 ), storeSizeBean.getRelationshipStoreSize() ); + } + + @Test + public void sumAllLabelRelatedFiles() throws Exception + { + assertEquals( getExpected( 23, 26 ), storeSizeBean.getLabelStoreSize() ); + } + + @Test + public void sumAllCountStoreRelatedFiles() throws Exception + { + assertEquals( getExpected( 29, 29), storeSizeBean.getCountStoreSize() ); + createFileOfSize( new File( storeDir.getCanonicalFile().getAbsoluteFile(), COUNTS_STORE_RIGHT.fileName( STORE ) ), 30 ); + assertEquals( getExpected( 29, 30), storeSizeBean.getCountStoreSize() ); + } + + @Test + public void sumAllSchemaRelatedFiles() throws Exception + { + assertEquals( getExpected( 27, 28 ), storeSizeBean.getSchemaStoreSize() ); + } + + @Test + public void sumAllFiles() throws Exception + { + assertEquals( getExpected( 0, 29 ), storeSizeBean.getTotalStoreSize() ); + } + + @Test + public void shouldCountAllLogFiles() throws Throwable + { + createFileOfSize( physicalLogFiles.getLogFileForVersion( 0 ), 1 ); + createFileOfSize( physicalLogFiles.getLogFileForVersion( 1 ), 2 ); + + assertEquals( 3L, storeSizeBean.getTransactionLogsSize() ); + } + + @Test + public void shouldCountAllIndexFiles() throws Exception + { + // Legacy index file + File legacyIndex = new File( storeDir, "legacyIndex" ); + createFileOfSize( legacyIndex, 1 ); + + IndexImplementation indexImplementation = mock( IndexImplementation.class ); + when( indexImplementation.getIndexImplementationDirectory( any() ) ).thenReturn( legacyIndex ); + when( legacyIndexProviderLookup.all() ).thenReturn( iterable( indexImplementation ) ); + + // Legacy index file + File schemaIndex = new File( storeDir, "schemaIndex" ); + createFileOfSize( schemaIndex, 2 ); + when( schemaIndexProvider.getSchemaIndexStoreDirectory( any() ) ).thenReturn( schemaIndex ); + + // Label scan store + File labelScan = new File( storeDir, "labelScanStore" ); + createFileOfSize( labelScan, 4 ); + when( labelScanStore.getLabelScanStoreFile() ).thenReturn( labelScan ); + + // Count all files + assertEquals( 7, storeSizeBean.getIndexStoreSize() ); + } + + private void createFileOfSize( File file, int size ) throws IOException + { + try ( StoreChannel storeChannel = fs.create( file ) ) + { + byte[] bytes = new byte[size]; + ByteBuffer buffer = ByteBuffer.wrap( bytes ); + storeChannel.writeAll( buffer ); + } + } + + private long getExpected( int lower, int upper ) + { + long expected = 0; + for ( int i = lower; i <= upper; i++ ) + { + expected += i; + } + return expected; + } +}