diff --git a/community/io/src/main/java/org/neo4j/io/pagecache/PageCursor.java b/community/io/src/main/java/org/neo4j/io/pagecache/PageCursor.java
index 0ac1137a7423c..e899b263c198f 100644
--- a/community/io/src/main/java/org/neo4j/io/pagecache/PageCursor.java
+++ b/community/io/src/main/java/org/neo4j/io/pagecache/PageCursor.java
@@ -299,7 +299,8 @@ public abstract class PageCursor implements AutoCloseable
public abstract boolean checkAndClearBoundsFlag();
/**
- * Check if a cursor error has been set, and if so, remove it from the cursor and throw it.
+ * Check if a cursor error has been set on this or any linked cursor, and if so, remove it from the cursor
+ * and throw it as a {@link CursorException}.
*/
public abstract void checkAndClearCursorError() throws CursorException;
@@ -321,6 +322,12 @@ public abstract class PageCursor implements AutoCloseable
*/
public abstract void setCursorError( String message );
+ /**
+ * Unconditionally clear any error condition that has been set on this or any linked cursor, without throwing an
+ * exception.
+ */
+ public abstract void clearCursorError();
+
/**
* Open a new page cursor with the same pf_flags as this cursor, as if calling the {@link PagedFile#io(long, int)}
* on the relevant paged file. This cursor will then also delegate to the linked cursor when checking
diff --git a/community/io/src/main/java/org/neo4j/io/pagecache/impl/CompositePageCursor.java b/community/io/src/main/java/org/neo4j/io/pagecache/impl/CompositePageCursor.java
index c0795cb0d65bd..9dca3472f6971 100644
--- a/community/io/src/main/java/org/neo4j/io/pagecache/impl/CompositePageCursor.java
+++ b/community/io/src/main/java/org/neo4j/io/pagecache/impl/CompositePageCursor.java
@@ -470,6 +470,13 @@ public void setCursorError( String message )
cursor( 0 ).setCursorError( message );
}
+ @Override
+ public void clearCursorError()
+ {
+ first.clearCursorError();
+ second.clearCursorError();
+ }
+
@Override
public PageCursor openLinkedCursor( long pageId )
{
diff --git a/community/io/src/main/java/org/neo4j/io/pagecache/impl/DelegatingPageCursor.java b/community/io/src/main/java/org/neo4j/io/pagecache/impl/DelegatingPageCursor.java
index d58f61cf2c6eb..b70a0399a0178 100644
--- a/community/io/src/main/java/org/neo4j/io/pagecache/impl/DelegatingPageCursor.java
+++ b/community/io/src/main/java/org/neo4j/io/pagecache/impl/DelegatingPageCursor.java
@@ -155,6 +155,12 @@ public void setCursorError( String message )
delegate.setCursorError( message );
}
+ @Override
+ public void clearCursorError()
+ {
+ delegate.clearCursorError();
+ }
+
@Override
public PageCursor openLinkedCursor( long pageId )
{
diff --git a/community/io/src/main/java/org/neo4j/io/pagecache/impl/muninn/CursorExceptionWithPreciseStackTrace.java b/community/io/src/main/java/org/neo4j/io/pagecache/impl/muninn/CursorExceptionWithPreciseStackTrace.java
new file mode 100644
index 0000000000000..40b4cfce17913
--- /dev/null
+++ b/community/io/src/main/java/org/neo4j/io/pagecache/impl/muninn/CursorExceptionWithPreciseStackTrace.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2002-2016 "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.io.pagecache.impl.muninn;
+
+import org.neo4j.io.pagecache.CursorException;
+
+/**
+ * This exception is used to indicate in the stack trace, whether or not
+ * {@link MuninnPageCursor#usePreciseCursorErrorStackTraces} is enabled.
+ *
+ * This is useful because it allows us to clearly see how much we can trust the stack traces for decoding errors.
+ */
+final class CursorExceptionWithPreciseStackTrace extends CursorException
+{
+ public CursorExceptionWithPreciseStackTrace( String message )
+ {
+ super( message );
+ }
+}
diff --git a/community/io/src/main/java/org/neo4j/io/pagecache/impl/muninn/MuninnPageCursor.java b/community/io/src/main/java/org/neo4j/io/pagecache/impl/muninn/MuninnPageCursor.java
index 0438114e0110d..b1ccbe01b0e28 100644
--- a/community/io/src/main/java/org/neo4j/io/pagecache/impl/muninn/MuninnPageCursor.java
+++ b/community/io/src/main/java/org/neo4j/io/pagecache/impl/muninn/MuninnPageCursor.java
@@ -21,6 +21,7 @@
import java.io.File;
import java.io.IOException;
+import java.util.Objects;
import org.neo4j.concurrent.BinaryLatch;
import org.neo4j.io.pagecache.CursorException;
@@ -38,6 +39,9 @@ abstract class MuninnPageCursor extends PageCursor
private static final boolean tracePinnedCachePageId =
flag( MuninnPageCursor.class, "tracePinnedCachePageId", false );
+ private static final boolean usePreciseCursorErrorStackTraces =
+ flag( MuninnPageCursor.class, "usePreciseCursorErrorStackTraces", true );
+
// Size of the respective primitive types in bytes.
private static final int SIZE_OF_BYTE = Byte.BYTES;
private static final int SIZE_OF_SHORT = Short.BYTES;
@@ -60,7 +64,10 @@ abstract class MuninnPageCursor extends PageCursor
private int filePageSize;
private int offset;
private boolean outOfBounds;
- private String cursorExceptionMessage;
+ // This is a String with the exception message if usePreciseCursorErrorStackTraces is false, otherwise it is a
+ // CursorExceptionWithPreciseStackTrace with the message and stack trace pointing more or less directly at the
+ // offending code.
+ private Object cursorException;
MuninnPageCursor( long victimPage )
{
@@ -104,7 +111,7 @@ public final void reset( MuninnPage page )
@Override
public final boolean next( long pageId ) throws IOException
{
- if ( currentPageId == nextPageId )
+ if ( currentPageId == pageId )
{
return true;
}
@@ -166,7 +173,7 @@ void clearPageState()
pageSize = 0; // make all future bound checks fail
page = null; // make all future page navigation fail
currentPageId = UNBOUND_PAGE_ID;
- cursorExceptionMessage = null;
+ cursorException = null;
}
@Override
@@ -708,18 +715,26 @@ public void checkAndClearCursorError() throws CursorException
MuninnPageCursor cursor = this;
do
{
- String message = cursor.cursorExceptionMessage;
- if ( message != null )
+ Object error = cursor.cursorException;
+ if ( error != null )
{
clearCursorError( cursor );
- throw new CursorException( message );
+ if ( usePreciseCursorErrorStackTraces )
+ {
+ throw (CursorExceptionWithPreciseStackTrace) error;
+ }
+ else
+ {
+ throw new CursorException( (String) error );
+ }
}
cursor = cursor.linkedCursor;
}
while ( cursor != null );
}
- protected void clearCursorError()
+ @Override
+ public void clearCursorError()
{
clearCursorError( this );
}
@@ -728,7 +743,7 @@ private void clearCursorError( MuninnPageCursor cursor )
{
while ( cursor != null )
{
- cursor.cursorExceptionMessage = null;
+ cursor.cursorException = null;
cursor = cursor.linkedCursor;
}
}
@@ -742,6 +757,14 @@ public void raiseOutOfBounds()
@Override
public void setCursorError( String message )
{
- this.cursorExceptionMessage = message;
+ Objects.requireNonNull( message );
+ if ( usePreciseCursorErrorStackTraces )
+ {
+ this.cursorException = new CursorExceptionWithPreciseStackTrace( message );
+ }
+ else
+ {
+ this.cursorException = message;
+ }
}
}
diff --git a/community/io/src/test/java/org/neo4j/adversaries/pagecache/AdversarialReadPageCursor.java b/community/io/src/test/java/org/neo4j/adversaries/pagecache/AdversarialReadPageCursor.java
index 9fd2d7469ecf8..986e687588ad9 100644
--- a/community/io/src/test/java/org/neo4j/adversaries/pagecache/AdversarialReadPageCursor.java
+++ b/community/io/src/test/java/org/neo4j/adversaries/pagecache/AdversarialReadPageCursor.java
@@ -371,20 +371,26 @@ public boolean shouldRetry() throws IOException
IllegalStateException.class );
if ( state.hasPreparedInconsistentRead() )
{
- delegate.shouldRetry();
- delegate.setOffset( 0 );
+ resetDelegate();
return true;
}
if ( state.hasInconsistentRead() )
{
- delegate.shouldRetry();
- delegate.setOffset( 0 );
+ resetDelegate();
return true;
}
boolean retry = delegate.shouldRetry();
return retry || (linkedCursor != null && linkedCursor.shouldRetry());
}
+ private void resetDelegate() throws IOException
+ {
+ delegate.shouldRetry();
+ delegate.setOffset( 0 );
+ delegate.checkAndClearBoundsFlag();
+ delegate.clearCursorError();
+ }
+
@Override
public int copyTo( int sourceOffset, PageCursor targetCursor, int targetOffset, int lengthInBytes )
{
@@ -424,6 +430,12 @@ public void setCursorError( String message )
delegate.setCursorError( message );
}
+ @Override
+ public void clearCursorError()
+ {
+ delegate.clearCursorError();
+ }
+
@Override
public PageCursor openLinkedCursor( long pageId )
{
diff --git a/community/io/src/test/java/org/neo4j/adversaries/pagecache/AdversarialWritePageCursor.java b/community/io/src/test/java/org/neo4j/adversaries/pagecache/AdversarialWritePageCursor.java
index 384dec70c9ab5..70b0d7e7d2840 100644
--- a/community/io/src/test/java/org/neo4j/adversaries/pagecache/AdversarialWritePageCursor.java
+++ b/community/io/src/test/java/org/neo4j/adversaries/pagecache/AdversarialWritePageCursor.java
@@ -291,6 +291,12 @@ public void setCursorError( String message )
delegate.setCursorError( message );
}
+ @Override
+ public void clearCursorError()
+ {
+ delegate.clearCursorError();
+ }
+
@Override
public PageCursor openLinkedCursor( long pageId )
{
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 e1f5d42156b6e..3d376af2ff5bd 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
@@ -2011,34 +2011,51 @@ public void tracerMustBeNotifiedAboutPinUnpinFaultAndEvictEventsWhenReading() th
getPageCache( fs, maxPages, pageCachePageSize, tracer );
long countedPages = 0;
+ long countedFaults = 0;
try ( PagedFile pagedFile = pageCache.map( file( "a" ), filePageSize );
PageCursor cursor = pagedFile.io( 0, PF_SHARED_READ_LOCK ) )
{
while ( cursor.next() )
{
- assertTrue( cursor.next( cursor.getCurrentPageId() ) );
+ countedPages++;
+ countedFaults++;
+ }
+
+ // Using next( pageId ) to the already-pinned page id does not count,
+ // so we only increment once for this section
+ countedPages++;
+ for ( int i = 0; i < 20; i++ )
+ {
+ assertTrue( cursor.next( 1 ) );
+ }
+
+ // But if we use next( pageId ) to a page that is different from the one already pinned,
+ // then it counts
+ for ( int i = 0; i < 20; i++ )
+ {
+ assertTrue( cursor.next( i ) );
countedPages++;
}
}
- assertThat( "wrong count of pins", tracer.pins(), is( countedPages * 2 ) );
- assertThat( "wrong count of unpins", tracer.unpins(), is( countedPages * 2 ) );
+ assertThat( "wrong count of pins", tracer.pins(), is( countedPages ) );
+ assertThat( "wrong count of unpins", tracer.unpins(), is( countedPages ) );
// We might be unlucky and fault in the second next call, on the page
// we brought up in the first next call. That's why we assert that we
// have observed *at least* the countedPages number of faults.
long faults = tracer.faults();
long bytesRead = tracer.bytesRead();
- assertThat( "wrong count of faults", faults, greaterThanOrEqualTo( countedPages ) );
+ assertThat( "wrong count of faults", faults, greaterThanOrEqualTo( countedFaults ) );
assertThat( "wrong number of bytes read",
- bytesRead, greaterThanOrEqualTo( countedPages * filePageSize ) );
+ bytesRead, greaterThanOrEqualTo( countedFaults * filePageSize ) );
// Every page we move forward can put the freelist behind so the cache
// wants to evict more pages. Plus, every page fault we do could also
// block and get a page directly transferred to it, and these kinds of
// evictions can count in addition to the evictions we do when the
// cache is behind on keeping the freelist full.
assertThat( "wrong count of evictions", tracer.evictions(),
- both( greaterThanOrEqualTo( countedPages - maxPages ) )
+ both( greaterThanOrEqualTo( countedFaults - maxPages ) )
.and( lessThanOrEqualTo( countedPages + faults ) ) );
}
@@ -2057,15 +2074,19 @@ public void tracerMustBeNotifiedAboutPinUnpinFaultFlushAndEvictionEventsWhenWrit
{
assertTrue( cursor.next() );
assertThat( cursor.getCurrentPageId(), is( i ) );
- assertTrue( cursor.next( i ) );
+ assertTrue( cursor.next( i ) ); // This does not count as a pin
assertThat( cursor.getCurrentPageId(), is( i ) );
writeRecords( cursor );
}
+
+ // This counts as a single pin
+ assertTrue( cursor.next( 0 ) );
+ assertTrue( cursor.next( 0 ) );
}
- assertThat( "wrong count of pins", tracer.pins(), is( pagesToGenerate * 2 ) );
- assertThat( "wrong count of unpins", tracer.unpins(), is( pagesToGenerate * 2 ) );
+ assertThat( "wrong count of pins", tracer.pins(), is( pagesToGenerate + 1 ) );
+ assertThat( "wrong count of unpins", tracer.unpins(), is( pagesToGenerate + 1 ) );
// We might be unlucky and fault in the second next call, on the page
// we brought up in the first next call. That's why we assert that we
@@ -4565,6 +4586,82 @@ public void openingLinkedCursorOnClosedCursorMustThrow() throws Exception
}
}
+ @Test
+ public void settingNullCursorErrorMustThrow() throws Exception
+ {
+ getPageCache( fs, maxPages, pageCachePageSize, PageCacheTracer.NULL );
+ try ( PagedFile pf = pageCache.map( file( "a" ), filePageSize );
+ PageCursor writer = pf.io( 0, PF_SHARED_WRITE_LOCK );
+ PageCursor reader = pf.io( 0, PF_SHARED_READ_LOCK ) )
+ {
+ assertTrue( writer.next() );
+ try
+ {
+ writer.setCursorError( null );
+ fail( "setting null cursor error on write cursor should have thrown" );
+ }
+ catch ( Exception e )
+ {
+ // all good
+ }
+
+ assertTrue( reader.next() );
+ try
+ {
+ reader.setCursorError( null );
+ fail( "setting null cursor error in read cursor should have thrown" );
+ }
+ catch ( Exception e )
+ {
+ // all good
+ }
+ }
+ }
+
+ @Test
+ public void clearCursorErrorMustUnsetErrorCondition() throws Exception
+ {
+ getPageCache( fs, maxPages, pageCachePageSize, PageCacheTracer.NULL );
+ try ( PagedFile pf = pageCache.map( file( "a" ), filePageSize );
+ PageCursor writer = pf.io( 0, PF_SHARED_WRITE_LOCK );
+ PageCursor reader = pf.io( 0, PF_SHARED_READ_LOCK ) )
+ {
+ assertTrue( writer.next() );
+ writer.setCursorError( "boo" );
+ writer.clearCursorError();
+ writer.checkAndClearCursorError();
+
+ assertTrue( reader.next() );
+ reader.setCursorError( "boo" );
+ reader.clearCursorError();
+ reader.checkAndClearCursorError();
+ }
+ }
+
+ @Test
+ public void clearCursorErrorMustUnsetErrorConditionOnLinkedCursor() throws Exception
+ {
+ getPageCache( fs, maxPages, pageCachePageSize, PageCacheTracer.NULL );
+ try ( PagedFile pf = pageCache.map( file( "a" ), filePageSize );
+ PageCursor writer = pf.io( 0, PF_SHARED_WRITE_LOCK );
+ PageCursor reader = pf.io( 0, PF_SHARED_READ_LOCK ) )
+ {
+ assertTrue( writer.next() );
+ PageCursor linkedWriter = writer.openLinkedCursor( 1 );
+ assertTrue( linkedWriter.next() );
+ linkedWriter.setCursorError( "boo" );
+ writer.clearCursorError();
+ writer.checkAndClearCursorError();
+
+ assertTrue( reader.next() );
+ PageCursor linkedReader = reader.openLinkedCursor( 1 );
+ assertTrue( linkedReader.next() );
+ linkedReader.setCursorError( "boo" );
+ reader.clearCursorError();
+ reader.checkAndClearCursorError();
+ }
+ }
+
@Test( timeout = SHORT_TIMEOUT_MILLIS )
public void readableByteChannelMustBeOpenUntilClosed() throws Exception
{
diff --git a/community/io/src/test/java/org/neo4j/io/pagecache/StubPageCursor.java b/community/io/src/test/java/org/neo4j/io/pagecache/StubPageCursor.java
index a28047a4bc0ad..f3a236dc6ed32 100644
--- a/community/io/src/test/java/org/neo4j/io/pagecache/StubPageCursor.java
+++ b/community/io/src/test/java/org/neo4j/io/pagecache/StubPageCursor.java
@@ -160,6 +160,12 @@ public void setCursorError( String message )
this.cursorErrorMessage = message;
}
+ @Override
+ public void clearCursorError()
+ {
+ this.cursorErrorMessage = null;
+ }
+
@Override
public PageCursor openLinkedCursor( long pageId )
{
diff --git a/community/io/src/test/java/org/neo4j/io/pagecache/harness/MunninPageCacheHarnessTest.java b/community/io/src/test/java/org/neo4j/io/pagecache/harness/MuninnPageCacheHarnessTest.java
similarity index 94%
rename from community/io/src/test/java/org/neo4j/io/pagecache/harness/MunninPageCacheHarnessTest.java
rename to community/io/src/test/java/org/neo4j/io/pagecache/harness/MuninnPageCacheHarnessTest.java
index 787a022b1f7ac..95343d2ed368d 100644
--- a/community/io/src/test/java/org/neo4j/io/pagecache/harness/MunninPageCacheHarnessTest.java
+++ b/community/io/src/test/java/org/neo4j/io/pagecache/harness/MuninnPageCacheHarnessTest.java
@@ -22,7 +22,7 @@
import org.neo4j.io.pagecache.impl.muninn.MuninnPageCache;
import org.neo4j.io.pagecache.impl.muninn.MuninnPageCacheFixture;
-public class MunninPageCacheHarnessTest extends PageCacheHarnessTest
+public class MuninnPageCacheHarnessTest extends PageCacheHarnessTest
{
@Override
protected Fixture createFixture()
diff --git a/community/io/src/test/java/org/neo4j/io/pagecache/harness/MuninnPageCacheHarnessWithRealFileSystemIT.java b/community/io/src/test/java/org/neo4j/io/pagecache/harness/MuninnPageCacheHarnessWithRealFileSystemIT.java
index 65bb129efe280..f8cd116884eee 100644
--- a/community/io/src/test/java/org/neo4j/io/pagecache/harness/MuninnPageCacheHarnessWithRealFileSystemIT.java
+++ b/community/io/src/test/java/org/neo4j/io/pagecache/harness/MuninnPageCacheHarnessWithRealFileSystemIT.java
@@ -25,7 +25,7 @@
import org.neo4j.io.pagecache.impl.muninn.MuninnPageCache;
import org.neo4j.test.TargetDirectory;
-public class MuninnPageCacheHarnessWithRealFileSystemIT extends MunninPageCacheHarnessTest
+public class MuninnPageCacheHarnessWithRealFileSystemIT extends MuninnPageCacheHarnessTest
{
@Rule
public TargetDirectory.TestDirectory directory = TargetDirectory.testDirForTest( getClass() );
diff --git a/community/io/src/test/java/org/neo4j/io/pagecache/impl/CompositePageCursorTest.java b/community/io/src/test/java/org/neo4j/io/pagecache/impl/CompositePageCursorTest.java
index 4572d8e13348a..629696bf392bb 100644
--- a/community/io/src/test/java/org/neo4j/io/pagecache/impl/CompositePageCursorTest.java
+++ b/community/io/src/test/java/org/neo4j/io/pagecache/impl/CompositePageCursorTest.java
@@ -1245,4 +1245,18 @@ public void checkAndClearCursorErrorWillOnlyCheckFirstCursorIfBothHaveErrorsSet(
assertThat( e.getMessage(), is( "second boo" ) );
}
}
+
+ @Test
+ public void clearCursorErrorMustClearBothCursors() throws Exception
+ {
+ PageCursor cursor = CompositePageCursor.compose( first, PAGE_SIZE, second, PAGE_SIZE );
+ first.setCursorError( "first boo" );
+ second.setCursorError( "second boo" );
+ cursor.clearCursorError();
+
+ // Now these must not throw
+ first.checkAndClearCursorError();
+ second.checkAndClearCursorError();
+ cursor.checkAndClearCursorError();
+ }
}
diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/CommonAbstractStore.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/CommonAbstractStore.java
index 46394f678f969..b930d2b3dc6fe 100644
--- a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/CommonAbstractStore.java
+++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/CommonAbstractStore.java
@@ -52,6 +52,7 @@
import static org.neo4j.io.pagecache.PagedFile.PF_SHARED_WRITE_LOCK;
import static org.neo4j.kernel.impl.store.record.Record.NULL_REFERENCE;
import static org.neo4j.kernel.impl.store.record.RecordLoad.CHECK;
+import static org.neo4j.kernel.impl.store.record.RecordLoad.NORMAL;
/**
* Contains common implementation of {@link RecordStore}.
@@ -220,33 +221,6 @@ protected void initialiseNewStoreFile( PagedFile file ) throws IOException
idGeneratorFactory.create( idFileName, getNumberOfReservedLowIds(), true );
}
- protected void checkForOutOfBounds( PageCursor pageCursor, long recordId )
- {
- if ( pageCursor.checkAndClearBoundsFlag() )
- {
- throwOutOfBoundsException( recordId );
- }
- }
-
- private void throwOutOfBoundsException( long recordId )
- {
- RECORD record = newRecord();
- record.setId( recordId );
- long pageId = pageIdForRecord( recordId );
- int offset = offsetForId( recordId );
- throw new UnderlyingStorageException( buildOutOfBoundsExceptionMessage(
- record, pageId, offset, recordSize, storeFile.pageSize(), storageFileName.getAbsolutePath() ) );
- }
-
- protected static String buildOutOfBoundsExceptionMessage( AbstractBaseRecord record, long pageId, int offset,
- int recordSize, int pageSize, String filename )
- {
- return "Access to record " + record + " went out of bounds of the page. The record size is " +
- recordSize + " bytes, and the access was at offset " + offset + " bytes into page " +
- pageId + ", and the pages have a capacity of " + pageSize + " bytes. " +
- "The mapped store file in question is " + filename;
- }
-
protected void createHeaderRecord( PageCursor cursor ) throws IOException
{
int offset = cursor.getOffset();
@@ -332,17 +306,17 @@ public byte[] getRawRecordData( long id ) throws IOException
byte[] data = new byte[recordSize];
long pageId = pageIdForRecord( id );
int offset = offsetForId( id );
- try ( PageCursor pageCursor = storeFile.io( pageId, PagedFile.PF_SHARED_READ_LOCK ) )
+ try ( PageCursor cursor = storeFile.io( pageId, PagedFile.PF_SHARED_READ_LOCK ) )
{
- if ( pageCursor.next() )
+ if ( cursor.next() )
{
do
{
- pageCursor.setOffset( offset );
- pageCursor.getBytes( data );
+ cursor.setOffset( offset );
+ cursor.getBytes( data );
}
- while ( pageCursor.shouldRetry() );
- checkForOutOfBounds( pageCursor, id );
+ while ( cursor.shouldRetry() );
+ checkForDecodingErrors( cursor, id, CHECK );
}
}
return data;
@@ -403,7 +377,7 @@ public boolean isInUse( long id )
recordIsInUse = isInUse( cursor );
}
while ( cursor.shouldRetry() );
- checkForOutOfBounds( cursor, id );
+ checkForDecodingErrors( cursor, id, NORMAL );
}
return recordIsInUse;
}
@@ -1063,18 +1037,13 @@ private void readIntoRecord( long id, RECORD record, RecordLoad mode, long pageI
if ( cursor.next( pageId ) )
{
// There is a page in the store that covers this record, go read it
- String error;
do
{
prepareForReading( cursor, offset, record );
- error = recordFormat.read( record, cursor, mode, recordSize, storeFile );
+ recordFormat.read( record, cursor, mode, recordSize, storeFile );
}
while ( cursor.shouldRetry() );
- checkForOutOfBounds( cursor, id );
- if ( error != null )
- {
- mode.report( error );
- }
+ checkForDecodingErrors( cursor, id, mode );
verifyAfterReading( record, mode );
}
else
@@ -1101,7 +1070,7 @@ public void updateRecord( RECORD record )
recordFormat.write( record, cursor, recordSize, storeFile );
}
while ( cursor.shouldRetry() );
- checkForOutOfBounds( cursor, id ); // We don't free ids if something weird goes wrong
+ checkForDecodingErrors( cursor, id, NORMAL ); // We don't free ids if something weird goes wrong
if ( !record.inUse() )
{
freeId( id );
@@ -1261,7 +1230,35 @@ protected void verifyAfterNotRead( RECORD record, RecordLoad mode )
}
- protected void verifyAfterReading( RECORD record, RecordLoad mode )
+ protected final void checkForDecodingErrors( PageCursor cursor, long recordId, RecordLoad mode )
+ {
+ if ( mode.checkForOutOfBounds( cursor ) )
+ {
+ throwOutOfBoundsException( recordId );
+ }
+ mode.clearOrThrowCursorError( cursor );
+ }
+
+ private void throwOutOfBoundsException( long recordId )
+ {
+ RECORD record = newRecord();
+ record.setId( recordId );
+ long pageId = pageIdForRecord( recordId );
+ int offset = offsetForId( recordId );
+ throw new UnderlyingStorageException( buildOutOfBoundsExceptionMessage(
+ record, pageId, offset, recordSize, storeFile.pageSize(), storageFileName.getAbsolutePath() ) );
+ }
+
+ protected static String buildOutOfBoundsExceptionMessage( AbstractBaseRecord record, long pageId, int offset,
+ int recordSize, int pageSize, String filename )
+ {
+ return "Access to record " + record + " went out of bounds of the page. The record size is " +
+ recordSize + " bytes, and the access was at offset " + offset + " bytes into page " +
+ pageId + ", and the pages have a capacity of " + pageSize + " bytes. " +
+ "The mapped store file in question is " + filename;
+ }
+
+ protected final void verifyAfterReading( RECORD record, RecordLoad mode )
{
if ( !mode.verify( record ) )
{
@@ -1269,7 +1266,7 @@ protected void verifyAfterReading( RECORD record, RecordLoad mode )
}
}
- protected void prepareForReading( PageCursor cursor, int offset, RECORD record )
+ protected final void prepareForReading( PageCursor cursor, int offset, RECORD record )
{
// Mark this record as unused. This to simplify implementations of readRecord.
// readRecord can behave differently depending on RecordLoad argument and so it may be that
diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/MetaDataStore.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/MetaDataStore.java
index 5c0f99e333819..4c402d30b85c5 100644
--- a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/MetaDataStore.java
+++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/MetaDataStore.java
@@ -51,6 +51,7 @@
import static org.neo4j.io.pagecache.PagedFile.PF_SHARED_READ_LOCK;
import static org.neo4j.io.pagecache.PagedFile.PF_SHARED_WRITE_LOCK;
import static org.neo4j.kernel.impl.store.format.standard.MetaDataRecordFormat.RECORD_SIZE;
+import static org.neo4j.kernel.impl.store.record.RecordLoad.NORMAL;
public class MetaDataStore extends CommonAbstractStore
implements TransactionIdStore, LogVersionRepository
@@ -432,7 +433,7 @@ private void incrementVersion( PageCursor cursor ) throws IOException
cursor.putLong( offset, value );
}
while ( cursor.shouldRetry() );
- checkForOutOfBounds( cursor, Position.LOG_VERSION.id );
+ checkForDecodingErrors( cursor, Position.LOG_VERSION.id, NORMAL );
versionField = value;
}
@@ -521,7 +522,7 @@ long getRecordValue( PageCursor cursor, Position position )
try
{
record.setId( position.id );
- recordFormat.read( record, cursor, RecordLoad.NORMAL, RECORD_SIZE, storeFile );
+ recordFormat.read( record, cursor, NORMAL, RECORD_SIZE, storeFile );
return record.getValue();
}
catch ( IOException e )
@@ -580,7 +581,7 @@ private void setRecord( PageCursor cursor, Position position, long value ) throw
cursor.putLong( value );
}
while ( cursor.shouldRetry() );
- checkForOutOfBounds( cursor, position.id );
+ checkForDecodingErrors( cursor, position.id, NORMAL );
}
public NeoStoreRecord graphPropertyRecord()
@@ -730,7 +731,7 @@ public TransactionId getUpgradeTransaction()
upgradeTxChecksumField = getRecordValue( cursor, Position.UPGRADE_TRANSACTION_CHECKSUM );
}
while ( cursor.shouldRetry() );
- checkForOutOfBounds( cursor, Position.UPGRADE_TRANSACTION_ID.id );
+ checkForDecodingErrors( cursor, Position.UPGRADE_TRANSACTION_ID.id, NORMAL );
}
catch ( IOException e )
{
diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/PropertyStore.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/PropertyStore.java
index 7133fe370a29c..f8b35f6d18e74 100644
--- a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/PropertyStore.java
+++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/PropertyStore.java
@@ -205,14 +205,13 @@ public Object getValue( PropertyBlock propertyBlock )
public static void allocateStringRecords( Collection target, byte[] chars,
DynamicRecordAllocator allocator )
{
- AbstractDynamicStore.allocateRecordsFromBytes( target, chars,
- Iterators.emptyIterator(), allocator );
+ AbstractDynamicStore.allocateRecordsFromBytes( target, chars, Iterators.emptyIterator(), allocator );
}
public static void allocateArrayRecords( Collection target, Object array,
DynamicRecordAllocator allocator )
{
- DynamicArrayStore.allocateRecords( target, array, Iterators.emptyIterator(), allocator );
+ DynamicArrayStore.allocateRecords( target, array, Iterators.emptyIterator(), allocator );
}
public void encodeValue( PropertyBlock block, int keyId, Object value )
@@ -394,11 +393,4 @@ public PropertyRecord newRecord()
{
return new PropertyRecord( -1 );
}
-
- @Override
- protected void verifyAfterReading( PropertyRecord record, RecordLoad mode )
- {
- super.verifyAfterReading( record, mode );
- record.verifyRecordIsWellFormed();
- }
}
diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/RecordFormat.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/RecordFormat.java
index a41e26a90fead..61feab5db9432 100644
--- a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/RecordFormat.java
+++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/RecordFormat.java
@@ -87,12 +87,9 @@ public interface RecordFormat
* @param recordSize size of records of this format. This is passed in like this since not all formats
* know the record size in advance, but may be read from store header when opening the store.
* @param storeFile {@link PagedFile} to get additional {@link PageCursor} from if needed.
- * @return An error message describing if something went wrong when reading the record, or {@code null} if
- * the record was read successfully. If non-null, the returned message can be fed to
- * {@link RecordLoad#report(String)}.
* @throws IOException on error reading.
*/
- String read( RECORD record, PageCursor cursor, RecordLoad mode, int recordSize, PagedFile storeFile )
+ void read( RECORD record, PageCursor cursor, RecordLoad mode, int recordSize, PagedFile storeFile )
throws IOException;
/**
diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/DynamicRecordFormat.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/DynamicRecordFormat.java
index c128f9e91adda..cc2bd80de3a56 100644
--- a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/DynamicRecordFormat.java
+++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/DynamicRecordFormat.java
@@ -48,7 +48,7 @@ public DynamicRecord newRecord()
}
@Override
- public String read( DynamicRecord record, PageCursor cursor, RecordLoad mode, int recordSize, PagedFile storeFile )
+ public void read( DynamicRecord record, PageCursor cursor, RecordLoad mode, int recordSize, PagedFile storeFile )
{
/*
* First 4b
@@ -69,7 +69,8 @@ public String read( DynamicRecord record, PageCursor cursor, RecordLoad mode, in
{
// We must have performed an inconsistent read,
// because this many bytes cannot possibly fit in a record!
- return payloadTooBigErrorMessage( record, recordSize, nrOfBytes );
+ cursor.setCursorError( payloadTooBigErrorMessage( record, recordSize, nrOfBytes ) );
+ return;
}
/*
@@ -83,13 +84,12 @@ public String read( DynamicRecord record, PageCursor cursor, RecordLoad mode, in
if ( longNextBlock != Record.NO_NEXT_BLOCK.intValue()
&& nrOfBytes < dataSize || nrOfBytes > dataSize )
{
- return format( "Next block set[%d] current block illegal size[%d/%d]",
- record.getNextBlock(), record.getLength(), dataSize );
+ cursor.setCursorError( illegalBlockSizeMessage( record, dataSize ) );
+ return;
}
readData( record, cursor );
}
- return null;
}
public static String payloadTooBigErrorMessage( DynamicRecord record, int recordSize, int nrOfBytes )
@@ -99,6 +99,12 @@ public static String payloadTooBigErrorMessage( DynamicRecord record, int record
record.getId(), nrOfBytes, recordSize );
}
+ private String illegalBlockSizeMessage( DynamicRecord record, int dataSize )
+ {
+ return format( "Next block set[%d] current block illegal size[%d/%d]",
+ record.getNextBlock(), record.getLength(), dataSize );
+ }
+
public static void readData( DynamicRecord record, PageCursor cursor )
{
int len = record.getLength();
diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/MetaDataRecordFormat.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/MetaDataRecordFormat.java
index 16203bb24c2f5..114083c294741 100644
--- a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/MetaDataRecordFormat.java
+++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/MetaDataRecordFormat.java
@@ -47,7 +47,7 @@ public MetaDataRecord newRecord()
}
@Override
- public String read( MetaDataRecord record, PageCursor cursor, RecordLoad mode, int recordSize,
+ public void read( MetaDataRecord record, PageCursor cursor, RecordLoad mode, int recordSize,
PagedFile storeFile ) throws IOException
{
int id = record.getIntId();
@@ -55,7 +55,7 @@ public String read( MetaDataRecord record, PageCursor cursor, RecordLoad mode, i
if ( id >= values.length )
{
record.initialize( false, FIELD_NOT_PRESENT );
- return null;
+ return;
}
Position position = values[id];
@@ -64,7 +64,6 @@ public String read( MetaDataRecord record, PageCursor cursor, RecordLoad mode, i
boolean inUse = cursor.getByte() == Record.IN_USE.byteValue();
long value = inUse ? cursor.getLong() : FIELD_NOT_PRESENT;
record.initialize( inUse, value );
- return null;
}
@Override
diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/NoRecordFormat.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/NoRecordFormat.java
index 9f0c3098c8856..22db3a67ae154 100644
--- a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/NoRecordFormat.java
+++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/NoRecordFormat.java
@@ -57,11 +57,10 @@ public boolean isInUse( PageCursor cursor )
}
@Override
- public String read( RECORD record, PageCursor cursor, RecordLoad mode, int recordSize, PagedFile storeFile )
+ public void read( RECORD record, PageCursor cursor, RecordLoad mode, int recordSize, PagedFile storeFile )
throws IOException
{
record.clear();
- return null;
}
@Override
diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/NodeRecordFormat.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/NodeRecordFormat.java
index b9ac8995cd45a..f82a68bd1eed1 100644
--- a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/NodeRecordFormat.java
+++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/NodeRecordFormat.java
@@ -45,7 +45,7 @@ public NodeRecord newRecord()
return new NodeRecord( -1 );
}
- public String read( NodeRecord record, PageCursor cursor, RecordLoad mode, int recordSize, PagedFile storeFile )
+ public void read( NodeRecord record, PageCursor cursor, RecordLoad mode, int recordSize, PagedFile storeFile )
throws IOException
{
byte headerByte = cursor.getByte();
@@ -69,7 +69,6 @@ public String read( NodeRecord record, PageCursor cursor, RecordLoad mode, int r
BaseRecordFormat.longFromIntAndMod( nextProp, propModifier ), dense,
BaseRecordFormat.longFromIntAndMod( nextRel, relModifier ), labels );
}
- return null;
}
@Override
diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/NodeRecordFormatV2_0.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/NodeRecordFormatV2_0.java
index 51d319f60d64a..b113d20b77b51 100644
--- a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/NodeRecordFormatV2_0.java
+++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/NodeRecordFormatV2_0.java
@@ -40,7 +40,7 @@ public NodeRecord newRecord()
return new NodeRecord( -1 );
}
- public String read( NodeRecord record, PageCursor cursor, RecordLoad mode, int recordSize, PagedFile storeFile )
+ public void read( NodeRecord record, PageCursor cursor, RecordLoad mode, int recordSize, PagedFile storeFile )
throws IOException
{
long headerByte = cursor.getByte();
@@ -66,7 +66,6 @@ public String read( NodeRecord record, PageCursor cursor, RecordLoad mode, int r
longFromIntAndMod( nextRel, relModifier ),
labels );
}
- return null;
}
@Override
diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/PropertyRecordFormat.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/PropertyRecordFormat.java
index 23df1df95bc69..ec3eb6fdb981e 100644
--- a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/PropertyRecordFormat.java
+++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/PropertyRecordFormat.java
@@ -51,7 +51,7 @@ public PropertyRecord newRecord()
}
@Override
- public String read( PropertyRecord record, PageCursor cursor, RecordLoad mode, int recordSize, PagedFile storeFile )
+ public void read( PropertyRecord record, PageCursor cursor, RecordLoad mode, int recordSize, PagedFile storeFile )
{
int offsetAtBeginning = cursor.getOffset();
@@ -81,19 +81,20 @@ public String read( PropertyRecord record, PageCursor cursor, RecordLoad mode, i
int numberOfBlocksUsed = type.calculateNumberOfBlocksUsed( block );
if ( numberOfBlocksUsed == PropertyType.BLOCKS_USED_FOR_BAD_TYPE_OR_ENCODING )
{
- return "Invalid type or encoding of property block";
+ cursor.setCursorError( "Invalid type or encoding of property block: " + block + " (type = " + type + ")" );
+ return;
}
int additionalBlocks = numberOfBlocksUsed - 1;
if ( additionalBlocks * 8 > RECORD_SIZE - (cursor.getOffset() - offsetAtBeginning ) )
{
- return "PropertyRecord claims to have more property blocks than can fit in a record";
+ cursor.setCursorError( "PropertyRecord claims to have more property blocks than can fit in a record" );
+ return;
}
while ( additionalBlocks --> 0 )
{
record.addLoadedBlock( cursor.getLong() );
}
}
- return null;
}
@Override
diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/RelationshipGroupRecordFormat.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/RelationshipGroupRecordFormat.java
index 6c9f8125a02b8..c5cf8676b1d52 100644
--- a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/RelationshipGroupRecordFormat.java
+++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/RelationshipGroupRecordFormat.java
@@ -47,7 +47,7 @@ public RelationshipGroupRecordFormat()
}
@Override
- public String read( RelationshipGroupRecord record, PageCursor cursor, RecordLoad mode, int recordSize,
+ public void read( RelationshipGroupRecord record, PageCursor cursor, RecordLoad mode, int recordSize,
PagedFile storeFile ) throws IOException
{
// [ , x] in use
@@ -81,7 +81,6 @@ public String read( RelationshipGroupRecord record, PageCursor cursor, RecordLoa
owningNode,
BaseRecordFormat.longFromIntAndMod( nextLowBits, nextMod ) );
}
- return null;
}
@Override
diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/RelationshipRecordFormat.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/RelationshipRecordFormat.java
index a7dd38f4546f7..47adc4af39e5d 100644
--- a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/RelationshipRecordFormat.java
+++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/RelationshipRecordFormat.java
@@ -48,7 +48,7 @@ public RelationshipRecord newRecord()
return new RelationshipRecord( -1 );
}
- public String read( RelationshipRecord record, PageCursor cursor, RecordLoad mode, int recordSize,
+ public void read( RelationshipRecord record, PageCursor cursor, RecordLoad mode, int recordSize,
PagedFile storeFile ) throws IOException
{
byte headerByte = cursor.getByte();
@@ -103,7 +103,6 @@ public String read( RelationshipRecord record, PageCursor cursor, RecordLoad mod
(extraByte & 0x1) != 0,
(extraByte & 0x2) != 0 );
}
- return null;
}
@Override
diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/RelationshipRecordFormatV2_0.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/RelationshipRecordFormatV2_0.java
index 66cb75a4dc6db..a75e6464c7695 100644
--- a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/RelationshipRecordFormatV2_0.java
+++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/RelationshipRecordFormatV2_0.java
@@ -41,7 +41,7 @@ public RelationshipRecord newRecord()
}
@Override
- public String read( RelationshipRecord record, PageCursor cursor, RecordLoad mode, int recordSize,
+ public void read( RelationshipRecord record, PageCursor cursor, RecordLoad mode, int recordSize,
PagedFile storeFile ) throws IOException
{
long headerByte = cursor.getByte();
@@ -92,7 +92,6 @@ public String read( RelationshipRecord record, PageCursor cursor, RecordLoad mod
longFromIntAndMod( secondNextRel, secondNextRelMod ),
false, false );
}
- return null;
}
@Override
diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/TokenRecordFormat.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/TokenRecordFormat.java
index c43e0752fb2ac..54c0e144dbe6e 100644
--- a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/TokenRecordFormat.java
+++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/standard/TokenRecordFormat.java
@@ -36,7 +36,7 @@ protected TokenRecordFormat( int recordSize, int idBits )
}
@Override
- public String read( RECORD record, PageCursor cursor, RecordLoad mode, int recordSize, PagedFile storeFile )
+ public void read( RECORD record, PageCursor cursor, RecordLoad mode, int recordSize, PagedFile storeFile )
{
byte inUseByte = cursor.getByte();
boolean inUse = isInUse( inUseByte );
@@ -45,7 +45,6 @@ public String read( RECORD record, PageCursor cursor, RecordLoad mode, int recor
{
readRecordData( cursor, record, inUse );
}
- return null;
}
protected void readRecordData( PageCursor cursor, RECORD record, boolean inUse )
diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/record/PropertyRecord.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/record/PropertyRecord.java
index 68f661b53682b..2a40fc95cd4fd 100644
--- a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/record/PropertyRecord.java
+++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/record/PropertyRecord.java
@@ -26,7 +26,6 @@
import java.util.List;
import java.util.NoSuchElementException;
-import org.neo4j.kernel.impl.store.InvalidRecordException;
import org.neo4j.kernel.impl.store.PropertyType;
import static org.neo4j.kernel.impl.store.record.Record.NO_NEXT_PROPERTY;
@@ -65,7 +64,6 @@ public class PropertyRecord extends AbstractBaseRecord implements Iterable deletedRecords;
- private String malformedMessage;
// state for the Iterator aspect of this class.
private int blockRecordsIteratorCursor;
@@ -258,19 +256,6 @@ private void ensureBlocksLoaded()
}
}
- public void verifyRecordIsWellFormed()
- {
- if ( malformedMessage != null )
- {
- throw new InvalidRecordException( malformedMessage );
- }
- }
-
- public void setMalformedMessage( String message )
- {
- malformedMessage = message;
- }
-
public void setPropertyBlock( PropertyBlock block )
{
removePropertyBlock( block.getKeyIndexId() );
diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/record/RecordLoad.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/record/RecordLoad.java
index b8f44a327c381..5287af091291f 100644
--- a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/record/RecordLoad.java
+++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/record/RecordLoad.java
@@ -19,6 +19,7 @@
*/
package org.neo4j.kernel.impl.store.record;
+import org.neo4j.io.pagecache.CursorException;
import org.neo4j.io.pagecache.PageCursor;
import org.neo4j.kernel.impl.store.InvalidRecordException;
@@ -39,77 +40,66 @@
*/
public enum RecordLoad
{
- NORMAL
- {
- @Override
- public boolean shouldLoad( boolean inUse )
- {
- return inUse;
- }
+ NORMAL, CHECK, FORCE;
- @Override
- public boolean verify( AbstractBaseRecord record )
- {
- if ( !record.inUse() )
- {
- throw new InvalidRecordException( record + " not in use" );
- }
- return true;
- }
- },
- CHECK
+ /**
+ * Checks whether or not a record should be fully loaded from {@link PageCursor}, based on inUse status.
+ */
+ public final boolean shouldLoad( boolean inUse )
{
- @Override
- public boolean shouldLoad( boolean inUse )
- {
- return inUse;
- }
-
- @Override
- public boolean verify( AbstractBaseRecord record )
- {
- return record.inUse();
- }
+ // FORCE mode always return true so that record data will always be loaded, even if not in use.
+ // The other modes only loads records that are in use.
+ return this == FORCE | inUse;
+ }
- @Override
- public void report( String message )
- {
- }
- },
- FORCE
+ /**
+ * Verifies that a record's in use status is in line with the mode, might throw {@link InvalidRecordException}.
+ */
+ public final boolean verify( AbstractBaseRecord record )
{
- @Override
- public boolean shouldLoad( boolean inUse )
+ boolean inUse = record.inUse();
+ if ( this == NORMAL & !inUse )
{
- // Always return true so that record data will always be loaded, even if not in use.
- return true;
+ throw new InvalidRecordException( record + " not in use" );
}
+ return this == FORCE | inUse;
+ }
- @Override
- public boolean verify( AbstractBaseRecord record )
+ /**
+ * Depending on the mode, this will - if a cursor error has been raised on the given {@link PageCursor} - either
+ * throw an {@link InvalidRecordException} with the underlying {@link CursorException}, or clear the error condition
+ * on the cursor.
+ * @param cursor The {@link PageCursor} to be checked for errors.
+ */
+ public final void clearOrThrowCursorError( PageCursor cursor )
+ {
+ if ( this == NORMAL )
{
- return true;
+ try
+ {
+ cursor.checkAndClearCursorError();
+ }
+ catch ( CursorException e )
+ {
+ throw new InvalidRecordException( e );
+ }
}
-
- @Override
- public void report( String message )
+ else
{
- // Don't report
+ // The CHECK and FORCE modes do not bother with reporting decoding errors...
+ // ... but they must still clear them, since the page cursor may be reused to read other records
+ cursor.clearCursorError();
}
- };
-
- /**
- * Checks whether or not a record should be fully loaded from {@link PageCursor}, based on inUse status.
- */
- public abstract boolean shouldLoad( boolean inUse );
+ }
/**
- * Verifies that a record's in use status is in line with the mode, might throw {@link InvalidRecordException}.
+ * Checks the given {@link PageCursor} to see if its out-of-bounds flag has been raised, and returns {@code true} if
+ * that is the case and and out-of-bounds condition should be reported up the stack.
+ * @param cursor The {@link PageCursor} to check the bounds flag for.
+ * @return {@code true} if an out-of-bounds condition should be reported up the stack, {@code false} otherwise.
*/
- public abstract boolean verify( AbstractBaseRecord record );
-
- public void report( String message )
+ public boolean checkForOutOfBounds( PageCursor cursor )
{
- throw new InvalidRecordException( message );
+ return cursor.checkAndClearBoundsFlag() & this == NORMAL;
}
}
diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/store/CommonAbstractStoreBehaviourTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/store/CommonAbstractStoreBehaviourTest.java
index a838e0b12a26e..aad21bff59478 100644
--- a/community/kernel/src/test/java/org/neo4j/kernel/impl/store/CommonAbstractStoreBehaviourTest.java
+++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/store/CommonAbstractStoreBehaviourTest.java
@@ -46,8 +46,11 @@
import org.neo4j.test.EphemeralFileSystemRule;
import org.neo4j.test.PageCacheRule;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.neo4j.helpers.collection.MapUtil.stringMap;
+import static org.neo4j.kernel.impl.store.record.RecordLoad.CHECK;
+import static org.neo4j.kernel.impl.store.record.RecordLoad.FORCE;
import static org.neo4j.kernel.impl.store.record.RecordLoad.NORMAL;
/**
@@ -71,6 +74,7 @@ public class CommonAbstractStoreBehaviourTest
private final Queue nextPageId = new ConcurrentLinkedQueue<>();
private final Queue nextPageOffset = new ConcurrentLinkedQueue<>();
+ private int cursorErrorOnRecord;
private int intsPerRecord = 1;
private MyStore store;
@@ -88,12 +92,12 @@ public void tearDown()
nextPageId.clear();
}
- private void assertThrows( ThrowingAction action ) throws Exception
+ private void assertThrowsUnderlyingStorageException( ThrowingAction action ) throws Exception
{
try
{
action.apply();
- fail( "expected an exception" );
+ fail( "expected an UnderlyingStorageException exception" );
}
catch ( UnderlyingStorageException exception )
{
@@ -101,11 +105,41 @@ private void assertThrows( ThrowingAction action ) throws Exception
}
}
- private void verifyExceptionOnAccess( ThrowingAction access ) throws Exception
+ private void assertThrowsInvalidRecordException( ThrowingAction action ) throws Exception
+ {
+ try
+ {
+ action.apply();
+ fail( "expected an InvalidRecordException exception" );
+ }
+ catch ( InvalidRecordException exception )
+ {
+ // Good!
+ }
+ }
+
+ private void verifyExceptionOnOutOfBoundsAccess( ThrowingAction access ) throws Exception
+ {
+ prepareStoreForOutOfBoundsAccess();
+ assertThrowsUnderlyingStorageException( access );
+ }
+
+ private void prepareStoreForOutOfBoundsAccess()
{
createStore();
nextPageOffset.add( 8190 );
- assertThrows( access );
+ }
+
+ private void verifyExceptionOnCursorError( ThrowingAction access ) throws Exception
+ {
+ prepareStoreForCursorError();
+ assertThrowsInvalidRecordException( access );
+ }
+
+ private void prepareStoreForCursorError()
+ {
+ createStore();
+ cursorErrorOnRecord = 5;
}
private void createStore()
@@ -120,7 +154,7 @@ public void writingOfHeaderRecordDuringInitialiseNewStoreFileMustThrowOnPageOver
// 16-byte header will overflow an 8-byte page size
Config config = CONFIG.with( stringMap( GraphDatabaseSettings.mapped_memory_page_size.name(), "8" ) );
MyStore store = new MyStore( config, pageCacheRule.getPageCache( fs.get(), config ) );
- assertThrows( () -> store.initialise( true ) );
+ assertThrowsUnderlyingStorageException( () -> store.initialise( true ) );
}
@Test
@@ -132,37 +166,78 @@ public void extractHeaderRecordDuringLoadStorageMustThrowOnPageOverflow() throws
config = CONFIG.with( stringMap( GraphDatabaseSettings.mapped_memory_page_size.name(), "8" ) );
MyStore second = new MyStore( config, pageCacheRule.getPageCache( fs.get(), config ) );
- assertThrows( () -> second.initialise( false ) );
+ assertThrowsUnderlyingStorageException( () -> second.initialise( false ) );
}
@Test
- public void getRawRecordDataMustThrowOnPageOverflow() throws Exception
+ public void getRawRecordDataMustNotThrowOnPageOverflow() throws Exception
{
- verifyExceptionOnAccess( () -> store.getRawRecordData( 5 ) );
+ prepareStoreForOutOfBoundsAccess();
+ store.getRawRecordData( 5 );
}
@Test
public void isInUseMustThrowOnPageOverflow() throws Exception
{
- verifyExceptionOnAccess( () -> store.isInUse( 5 ) );
+ verifyExceptionOnOutOfBoundsAccess( () -> store.isInUse( 5 ) );
+ }
+
+ @Test
+ public void isInUseMustThrowOnCursorError() throws Exception
+ {
+ verifyExceptionOnCursorError( () -> store.isInUse( 5 ) );
}
@Test
public void getRecordMustThrowOnPageOverflow() throws Exception
{
- verifyExceptionOnAccess( () -> store.getRecord( 5, new IntRecord( 5 ), NORMAL ) );
+ verifyExceptionOnOutOfBoundsAccess( () -> store.getRecord( 5, new IntRecord( 5 ), NORMAL ) );
+ }
+
+ @Test
+ public void getRecordMustNotThrowOnPageOverflowWithCheckLoadMode() throws Exception
+ {
+ prepareStoreForOutOfBoundsAccess();
+ store.getRecord( 5, new IntRecord( 5 ), CHECK );
+ }
+
+ @Test
+ public void getRecordMustNotThrowOnPageOverflowWithForceLoadMode() throws Exception
+ {
+ prepareStoreForOutOfBoundsAccess();
+ store.getRecord( 5, new IntRecord( 5 ), FORCE );
}
@Test
public void updateRecordMustThrowOnPageOverflow() throws Exception
{
- verifyExceptionOnAccess( () -> store.updateRecord( new IntRecord( 5 ) ) );
+ verifyExceptionOnOutOfBoundsAccess( () -> store.updateRecord( new IntRecord( 5 ) ) );
+ }
+
+ @Test
+ public void getRecordMustThrowOnCursorError() throws Exception
+ {
+ verifyExceptionOnCursorError( () -> store.getRecord( 5, new IntRecord( 5 ), NORMAL ) );
+ }
+
+ @Test
+ public void getRecordMustNotThrowOnCursorErrorWithCheckLoadMode() throws Exception
+ {
+ prepareStoreForCursorError();
+ store.getRecord( 5, new IntRecord( 5 ), CHECK );
+ }
+
+ @Test
+ public void getRecordMustNotThrowOnCursorErrorWithForceLoadMode() throws Exception
+ {
+ prepareStoreForCursorError();
+ store.getRecord( 5, new IntRecord( 5 ), FORCE );
}
@Test
public void recordCursorNextMustThrowOnPageOverflow() throws Exception
{
- verifyExceptionOnAccess( () -> {
+ verifyExceptionOnOutOfBoundsAccess( () -> {
try ( RecordCursor cursor = store.newRecordCursor( new IntRecord( 0 ) ).acquire( 5, NORMAL ) )
{
cursor.next();
@@ -170,6 +245,18 @@ public void recordCursorNextMustThrowOnPageOverflow() throws Exception
} );
}
+ @Test
+ public void pageCursorErrorsMustNotLingerInRecordCursor() throws Exception
+ {
+ createStore();
+ RecordCursor cursor = store.newRecordCursor( new IntRecord( 1 ) ).acquire( 1, FORCE );
+ cursorErrorOnRecord = 1;
+ // This will encounter a decoding error, which is ignored because FORCE
+ assertTrue( cursor.next() );
+ // Then this should not fail because of the previous decoding error, even though we stay on the same page
+ assertTrue( cursor.next( 2, new IntRecord( 2 ), NORMAL ) );
+ }
+
@Test
public void rebuildIdGeneratorSlowMustThrowOnPageOverflow() throws Exception
{
@@ -181,7 +268,7 @@ public void rebuildIdGeneratorSlowMustThrowOnPageOverflow() throws Exception
record.value = 0xCAFEBABE;
store.updateRecord( record );
intsPerRecord = 8192;
- assertThrows( () -> store.makeStoreOk() );
+ assertThrowsUnderlyingStorageException( () -> store.makeStoreOk() );
}
@Test
@@ -193,7 +280,7 @@ public void scanForHighIdMustThrowOnPageOverflow() throws Exception
record.value = 0xCAFEBABE;
store.updateRecord( record );
intsPerRecord = 8192;
- assertThrows( () -> store.makeStoreOk() );
+ assertThrowsUnderlyingStorageException( () -> store.makeStoreOk() );
}
private static class IntRecord extends AbstractBaseRecord
@@ -233,16 +320,20 @@ public IntRecord newRecord()
@Override
public boolean isInUse( PageCursor cursor )
{
+ int offset = cursor.getOffset();
+ long pageId = cursor.getCurrentPageId();
+ long recordId = (offset + pageId * cursor.getCurrentPageSize()) / 4;
boolean inUse = false;
for ( int i = 0; i < intsPerRecord; i++ )
{
inUse |= cursor.getInt() != 0;
}
+ maybeSetCursorError( cursor, recordId );
return inUse;
}
@Override
- public String read( IntRecord record, PageCursor cursor, RecordLoad mode, int recordSize, PagedFile storeFile )
+ public void read( IntRecord record, PageCursor cursor, RecordLoad mode, int recordSize, PagedFile storeFile )
throws IOException
{
for ( int i = 0; i < intsPerRecord; i++ )
@@ -250,7 +341,15 @@ public String read( IntRecord record, PageCursor cursor, RecordLoad mode, int re
record.value = cursor.getInt();
}
record.setInUse( true );
- return null;
+ maybeSetCursorError( cursor, record.getId() );
+ }
+
+ private void maybeSetCursorError( PageCursor cursor, long id )
+ {
+ if ( cursorErrorOnRecord == id )
+ {
+ cursor.setCursorError( "boom" );
+ }
}
@Override
diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/store/CommonAbstractStoreTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/store/CommonAbstractStoreTest.java
index a8a2a532d9f8f..2ca7c6a078645 100644
--- a/community/kernel/src/test/java/org/neo4j/kernel/impl/store/CommonAbstractStoreTest.java
+++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/store/CommonAbstractStoreTest.java
@@ -169,9 +169,13 @@ public void recordCursorPinsEachPageItReads() throws Exception
cursor.acquire( 0, RecordLoad.NORMAL );
assertTrue( cursor.next( nodeId1 ) );
assertTrue( cursor.next( nodeId2 ) );
- assertNotNull( tracer.tryObserve( Pin.class ) );
- assertNull( tracer.tryObserve( Event.class ) );
}
+ // Because both nodes hit the same page, the code will only pin the page once and thus only emit one pin
+ // event. This pin event will not be observable until after we have closed the cursor. We could
+ // alternatively have chosen nodeId2 to be on a different page than nodeId1. In that case, the pin event
+ // for nodeId1 would have been visible after our call to cursor.next( nodeId2 ).
+ assertNotNull( tracer.tryObserve( Pin.class ) );
+ assertNull( tracer.tryObserve( Event.class ) );
}
}
diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/store/format/RecordFormatTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/store/format/RecordFormatTest.java
index 71d339b0c9a54..073ccee4119f3 100644
--- a/community/kernel/src/test/java/org/neo4j/kernel/impl/store/format/RecordFormatTest.java
+++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/store/format/RecordFormatTest.java
@@ -46,13 +46,9 @@
import static java.lang.System.currentTimeMillis;
import static java.nio.file.StandardOpenOption.CREATE;
import static java.util.concurrent.TimeUnit.SECONDS;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
-
+import static org.junit.Assert.assertTrue;
import static org.neo4j.io.ByteUnit.kibiBytes;
import static org.neo4j.kernel.impl.store.record.RecordLoad.NORMAL;
@@ -222,11 +218,11 @@ private void readAndVerifyRecord( R written, R re
do
{
cursor.setOffset( offset );
- error = format.read( read, cursor, NORMAL, recordSize, storeFile );
+ format.read( read, cursor, NORMAL, recordSize, storeFile );
}
while ( cursor.shouldRetry() );
- assertThat( error, is( nullValue() ) );
assertFalse( "Out-of-bounds when reading record " + written, cursor.checkAndClearBoundsFlag() );
+ cursor.checkAndClearCursorError();
// THEN
if ( written.inUse() )
diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/state/PrepareTrackingRecordFormats.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/state/PrepareTrackingRecordFormats.java
index 23a868270c301..fa9f9b3bdbd07 100644
--- a/community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/state/PrepareTrackingRecordFormats.java
+++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/state/PrepareTrackingRecordFormats.java
@@ -181,10 +181,10 @@ public boolean isInUse( PageCursor cursor )
}
@Override
- public String read( RECORD record, PageCursor cursor, RecordLoad mode, int recordSize, PagedFile storeFile )
+ public void read( RECORD record, PageCursor cursor, RecordLoad mode, int recordSize, PagedFile storeFile )
throws IOException
{
- return actual.read( record, cursor, mode, recordSize, storeFile );
+ actual.read( record, cursor, mode, recordSize, storeFile );
}
@Override
diff --git a/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/BaseHighLimitRecordFormat.java b/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/BaseHighLimitRecordFormat.java
index fb83e7bd87bc5..0664078ca1817 100644
--- a/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/BaseHighLimitRecordFormat.java
+++ b/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/BaseHighLimitRecordFormat.java
@@ -90,7 +90,7 @@ protected BaseHighLimitRecordFormat( Function recordSize, i
super( recordSize, recordHeaderSize, IN_USE_BIT, HighLimit.DEFAULT_MAXIMUM_BITS_PER_ID );
}
- public String read( RECORD record, PageCursor primaryCursor, RecordLoad mode, int recordSize, PagedFile storeFile )
+ public void read( RECORD record, PageCursor primaryCursor, RecordLoad mode, int recordSize, PagedFile storeFile )
throws IOException
{
int primaryStartOffset = primaryCursor.getOffset();
@@ -106,7 +106,9 @@ public String read( RECORD record, PageCursor primaryCursor, RecordLoad mode, in
// it may only be read as part of reading the primary unit.
record.clear();
// Return and try again
- return "Expected record to be the first unit in the chain, but record header says it's not";
+ primaryCursor.setCursorError(
+ "Expected record to be the first unit in the chain, but record header says it's not" );
+ return;
}
// This is a record that is split into multiple record units. We need a bit more clever
@@ -121,7 +123,8 @@ public String read( RECORD record, PageCursor primaryCursor, RecordLoad mode, in
// We must have made an inconsistent read of the secondary record unit reference.
// No point in trying to read this.
record.clear();
- return illegalSecondaryReferenceMessage( pageId );
+ primaryCursor.setCursorError( illegalSecondaryReferenceMessage( pageId ) );
+ return;
}
secondaryCursor.setOffset( offset + HEADER_BYTE);
int primarySize = recordSize - (primaryCursor.getOffset() - primaryStartOffset);
@@ -132,13 +135,12 @@ public String read( RECORD record, PageCursor primaryCursor, RecordLoad mode, in
int secondarySize = recordSize - HEADER_BYTE;
PageCursor composite = CompositePageCursor.compose(
primaryCursor, primarySize, secondaryCursor, secondarySize );
- String result = doReadInternal( record, composite, recordSize, headerByte, inUse );
+ doReadInternal( record, composite, recordSize, headerByte, inUse );
record.setSecondaryUnitId( secondaryId );
- return result;
}
else
{
- return doReadInternal( record, primaryCursor, recordSize, headerByte, inUse );
+ doReadInternal( record, primaryCursor, recordSize, headerByte, inUse );
}
}
@@ -147,7 +149,7 @@ private String illegalSecondaryReferenceMessage( long secondaryId )
return "Illegal secondary record reference: " + secondaryId;
}
- protected abstract String doReadInternal(
+ protected abstract void doReadInternal(
RECORD record, PageCursor cursor, int recordSize, long inUseByte, boolean inUse );
@Override
diff --git a/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/DynamicRecordFormat.java b/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/DynamicRecordFormat.java
index cb6ec134ca228..6e4e3d76dc263 100644
--- a/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/DynamicRecordFormat.java
+++ b/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/DynamicRecordFormat.java
@@ -62,7 +62,7 @@ public DynamicRecord newRecord()
}
@Override
- public String read( DynamicRecord record, PageCursor cursor, RecordLoad mode, int recordSize, PagedFile storeFile )
+ public void read( DynamicRecord record, PageCursor cursor, RecordLoad mode, int recordSize, PagedFile storeFile )
throws IOException
{
byte headerByte = cursor.getByte();
@@ -72,14 +72,14 @@ public String read( DynamicRecord record, PageCursor cursor, RecordLoad mode, in
int length = cursor.getShort() | cursor.getByte() << 16;
if ( length > recordSize | length < 0 )
{
- return payloadLengthErrorMessage( record, recordSize, length );
+ cursor.setCursorError( payloadLengthErrorMessage( record, recordSize, length ) );
+ return;
}
long next = cursor.getLong();
boolean isStartRecord = (headerByte & START_RECORD_BIT) != 0;
record.initialize( inUse, isStartRecord, next, -1, length );
readData( record, cursor );
}
- return null;
}
private String payloadLengthErrorMessage( DynamicRecord record, int recordSize, int length )
diff --git a/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/NodeRecordFormat.java b/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/NodeRecordFormat.java
index 17e046b562a2c..fe62bfe558053 100644
--- a/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/NodeRecordFormat.java
+++ b/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/NodeRecordFormat.java
@@ -64,7 +64,7 @@ public NodeRecord newRecord()
}
@Override
- protected String doReadInternal( NodeRecord record, PageCursor cursor, int recordSize, long headerByte,
+ protected void doReadInternal( NodeRecord record, PageCursor cursor, int recordSize, long headerByte,
boolean inUse )
{
// Interpret the header byte
@@ -76,7 +76,6 @@ protected String doReadInternal( NodeRecord record, PageCursor cursor, int recor
long nextProp = decodeCompressedReference( cursor, headerByte, HAS_PROPERTY_BIT, NULL );
long labelField = decodeCompressedReference( cursor, headerByte, HAS_LABELS_BIT, NULL_LABELS );
record.initialize( inUse, nextProp, dense, nextRel, labelField );
- return null;
}
@Override
diff --git a/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/PropertyRecordFormat.java b/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/PropertyRecordFormat.java
index ea59d9c9fad85..ac5f0771b0a19 100644
--- a/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/PropertyRecordFormat.java
+++ b/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/PropertyRecordFormat.java
@@ -63,7 +63,7 @@ public PropertyRecord newRecord()
}
@Override
- public String read( PropertyRecord record, PageCursor cursor, RecordLoad mode, int recordSize, PagedFile storeFile )
+ public void read( PropertyRecord record, PageCursor cursor, RecordLoad mode, int recordSize, PagedFile storeFile )
throws IOException
{
int offset = cursor.getOffset();
@@ -78,14 +78,14 @@ public String read( PropertyRecord record, PageCursor cursor, RecordLoad mode, i
toAbsolute( Reference.decode( cursor ), recordId ) );
if ( (blockCount > record.getBlockCapacity()) | (RECORD_SIZE - (cursor.getOffset() - offset) < blockCount * Long.BYTES) )
{
- return "PropertyRecord claims to contain more blocks than can fit in a record";
+ cursor.setCursorError( "PropertyRecord claims to contain more blocks than can fit in a record" );
+ return;
}
while ( blockCount-- > 0 )
{
record.addLoadedBlock( cursor.getLong() );
}
}
- return null;
}
@Override
diff --git a/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/RelationshipGroupRecordFormat.java b/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/RelationshipGroupRecordFormat.java
index b0d50430e2be8..8863e1ac38066 100644
--- a/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/RelationshipGroupRecordFormat.java
+++ b/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/RelationshipGroupRecordFormat.java
@@ -65,7 +65,7 @@ public RelationshipGroupRecord newRecord()
}
@Override
- protected String doReadInternal( RelationshipGroupRecord record, PageCursor cursor, int recordSize, long headerByte,
+ protected void doReadInternal( RelationshipGroupRecord record, PageCursor cursor, int recordSize, long headerByte,
boolean inUse )
{
record.initialize( inUse,
@@ -75,7 +75,6 @@ protected String doReadInternal( RelationshipGroupRecord record, PageCursor curs
decodeCompressedReference( cursor, headerByte, HAS_LOOP_BIT, NULL ),
decodeCompressedReference( cursor ),
decodeCompressedReference( cursor, headerByte, HAS_NEXT_BIT, NULL ) );
- return null;
}
@Override
diff --git a/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/RelationshipRecordFormat.java b/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/RelationshipRecordFormat.java
index 51f29b7968492..95c259509ed89 100644
--- a/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/RelationshipRecordFormat.java
+++ b/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/RelationshipRecordFormat.java
@@ -71,7 +71,7 @@ public RelationshipRecord newRecord()
}
@Override
- protected String doReadInternal(
+ protected void doReadInternal(
RelationshipRecord record, PageCursor cursor, int recordSize, long headerByte, boolean inUse )
{
int type = cursor.getShort() & 0xFFFF;
@@ -87,7 +87,6 @@ protected String doReadInternal(
decodeAbsoluteIfPresent( cursor, headerByte, HAS_SECOND_CHAIN_NEXT_BIT, recordId ),
has( headerByte, FIRST_IN_FIRST_CHAIN_BIT ),
has( headerByte, FIRST_IN_SECOND_CHAIN_BIT ) );
- return null;
}
private long decodeAbsoluteOrRelative( PageCursor cursor, long headerByte, int firstInStartBit, long recordId )
diff --git a/enterprise/kernel/src/test/java/org/neo4j/kernel/impl/store/format/highlimit/BaseHighLimitRecordFormatTest.java b/enterprise/kernel/src/test/java/org/neo4j/kernel/impl/store/format/highlimit/BaseHighLimitRecordFormatTest.java
index 1fc99a70ed201..87240e45395e7 100644
--- a/enterprise/kernel/src/test/java/org/neo4j/kernel/impl/store/format/highlimit/BaseHighLimitRecordFormatTest.java
+++ b/enterprise/kernel/src/test/java/org/neo4j/kernel/impl/store/format/highlimit/BaseHighLimitRecordFormatTest.java
@@ -101,7 +101,7 @@ protected MyRecordFormat()
}
@Override
- protected String doReadInternal( MyRecord record, PageCursor cursor, int recordSize,
+ protected void doReadInternal( MyRecord record, PageCursor cursor, int recordSize,
long inUseByte, boolean inUse )
{
int shortsPerRecord = getShortsPerRecord();
@@ -111,7 +111,6 @@ protected String doReadInternal( MyRecord record, PageCursor cursor, int recordS
v += (cursor.getByte() & 0xFF);
record.value = v;
}
- return null;
}
private int getShortsPerRecord()