Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Reworked sanity checking of memory requirements in importer
- Loading branch information
Showing
14 changed files
with
536 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
126 changes: 126 additions & 0 deletions
126
community/import-tool/src/test/java/org/neo4j/tooling/PrintingImportLogicMonitorTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,126 @@ | |||
package org.neo4j.tooling; | |||
|
|||
import org.junit.After; | |||
import org.junit.Test; | |||
|
|||
import java.io.ByteArrayOutputStream; | |||
import java.io.PrintStream; | |||
|
|||
import org.neo4j.unsafe.impl.batchimport.ImportLogic; | |||
|
|||
import static org.junit.Assert.assertTrue; | |||
|
|||
import static org.neo4j.helpers.Format.bytes; | |||
import static org.neo4j.io.ByteUnit.gibiBytes; | |||
|
|||
/** | |||
* Why test a silly thing like this? This implementation contains some printf calls that needs to get arguments correct | |||
* or will otherwise throw exception. It's surprisingly easy to get those wrong. | |||
*/ | |||
public class PrintingImportLogicMonitorTest | |||
{ | |||
private final ByteArrayOutputStream outBuffer = new ByteArrayOutputStream(); | |||
private final PrintStream out = new PrintStream( outBuffer ); | |||
private final ByteArrayOutputStream errBuffer = new ByteArrayOutputStream(); | |||
private final PrintStream err = new PrintStream( errBuffer ); | |||
private final ImportLogic.Monitor monitor = new PrintingImportLogicMonitor( out, err ); | |||
|
|||
@After | |||
public void after() | |||
{ | |||
System.out.println( "out " + outBuffer ); | |||
System.out.println( "err " + errBuffer ); | |||
} | |||
|
|||
@Test | |||
public void mayExceedNodeIdCapacity() throws Exception | |||
{ | |||
// given | |||
long capacity = 10_000_000; | |||
long estimatedCount = 12_000_000; | |||
|
|||
// when | |||
monitor.mayExceedNodeIdCapacity( capacity, estimatedCount ); | |||
|
|||
// then | |||
String text = errBuffer.toString(); | |||
assertTrue( text.contains( "WARNING" ) ); | |||
assertTrue( text.contains( "exceed" ) ); | |||
assertTrue( text.contains( String.valueOf( capacity ) ) ); | |||
assertTrue( text.contains( String.valueOf( estimatedCount ) ) ); | |||
} | |||
|
|||
@Test | |||
public void mayExceedRelationshipIdCapacity() throws Exception | |||
{ | |||
// given | |||
long capacity = 10_000_000; | |||
long estimatedCount = 12_000_000; | |||
|
|||
// when | |||
monitor.mayExceedRelationshipIdCapacity( capacity, estimatedCount ); | |||
|
|||
// then | |||
String text = errBuffer.toString(); | |||
assertTrue( text.contains( "WARNING" ) ); | |||
assertTrue( text.contains( "exceed" ) ); | |||
assertTrue( text.contains( String.valueOf( capacity ) ) ); | |||
assertTrue( text.contains( String.valueOf( estimatedCount ) ) ); | |||
} | |||
|
|||
@Test | |||
public void insufficientHeapSize() throws Exception | |||
{ | |||
// given | |||
long optimalHeapSize = gibiBytes( 2 ); | |||
long heapSize = gibiBytes( 1 ); | |||
|
|||
// when | |||
monitor.insufficientHeapSize( optimalHeapSize, heapSize ); | |||
|
|||
// then | |||
String text = errBuffer.toString(); | |||
assertTrue( text.contains( "WARNING" ) ); | |||
assertTrue( text.contains( "too small" ) ); | |||
assertTrue( text.contains( bytes( heapSize ) ) ); | |||
assertTrue( text.contains( bytes( optimalHeapSize ) ) ); | |||
} | |||
|
|||
@Test | |||
public void abundantHeapSize() throws Exception | |||
{ | |||
// given | |||
long optimalHeapSize = gibiBytes( 2 ); | |||
long heapSize = gibiBytes( 10 ); | |||
|
|||
// when | |||
monitor.abundantHeapSize( optimalHeapSize, heapSize ); | |||
|
|||
// then | |||
String text = errBuffer.toString(); | |||
assertTrue( text.contains( "WARNING" ) ); | |||
assertTrue( text.contains( "unnecessarily large" ) ); | |||
assertTrue( text.contains( bytes( heapSize ) ) ); | |||
assertTrue( text.contains( bytes( optimalHeapSize ) ) ); | |||
} | |||
|
|||
@Test | |||
public void insufficientAvailableMemory() throws Exception | |||
{ | |||
// given | |||
long estimatedCacheSize = gibiBytes( 2 ); | |||
long optimalHeapSize = gibiBytes( 2 ); | |||
long availableMemory = gibiBytes( 1 ); | |||
|
|||
// when | |||
monitor.insufficientAvailableMemory( estimatedCacheSize, optimalHeapSize, availableMemory ); | |||
|
|||
// then | |||
String text = errBuffer.toString(); | |||
assertTrue( text.contains( "WARNING" ) ); | |||
assertTrue( text.contains( "may not be sufficient" ) ); | |||
assertTrue( text.contains( bytes( estimatedCacheSize ) ) ); | |||
assertTrue( text.contains( bytes( optimalHeapSize ) ) ); | |||
assertTrue( text.contains( bytes( availableMemory ) ) ); | |||
} | |||
} |
86 changes: 86 additions & 0 deletions
86
community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/HeapSizeSanityChecker.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,86 @@ | |||
/* | |||
* Copyright (c) 2002-2018 "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 <http://www.gnu.org/licenses/>. | |||
*/ | |||
package org.neo4j.unsafe.impl.batchimport; | |||
|
|||
import java.util.function.LongSupplier; | |||
|
|||
import org.neo4j.kernel.impl.store.format.RecordFormats; | |||
import org.neo4j.kernel.impl.util.OsBeanUtil; | |||
import org.neo4j.unsafe.impl.batchimport.cache.MemoryStatsVisitor; | |||
import org.neo4j.unsafe.impl.batchimport.input.Input; | |||
|
|||
import static org.neo4j.kernel.impl.util.OsBeanUtil.VALUE_UNAVAILABLE; | |||
import static org.neo4j.unsafe.impl.batchimport.ImportMemoryCalculator.estimatedCacheSize; | |||
import static org.neo4j.unsafe.impl.batchimport.ImportMemoryCalculator.optimalMinimalHeapSize; | |||
|
|||
/** | |||
* Sanity checking of {@link org.neo4j.unsafe.impl.batchimport.input.Input.Estimates} against heap size and free memory. | |||
* Registers warnings onto a {@link ImportLogic.Monitor}. | |||
*/ | |||
public class HeapSizeSanityChecker | |||
{ | |||
private final ImportLogic.Monitor monitor; | |||
private final LongSupplier freeMemoryLookup; | |||
private final LongSupplier actualHeapSizeLookup; | |||
|
|||
HeapSizeSanityChecker( ImportLogic.Monitor monitor ) | |||
{ | |||
this( monitor, OsBeanUtil::getFreePhysicalMemory, Runtime.getRuntime()::maxMemory ); | |||
} | |||
|
|||
public HeapSizeSanityChecker( ImportLogic.Monitor monitor, LongSupplier freeMemoryLookup, LongSupplier actualHeapSizeLookup ) | |||
{ | |||
this.monitor = monitor; | |||
this.freeMemoryLookup = freeMemoryLookup; | |||
this.actualHeapSizeLookup = actualHeapSizeLookup; | |||
} | |||
|
|||
public void sanityCheck( Input.Estimates inputEstimates, RecordFormats recordFormats, MemoryStatsVisitor.Visitable baseMemory, | |||
MemoryStatsVisitor.Visitable... memoryVisitables ) | |||
{ | |||
// At this point in time the store hasn't started so it won't show up in free memory reported from OS, | |||
// i.e. we have to include it here in the calculations. | |||
long estimatedCacheSize = estimatedCacheSize( baseMemory, memoryVisitables ); | |||
long freeMemory = freeMemoryLookup.getAsLong(); | |||
long optimalMinimalHeapSize = optimalMinimalHeapSize( inputEstimates, recordFormats ); | |||
long actualHeapSize = actualHeapSizeLookup.getAsLong(); | |||
boolean freeMemoryIsKnown = freeMemory != VALUE_UNAVAILABLE; | |||
|
|||
// Check if there's enough memory for the import | |||
if ( freeMemoryIsKnown && actualHeapSize + freeMemory < estimatedCacheSize + optimalMinimalHeapSize ) | |||
{ | |||
monitor.insufficientAvailableMemory( estimatedCacheSize, optimalMinimalHeapSize, freeMemory ); | |||
return; // there's likely not available memory, no need to warn about anything else | |||
} | |||
|
|||
// Check if the heap is big enough to handle the import | |||
if ( actualHeapSize < optimalMinimalHeapSize ) | |||
{ | |||
monitor.insufficientHeapSize( optimalMinimalHeapSize, actualHeapSize ); | |||
return; // user have been warned about heap size issue | |||
} | |||
|
|||
// Check if heap size could be tweaked | |||
if ( ((freeMemoryIsKnown && freeMemory < estimatedCacheSize) || !freeMemoryIsKnown) && actualHeapSize > optimalMinimalHeapSize * 1.2 ) | |||
{ | |||
monitor.abundantHeapSize( optimalMinimalHeapSize, actualHeapSize ); | |||
} | |||
} | |||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.