Skip to content

Commit

Permalink
Increase relationship type to take up to 24 bits in high limit enterp…
Browse files Browse the repository at this point in the history
…rise record format.

Update relationship record and relationship group formats.
  • Loading branch information
MishaDemianenko committed Sep 13, 2016
1 parent 760dabd commit 39b3f57
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 129 deletions.
Expand Up @@ -52,10 +52,12 @@
*/ */
class RelationshipGroupRecordFormat extends BaseHighLimitRecordFormat<RelationshipGroupRecord> class RelationshipGroupRecordFormat extends BaseHighLimitRecordFormat<RelationshipGroupRecord>
{ {
private static final int TYPE_BYTES = 3;

static final int RECORD_SIZE = 32; static final int RECORD_SIZE = 32;
static final int FIXED_FORMAT_RECORD_SIZE = HEADER_BYTE + static final int FIXED_FORMAT_RECORD_SIZE = HEADER_BYTE +
Byte.BYTES /* modifiers */ + Byte.BYTES /* modifiers */ +
Short.BYTES /* type */ + TYPE_BYTES /* type */ +
Integer.BYTES /* next */ + Integer.BYTES /* next */ +
Integer.BYTES /* first out */ + Integer.BYTES /* first out */ +
Integer.BYTES /* first in */ + Integer.BYTES /* first in */ +
Expand All @@ -65,14 +67,14 @@ class RelationshipGroupRecordFormat extends BaseHighLimitRecordFormat<Relationsh
private static final int HAS_OUTGOING_BIT = 0b0000_1000; private static final int HAS_OUTGOING_BIT = 0b0000_1000;
private static final int HAS_INCOMING_BIT = 0b0001_0000; private static final int HAS_INCOMING_BIT = 0b0001_0000;
private static final int HAS_LOOP_BIT = 0b0010_0000; private static final int HAS_LOOP_BIT = 0b0010_0000;
private static final int HAS_NEXT_BIT = 0b0100_0000;


private static final int HAS_NEXT_BIT = 0b0100_0000;
private static final int NEXT_RECORD_BIT = 0b0000_0001; private static final int NEXT_RECORD_BIT = 0b0000_0001;
private static final int FIRST_OUT_BIT = 0b0000_0010; private static final int FIRST_OUT_BIT = 0b0000_0010;
private static final int FIRST_IN_BIT = 0b0000_0100; private static final int FIRST_IN_BIT = 0b0000_0100;
private static final int FIRST_LOOP_BIT = 0b0000_1000; private static final int FIRST_LOOP_BIT = 0b0000_1000;
private static final int OWNING_NODE_BIT = 0b0001_0000;


private static final int OWNING_NODE_BIT = 0b0001_0000;
private static final long ONE_BIT_OVERFLOW_BIT_MASK = 0xFFFF_FFFE_0000_0000L; private static final long ONE_BIT_OVERFLOW_BIT_MASK = 0xFFFF_FFFE_0000_0000L;
private static final long HIGH_DWORD_LAST_BIT_MASK = 0x100000000L; private static final long HIGH_DWORD_LAST_BIT_MASK = 0x100000000L;


Expand Down Expand Up @@ -104,8 +106,10 @@ protected void doReadInternal( RelationshipGroupRecord record, PageCursor cursor
} }
else else
{ {
int typeLowWord = cursor.getShort() & 0xFFFF;
int type = (((cursor.getByte() & 0xFF) << Short.SIZE) | typeLowWord);
record.initialize( inUse, record.initialize( inUse,
cursor.getShort() & 0xFFFF, type,
decodeCompressedReference( cursor, headerByte, HAS_OUTGOING_BIT, NULL ), decodeCompressedReference( cursor, headerByte, HAS_OUTGOING_BIT, NULL ),
decodeCompressedReference( cursor, headerByte, HAS_INCOMING_BIT, NULL ), decodeCompressedReference( cursor, headerByte, HAS_INCOMING_BIT, NULL ),
decodeCompressedReference( cursor, headerByte, HAS_LOOP_BIT, NULL ), decodeCompressedReference( cursor, headerByte, HAS_LOOP_BIT, NULL ),
Expand All @@ -128,12 +132,12 @@ protected byte headerBits( RelationshipGroupRecord record )
@Override @Override
protected int requiredDataLength( RelationshipGroupRecord record ) protected int requiredDataLength( RelationshipGroupRecord record )
{ {
return 2 + // type return TYPE_BYTES + // type
length( record.getFirstOut(), NULL ) + length( record.getFirstOut(), NULL ) +
length( record.getFirstIn(), NULL ) + length( record.getFirstIn(), NULL ) +
length( record.getFirstLoop(), NULL ) + length( record.getFirstLoop(), NULL ) +
length( record.getOwningNode() ) + length( record.getOwningNode() ) +
length( record.getNext(), NULL ); length( record.getNext(), NULL );
} }


@Override @Override
Expand All @@ -147,7 +151,10 @@ protected void doWriteInternal( RelationshipGroupRecord record, PageCursor curso
} }
else else
{ {
cursor.putShort( (short) record.getType() ); int type = record.getType();
cursor.putShort( (short) type );
cursor.putByte( (byte) (type >>> Short.SIZE) );

encode( cursor, record.getFirstOut(), NULL ); encode( cursor, record.getFirstOut(), NULL );
encode( cursor, record.getFirstIn(), NULL ); encode( cursor, record.getFirstIn(), NULL );
encode( cursor, record.getFirstLoop(), NULL ); encode( cursor, record.getFirstLoop(), NULL );
Expand Down Expand Up @@ -181,7 +188,8 @@ private void readFixedReferencesMethod( RelationshipGroupRecord record, PageCurs
// [ x, ] high owner bits // [ x, ] high owner bits
long modifiers = cursor.getByte(); 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 nextLowBits = cursor.getInt() & 0xFFFFFFFFL;
long firstOutLowBits = cursor.getInt() & 0xFFFFFFFFL; long firstOutLowBits = cursor.getInt() & 0xFFFFFFFFL;
Expand Down Expand Up @@ -218,7 +226,10 @@ private void writeFixedReferencesRecord( RelationshipGroupRecord record, PageCur
// [ x, ] high owner bits // [ x, ] high owner bits
cursor.putByte( (byte) (nextMod | firstOutMod | firstInMod | firstLoopMod | owningNodeMod) ); 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.getNext() );
cursor.putInt( (int) record.getFirstOut() ); cursor.putInt( (int) record.getFirstOut() );
cursor.putInt( (int) record.getFirstIn() ); cursor.putInt( (int) record.getFirstIn() );
Expand Down
Expand Up @@ -70,6 +70,8 @@ class RelationshipRecordFormat extends BaseHighLimitRecordFormat<RelationshipRec
Integer.BYTES /* second next rel */ + Integer.BYTES /* second next rel */ +
Integer.BYTES /* next property */; Integer.BYTES /* next property */;


private static final int TYPE_FIELD_BYTES = 3;

private static final int FIRST_IN_FIRST_CHAIN_BIT = 0b0000_1000; private static final int FIRST_IN_FIRST_CHAIN_BIT = 0b0000_1000;
private static final int FIRST_IN_SECOND_CHAIN_BIT = 0b0001_0000; private static final int FIRST_IN_SECOND_CHAIN_BIT = 0b0001_0000;
private static final int HAS_FIRST_CHAIN_NEXT_BIT = 0b0010_0000; private static final int HAS_FIRST_CHAIN_NEXT_BIT = 0b0010_0000;
Expand Down Expand Up @@ -110,15 +112,17 @@ public RelationshipRecord newRecord()
protected void doReadInternal( protected void doReadInternal(
RelationshipRecord record, PageCursor cursor, int recordSize, long headerByte, boolean inUse ) RelationshipRecord record, PageCursor cursor, int recordSize, long headerByte, boolean inUse )
{ {
int type = cursor.getShort() & 0xFFFF;
if (record.isUseFixedReferences()) if (record.isUseFixedReferences())
{ {
int type = cursor.getShort() & 0xFFFF;
// read record in fixed reference format // read record in fixed reference format
readFixedReferencesRecord( record, cursor, headerByte, inUse, type ); readFixedReferencesRecord( record, cursor, headerByte, inUse, type );
record.setUseFixedReferences( true ); record.setUseFixedReferences( true );
} }
else else
{ {
int typeLowWord = cursor.getShort() & 0xFFFF;
int type = (((cursor.getByte() & 0xFF) << Short.SIZE) | typeLowWord);
long recordId = record.getId(); long recordId = record.getId();
record.initialize( inUse, record.initialize( inUse,
decodeCompressedReference( cursor, headerByte, HAS_PROPERTY_BIT, NULL ), decodeCompressedReference( cursor, headerByte, HAS_PROPERTY_BIT, NULL ),
Expand Down Expand Up @@ -150,7 +154,7 @@ protected byte headerBits( RelationshipRecord record )
protected int requiredDataLength( RelationshipRecord record ) protected int requiredDataLength( RelationshipRecord record )
{ {
long recordId = record.getId(); long recordId = record.getId();
return Short.BYTES + // type return TYPE_FIELD_BYTES +
length( record.getNextProp(), NULL ) + length( record.getNextProp(), NULL ) +
length( record.getFirstNode() ) + length( record.getFirstNode() ) +
length( record.getSecondNode() ) + length( record.getSecondNode() ) +
Expand All @@ -164,14 +168,17 @@ protected int requiredDataLength( RelationshipRecord record )
protected void doWriteInternal( RelationshipRecord record, PageCursor cursor ) protected void doWriteInternal( RelationshipRecord record, PageCursor cursor )
throws IOException throws IOException
{ {
cursor.putShort( (short) record.getType() );
if (record.isUseFixedReferences()) if (record.isUseFixedReferences())
{ {
// write record in fixed reference format // write record in fixed reference format
writeFixedReferencesRecord( record, cursor ); writeFixedReferencesRecord( record, cursor );
} }
else else
{ {
int type = record.getType();
cursor.putShort( (short) type );
cursor.putByte( (byte) (type >>> Short.SIZE) );

long recordId = record.getId(); long recordId = record.getId();
encode( cursor, record.getNextProp(), NULL ); encode( cursor, record.getNextProp(), NULL );
encode( cursor, record.getFirstNode() ); encode( cursor, record.getFirstNode() );
Expand All @@ -194,6 +201,7 @@ protected void doWriteInternal( RelationshipRecord record, PageCursor cursor )
protected boolean canUseFixedReferences( RelationshipRecord record, int recordSize ) protected boolean canUseFixedReferences( RelationshipRecord record, int recordSize )
{ {
return (isRecordBigEnoughForFixedReferences( recordSize ) && return (isRecordBigEnoughForFixedReferences( recordSize ) &&
(record.getType() < (1 << Short.SIZE)) &&
(record.getFirstNode() & ONE_BIT_OVERFLOW_BIT_MASK) == 0) && (record.getFirstNode() & ONE_BIT_OVERFLOW_BIT_MASK) == 0) &&
((record.getSecondNode() & 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)) && ((record.getFirstPrevRel() == NULL) || ((record.getFirstPrevRel() & ONE_BIT_OVERFLOW_BIT_MASK) == 0)) &&
Expand Down Expand Up @@ -285,6 +293,8 @@ private void readFixedReferencesRecord( RelationshipRecord record, PageCursor cu


private void writeFixedReferencesRecord( RelationshipRecord record, PageCursor cursor ) private void writeFixedReferencesRecord( RelationshipRecord record, PageCursor cursor )
{ {
cursor.putShort( (short) record.getType() );

long firstNode = record.getFirstNode(); long firstNode = record.getFirstNode();
short firstNodeMod = (short)((firstNode & HIGH_DWORD_LAST_BIT_MASK) >> 32); short firstNodeMod = (short)((firstNode & HIGH_DWORD_LAST_BIT_MASK) >> 32);


Expand Down
Expand Up @@ -66,13 +66,13 @@ public void readWriteFixedReferencesRecord() throws Exception
{ {
RelationshipGroupRecord source = new RelationshipGroupRecord( 1 ); RelationshipGroupRecord source = new RelationshipGroupRecord( 1 );
RelationshipGroupRecord target = new RelationshipGroupRecord( 1 ); RelationshipGroupRecord target = new RelationshipGroupRecord( 1 );
source.initialize( true, 0, randomFixedReference(), randomFixedReference(), source.initialize( true, randomType(), randomFixedReference(), randomFixedReference(),
randomFixedReference(), randomFixedReference(), randomFixedReference()); randomFixedReference(), randomFixedReference(), randomFixedReference());


writeReadRecord( source, target ); writeReadRecord( source, target );


assertTrue( "Record should use fixed reference format.", target.isUseFixedReferences() ); assertTrue( "Record should use fixed reference format.", target.isUseFixedReferences() );
verifySameReferences( source, target); verifySame( source, target);
} }


@Test @Test
Expand All @@ -98,70 +98,27 @@ public void useVariableLengthFormatWhenRecordSizeIsTooSmall() throws IOException
{ {
RelationshipGroupRecord source = new RelationshipGroupRecord( 1 ); RelationshipGroupRecord source = new RelationshipGroupRecord( 1 );
RelationshipGroupRecord target = new RelationshipGroupRecord( 1 ); RelationshipGroupRecord target = new RelationshipGroupRecord( 1 );
source.initialize( true, 0, randomFixedReference(), randomFixedReference(), source.initialize( true, randomType(), randomFixedReference(), randomFixedReference(),
randomFixedReference(), randomFixedReference(), randomFixedReference()); randomFixedReference(), randomFixedReference(), randomFixedReference());


writeReadRecord( source, target, RelationshipGroupRecordFormat.FIXED_FORMAT_RECORD_SIZE - 1 ); writeReadRecord( source, target, RelationshipGroupRecordFormat.FIXED_FORMAT_RECORD_SIZE - 1 );


assertFalse( "Record should use variable length reference if format record is too small.", target.isUseFixedReferences() ); assertFalse( "Record should use variable length reference if format record is too small.", target.isUseFixedReferences() );
verifySameReferences( source, target); verifySame( source, target);
} }


@Test @Test
public void useFixedReferenceFormatWhenRecordCanFitInRecordSizeRecord() throws IOException public void useFixedReferenceFormatWhenRecordCanFitInRecordSizeRecord() throws IOException
{ {
RelationshipGroupRecord source = new RelationshipGroupRecord( 1 ); RelationshipGroupRecord source = new RelationshipGroupRecord( 1 );
RelationshipGroupRecord target = new RelationshipGroupRecord( 1 ); RelationshipGroupRecord target = new RelationshipGroupRecord( 1 );
source.initialize( true, 0, randomFixedReference(), randomFixedReference(), source.initialize( true, randomType(), randomFixedReference(), randomFixedReference(),
randomFixedReference(), randomFixedReference(), randomFixedReference()); randomFixedReference(), randomFixedReference(), randomFixedReference());


writeReadRecord( source, target, RelationshipGroupRecordFormat.FIXED_FORMAT_RECORD_SIZE ); writeReadRecord( source, target, RelationshipGroupRecordFormat.FIXED_FORMAT_RECORD_SIZE );


assertTrue( "Record should use fixed reference if can fit in format record.", target.isUseFixedReferences() ); assertTrue( "Record should use fixed reference if can fit in format record.", target.isUseFixedReferences() );
verifySameReferences( source, target); verifySame( 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 );
} }


private void verifyRecordsWithPoisonedReference( RelationshipGroupRecord source, RelationshipGroupRecord target, private void verifyRecordsWithPoisonedReference( RelationshipGroupRecord source, RelationshipGroupRecord target,
Expand All @@ -188,7 +145,7 @@ private void verifyRecordsWithPoisonedReference( RelationshipGroupRecord source,
{ {
assertFalse( "Record should use variable length reference format.", target.isUseFixedReferences() ); assertFalse( "Record should use variable length reference format.", target.isUseFixedReferences() );
} }
verifySameReferences( source, target ); verifySame( source, target );
Collections.rotate( references, 1 ); Collections.rotate( references, 1 );
} }
} }
Expand All @@ -204,8 +161,9 @@ private List<Long> buildReferenceList( int differentReferences, long poison )
return references; 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 In references should be equal.", recordA.getFirstIn(), recordB.getFirstIn() );
assertEquals( "First Loop references should be equal.", recordA.getFirstLoop(), recordB.getFirstLoop() ); assertEquals( "First Loop references should be equal.", recordA.getFirstLoop(), recordB.getFirstLoop() );
assertEquals( "First Out references should be equal.", recordA.getFirstOut(), recordB.getFirstOut() ); assertEquals( "First Out references should be equal.", recordA.getFirstOut(), recordB.getFirstOut() );
Expand All @@ -228,14 +186,14 @@ private void writeReadRecord( RelationshipGroupRecord source, RelationshipGroupR
recordFormat.read( target, pageCursor, RecordLoad.NORMAL, recordSize ); 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 ) private long randomReference( long maxValue )
Expand Down

0 comments on commit 39b3f57

Please sign in to comment.