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 456efad5ef5dd..aa0550a3e109f 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 @@ -52,10 +52,12 @@ */ class RelationshipGroupRecordFormat extends BaseHighLimitRecordFormat { + private static final int TYPE_BYTES = 3; + static final int RECORD_SIZE = 32; static final int FIXED_FORMAT_RECORD_SIZE = HEADER_BYTE + Byte.BYTES /* modifiers */ + - Short.BYTES /* type */ + + TYPE_BYTES /* type */ + Integer.BYTES /* next */ + Integer.BYTES /* first out */ + Integer.BYTES /* first in */ + @@ -65,14 +67,14 @@ class RelationshipGroupRecordFormat extends BaseHighLimitRecordFormat>> Short.SIZE) ); + encode( cursor, record.getFirstOut(), NULL ); encode( cursor, record.getFirstIn(), NULL ); encode( cursor, record.getFirstLoop(), NULL ); @@ -181,7 +188,8 @@ private void readFixedReferencesMethod( RelationshipGroupRecord record, PageCurs // [ x, ] high owner bits long modifiers = cursor.getByte(); - int type = cursor.getShort() & 0xFFFF; + int typeLowWord = cursor.getShort() & 0xFFFF; + int type = (((cursor.getByte() & 0xFF) << Short.SIZE) | typeLowWord); long nextLowBits = cursor.getInt() & 0xFFFFFFFFL; long firstOutLowBits = cursor.getInt() & 0xFFFFFFFFL; @@ -218,7 +226,10 @@ private void writeFixedReferencesRecord( RelationshipGroupRecord record, PageCur // [ x, ] high owner bits cursor.putByte( (byte) (nextMod | firstOutMod | firstInMod | firstLoopMod | owningNodeMod) ); - cursor.putShort( (short) record.getType() ); + int type = record.getType(); + cursor.putShort( (short) type ); + cursor.putByte( (byte) (type >> Short.SIZE) ); + cursor.putInt( (int) record.getNext() ); cursor.putInt( (int) record.getFirstOut() ); cursor.putInt( (int) record.getFirstIn() ); 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 edc800a14effe..0f1dd93640793 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 @@ -70,6 +70,8 @@ class RelationshipRecordFormat extends BaseHighLimitRecordFormat>> Short.SIZE) ); + long recordId = record.getId(); encode( cursor, record.getNextProp(), NULL ); encode( cursor, record.getFirstNode() ); @@ -194,6 +201,7 @@ protected void doWriteInternal( RelationshipRecord record, PageCursor cursor ) protected boolean canUseFixedReferences( RelationshipRecord record, int recordSize ) { return (isRecordBigEnoughForFixedReferences( recordSize ) && + (record.getType() < (1 << Short.SIZE)) && (record.getFirstNode() & ONE_BIT_OVERFLOW_BIT_MASK) == 0) && ((record.getSecondNode() & ONE_BIT_OVERFLOW_BIT_MASK) == 0) && ((record.getFirstPrevRel() == NULL) || ((record.getFirstPrevRel() & ONE_BIT_OVERFLOW_BIT_MASK) == 0)) && @@ -285,6 +293,8 @@ private void readFixedReferencesRecord( RelationshipRecord record, PageCursor cu private void writeFixedReferencesRecord( RelationshipRecord record, PageCursor cursor ) { + cursor.putShort( (short) record.getType() ); + long firstNode = record.getFirstNode(); short firstNodeMod = (short)((firstNode & HIGH_DWORD_LAST_BIT_MASK) >> 32); diff --git a/enterprise/kernel/src/test/java/org/neo4j/kernel/impl/store/format/highlimit/RelationshipGroupRecordFormatTest.java b/enterprise/kernel/src/test/java/org/neo4j/kernel/impl/store/format/highlimit/RelationshipGroupRecordFormatTest.java index f035bc0f4b97b..ed6a42b2a4ba6 100644 --- a/enterprise/kernel/src/test/java/org/neo4j/kernel/impl/store/format/highlimit/RelationshipGroupRecordFormatTest.java +++ b/enterprise/kernel/src/test/java/org/neo4j/kernel/impl/store/format/highlimit/RelationshipGroupRecordFormatTest.java @@ -66,13 +66,13 @@ public void readWriteFixedReferencesRecord() throws Exception { RelationshipGroupRecord source = new RelationshipGroupRecord( 1 ); RelationshipGroupRecord target = new RelationshipGroupRecord( 1 ); - source.initialize( true, 0, randomFixedReference(), randomFixedReference(), + source.initialize( true, randomType(), randomFixedReference(), randomFixedReference(), randomFixedReference(), randomFixedReference(), randomFixedReference()); writeReadRecord( source, target ); assertTrue( "Record should use fixed reference format.", target.isUseFixedReferences() ); - verifySameReferences( source, target); + verifySame( source, target); } @Test @@ -98,13 +98,13 @@ public void useVariableLengthFormatWhenRecordSizeIsTooSmall() throws IOException { RelationshipGroupRecord source = new RelationshipGroupRecord( 1 ); RelationshipGroupRecord target = new RelationshipGroupRecord( 1 ); - source.initialize( true, 0, randomFixedReference(), randomFixedReference(), + source.initialize( true, randomType(), randomFixedReference(), randomFixedReference(), randomFixedReference(), randomFixedReference(), randomFixedReference()); writeReadRecord( source, target, RelationshipGroupRecordFormat.FIXED_FORMAT_RECORD_SIZE - 1 ); assertFalse( "Record should use variable length reference if format record is too small.", target.isUseFixedReferences() ); - verifySameReferences( source, target); + verifySame( source, target); } @Test @@ -112,56 +112,13 @@ public void useFixedReferenceFormatWhenRecordCanFitInRecordSizeRecord() throws I { RelationshipGroupRecord source = new RelationshipGroupRecord( 1 ); RelationshipGroupRecord target = new RelationshipGroupRecord( 1 ); - source.initialize( true, 0, randomFixedReference(), randomFixedReference(), + source.initialize( true, randomType(), randomFixedReference(), randomFixedReference(), randomFixedReference(), randomFixedReference(), randomFixedReference()); writeReadRecord( source, target, RelationshipGroupRecordFormat.FIXED_FORMAT_RECORD_SIZE ); assertTrue( "Record should use fixed reference if can fit in format record.", target.isUseFixedReferences() ); - verifySameReferences( source, target); - } - - @Test - public void readSingleUnitRecordStoredNotInFixedReferenceFormat() throws Exception - { - RelationshipGroupRecord oldFormatRecord = new RelationshipGroupRecord( 1 ); - RelationshipGroupRecord newFormatRecord = new RelationshipGroupRecord( 1 ); - oldFormatRecord.initialize( true, 0, randomFixedReference(), randomFixedReference(), - randomFixedReference(), randomFixedReference(), randomFixedReference()); - - writeRecordWithOldFormat( oldFormatRecord ); - - assertFalse( "This should be single unit record.", oldFormatRecord.hasSecondaryUnitId() ); - assertFalse( "Old format is not aware about fixed references.", oldFormatRecord.isUseFixedReferences() ); - - recordFormat.read( newFormatRecord, pageCursor, RecordLoad.NORMAL, RelationshipGroupRecordFormat.RECORD_SIZE ); - verifySameReferences( oldFormatRecord, newFormatRecord ); - } - - @Test - public void readDoubleUnitRecordStoredNotInFixedReferenceFormat() throws Exception - { - RelationshipGroupRecord oldFormatRecord = new RelationshipGroupRecord( 0 ); - RelationshipGroupRecord newFormatRecord = new RelationshipGroupRecord( 0 ); - oldFormatRecord.initialize( true, 0, bigReference(), bigReference(), - bigReference(), bigReference(), bigReference()); - - writeRecordWithOldFormat( oldFormatRecord ); - - assertTrue( "This should be double unit record.", oldFormatRecord.hasSecondaryUnitId() ); - assertFalse( "Old format is not aware about fixed references.", oldFormatRecord.isUseFixedReferences() ); - - recordFormat.read( newFormatRecord, pageCursor, RecordLoad.NORMAL, RelationshipGroupRecordFormat.RECORD_SIZE ); - verifySameReferences( oldFormatRecord, newFormatRecord ); - } - - private void writeRecordWithOldFormat( RelationshipGroupRecord oldFormatRecord ) throws IOException - { - int oldRecordSize = RelationshipGroupRecordFormatV3_0_0.RECORD_SIZE; - RelationshipGroupRecordFormatV3_0_0 recordFormatV30 = new RelationshipGroupRecordFormatV3_0_0(); - recordFormatV30.prepare( oldFormatRecord, oldRecordSize, idSequence ); - recordFormatV30.write( oldFormatRecord, pageCursor, oldRecordSize ); - pageCursor.setOffset( 0 ); + verifySame( source, target); } private void verifyRecordsWithPoisonedReference( RelationshipGroupRecord source, RelationshipGroupRecord target, @@ -188,7 +145,7 @@ private void verifyRecordsWithPoisonedReference( RelationshipGroupRecord source, { assertFalse( "Record should use variable length reference format.", target.isUseFixedReferences() ); } - verifySameReferences( source, target ); + verifySame( source, target ); Collections.rotate( references, 1 ); } } @@ -204,8 +161,9 @@ private List buildReferenceList( int differentReferences, long poison ) return references; } - private void verifySameReferences( RelationshipGroupRecord recordA, RelationshipGroupRecord recordB ) + private void verifySame( RelationshipGroupRecord recordA, RelationshipGroupRecord recordB ) { + assertEquals( "Types should be equal.", recordA.getType(), recordB.getType() ); assertEquals( "First In references should be equal.", recordA.getFirstIn(), recordB.getFirstIn() ); assertEquals( "First Loop references should be equal.", recordA.getFirstLoop(), recordB.getFirstLoop() ); assertEquals( "First Out references should be equal.", recordA.getFirstOut(), recordB.getFirstOut() ); @@ -228,14 +186,14 @@ private void writeReadRecord( RelationshipGroupRecord source, RelationshipGroupR recordFormat.read( target, pageCursor, RecordLoad.NORMAL, recordSize ); } - private long randomFixedReference() + private int randomType() { - return randomReference( 1L << (Integer.SIZE + 1) ); + return (int) randomReference( 1L << 24 ); } - private long bigReference() + private long randomFixedReference() { - return 1L << 57; + return randomReference( 1L << (Integer.SIZE + 1) ); } private long randomReference( long maxValue ) diff --git a/enterprise/kernel/src/test/java/org/neo4j/kernel/impl/store/format/highlimit/RelationshipRecordFormatTest.java b/enterprise/kernel/src/test/java/org/neo4j/kernel/impl/store/format/highlimit/RelationshipRecordFormatTest.java index 6facaaad34ba0..3bd5bc8b4f3c8 100644 --- a/enterprise/kernel/src/test/java/org/neo4j/kernel/impl/store/format/highlimit/RelationshipRecordFormatTest.java +++ b/enterprise/kernel/src/test/java/org/neo4j/kernel/impl/store/format/highlimit/RelationshipRecordFormatTest.java @@ -118,7 +118,37 @@ public void readWriteFixedReferencesRecord() throws Exception { RelationshipRecord source = new RelationshipRecord( 1 ); RelationshipRecord target = new RelationshipRecord( 1 ); - source.initialize( true, randomFixedReference(), randomFixedReference(), randomFixedReference(), 0, + source.initialize( true, randomFixedReference(), randomFixedReference(), randomFixedReference(), randomShortType(), + randomFixedReference(), randomFixedReference(), randomFixedReference(), randomFixedReference(), + true, true ); + + writeReadRecord( source, target ); + + assertTrue( "Record should use fixed reference format.", target.isUseFixedReferences() ); + verifySameReferences( source, target); + } + + @Test + public void useVariableLengthFormatWhenTypeIsTooBig() throws IOException + { + RelationshipRecord source = new RelationshipRecord( 1 ); + RelationshipRecord target = new RelationshipRecord( 1 ); + source.initialize( true, randomFixedReference(), randomFixedReference(), randomFixedReference(), 1 << 16, + randomFixedReference(), randomFixedReference(), randomFixedReference(), randomFixedReference(), + true, true ); + + writeReadRecord( source, target ); + + assertFalse( "Record should use variable length format.", target.isUseFixedReferences() ); + verifySameReferences( source, target); + } + + @Test + public void useFixedReferenceFormatWhenTypeIsSmallEnough() throws IOException + { + RelationshipRecord source = new RelationshipRecord( 1 ); + RelationshipRecord target = new RelationshipRecord( 1 ); + source.initialize( true, randomFixedReference(), randomFixedReference(), randomFixedReference(), (1 << 16) - 1, randomFixedReference(), randomFixedReference(), randomFixedReference(), randomFixedReference(), true, true ); @@ -134,7 +164,7 @@ public void useFixedRecordFormatWhenAtLeastOneOfTheReferencesIsMissing() throws RelationshipRecord source = new RelationshipRecord( 1 ); RelationshipRecord target = new RelationshipRecord( 1 ); - verifyRecordsWithPoisonedReference( source, target, NULL ); + verifyRecordsWithPoisonedReference( source, target, NULL, randomShortType() ); } @Test @@ -142,7 +172,7 @@ public void useVariableLengthFormatWhenAtLeastOneOfTheReferencesIsTooBig() throw { RelationshipRecord source = new RelationshipRecord( 1 ); RelationshipRecord target = new RelationshipRecord( 1 ); - verifyRecordsWithPoisonedReference( source, target, 1L << Integer.SIZE + 5 ); + verifyRecordsWithPoisonedReference( source, target, 1L << Integer.SIZE + 5, randomType() ); } @Test @@ -150,7 +180,7 @@ public void useVariableLengthFormatWhenRecordSizeIsTooSmall() throws IOException { RelationshipRecord source = new RelationshipRecord( 1 ); RelationshipRecord target = new RelationshipRecord( 1 ); - source.initialize( true, randomFixedReference(), randomFixedReference(), randomFixedReference(), 0, + source.initialize( true, randomFixedReference(), randomFixedReference(), randomFixedReference(), randomType(), randomFixedReference(), randomFixedReference(), randomFixedReference(), randomFixedReference(), true, true ); @@ -165,7 +195,7 @@ public void useFixedReferenceFormatWhenRecordCanFitInRecordSizeRecord() throws I { RelationshipRecord source = new RelationshipRecord( 1 ); RelationshipRecord target = new RelationshipRecord( 1 ); - source.initialize( true, randomFixedReference(), randomFixedReference(), randomFixedReference(), 0, + source.initialize( true, randomFixedReference(), randomFixedReference(), randomFixedReference(), randomShortType(), randomFixedReference(), randomFixedReference(), randomFixedReference(), randomFixedReference(), true, true ); @@ -175,53 +205,8 @@ public void useFixedReferenceFormatWhenRecordCanFitInRecordSizeRecord() throws I verifySameReferences( source, target); } - @Test - public void readSingleUnitRecordStoredNotInFixedReferenceFormat() throws Exception - { - RelationshipRecord oldFormatRecord = new RelationshipRecord( 1 ); - RelationshipRecord newFormatRecord = new RelationshipRecord( 1 ); - oldFormatRecord.initialize( true, randomSmallReference(), randomSmallReference(), randomSmallReference(), 0, - randomSmallReference(), randomSmallReference(), randomSmallReference(), randomSmallReference(), - true, true ); - - writeRecordWithOldFormat( oldFormatRecord ); - - assertFalse( "This should be single unit record.", oldFormatRecord.hasSecondaryUnitId() ); - assertFalse( "Old format is not aware about fixed references.", oldFormatRecord.isUseFixedReferences() ); - - format.read( newFormatRecord, cursor, RecordLoad.NORMAL, RelationshipRecordFormat.RECORD_SIZE ); - verifySameReferences( oldFormatRecord, newFormatRecord ); - } - - @Test - public void readDoubleUnitRecordStoredNotInFixedReferenceFormat() throws Exception - { - RelationshipRecord oldFormatRecord = new RelationshipRecord( 1 ); - RelationshipRecord newFormatRecord = new RelationshipRecord( 1 ); - oldFormatRecord.initialize( true, bigReference(), bigReference(), bigReference(), 0, - bigReference(), bigReference(), bigReference(), bigReference(), - true, true ); - - writeRecordWithOldFormat( oldFormatRecord ); - - assertTrue( "This should be double unit record.", oldFormatRecord.hasSecondaryUnitId() ); - assertFalse( "Old format is not aware about fixed references.", oldFormatRecord.isUseFixedReferences() ); - - format.read( newFormatRecord, cursor, RecordLoad.NORMAL, RelationshipRecordFormat.RECORD_SIZE ); - verifySameReferences( oldFormatRecord, newFormatRecord ); - } - - private void writeRecordWithOldFormat( RelationshipRecord oldFormatRecord ) throws IOException - { - int oldRecordSize = RelationshipRecordFormatV3_0_0.RECORD_SIZE; - RelationshipRecordFormatV3_0_0 recordFormatV30 = new RelationshipRecordFormatV3_0_0(); - recordFormatV30.prepare( oldFormatRecord, oldRecordSize, idSequence ); - recordFormatV30.write( oldFormatRecord, cursor, oldRecordSize ); - cursor.setOffset( 0 ); - } - private void verifyRecordsWithPoisonedReference( RelationshipRecord source, RelationshipRecord target, - long poisonedReference ) throws IOException + long poisonedReference, int type ) throws IOException { boolean nullPoison = poisonedReference == NULL; // first and second node can't be empty references so excluding them in case if poisoned reference is null @@ -234,7 +219,7 @@ private void verifyRecordsWithPoisonedReference( RelationshipRecord source, Rela source.initialize( true, iterator.next(), nullPoison ? randomFixedReference() : iterator.next(), nullPoison ? randomFixedReference() : iterator.next(), - 0, iterator.next(), iterator.next(), iterator.next(), iterator.next(), true, true ); + type, iterator.next(), iterator.next(), iterator.next(), iterator.next(), true, true ); writeReadRecord( source, target ); @@ -308,6 +293,7 @@ private void checkRecord( RelationshipRecordFormat format, int recordSize, StubP private void verifySameReferences( RelationshipRecord record, RelationshipRecord recordFromStore ) { + assertEquals( "Types should be equal.", record.getType(), recordFromStore.getType() ); assertEquals( "First Next references should be equal.", record.getFirstNextRel(), recordFromStore.getFirstNextRel() ); assertEquals( "First Node references should be equal.", record.getFirstNode(), recordFromStore.getFirstNode() ); assertEquals( "First Prev Rel references should be equal.", record.getFirstPrevRel(), recordFromStore.getFirstPrevRel() ); @@ -336,22 +322,23 @@ private RelationshipRecord createRecord( RelationshipRecordFormat format, long r record.setSecondNextRel( 4L ); record.setSecondNode( 5L ); record.setSecondPrevRel( 6L ); + record.setType( 7 ); return record; } - private long randomFixedReference() + private int randomShortType() { - return randomReference( 1L << (Integer.SIZE + 1 ) ); + return (int) randomReference( 1L << Short.SIZE ); } - private long bigReference() + private int randomType() { - return 1L << 57; + return (int) randomReference( 1L << 24 ); } - private long randomSmallReference() + private long randomFixedReference() { - return randomReference( 1L << (Integer.SIZE - 4 ) ); + return randomReference( 1L << (Integer.SIZE + 1 ) ); } private long randomReference( long maxValue )