diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/BaseHighLimitRecordFormat.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/BaseHighLimitRecordFormat.java index 5f7bf945eb5a2..c0a09f66b9a0c 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/BaseHighLimitRecordFormat.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/BaseHighLimitRecordFormat.java @@ -31,8 +31,6 @@ import org.neo4j.kernel.impl.store.record.AbstractBaseRecord; import org.neo4j.kernel.impl.store.record.Record; -import static org.neo4j.kernel.impl.store.RecordPageLocationCalculator.offsetForId; -import static org.neo4j.kernel.impl.store.RecordPageLocationCalculator.pageIdForRecord; import static org.neo4j.kernel.impl.store.format.highlimit.Reference.PAGE_CURSOR_ADAPTER; /** @@ -81,18 +79,22 @@ abstract class BaseHighLimitRecordFormat static final long NULL = Record.NULL_REFERENCE.intValue(); static final int HEADER_BIT_RECORD_UNIT = 0b0000_0010; static final int HEADER_BIT_FIRST_RECORD_UNIT = 0b0000_0100; + // Default to community record format + private final RecordIO recordIO; // = new RecordIO.CommunityRecordIO<>(); - protected BaseHighLimitRecordFormat( Function recordSize, int recordHeaderSize ) + protected BaseHighLimitRecordFormat( Function recordSize, int recordHeaderSize, + RecordIO recordIO ) { super( recordSize, recordHeaderSize, IN_USE_BIT ); + this.recordIO = recordIO; } @Override protected final void doRead( RECORD record, PageCursor primaryCursor, int recordSize, PagedFile storeFile, long headerByte, boolean inUse ) throws IOException { - boolean recordUnit = has( headerByte, HEADER_BIT_RECORD_UNIT ); - if ( recordUnit ) + boolean doubleRecordUnit = has( headerByte, HEADER_BIT_RECORD_UNIT ); + if ( doubleRecordUnit ) { boolean firstRecordUnit = has( headerByte, HEADER_BIT_FIRST_RECORD_UNIT ); if ( !firstRecordUnit ) @@ -100,40 +102,14 @@ protected final void doRead( RECORD record, PageCursor primaryCursor, int record // This is a record unit and not even the first one, so you cannot go here directly and read it, // it may only be read as part of reading the primary unit. record.clear(); + // Return and try again return; + } else { + recordIO.read( record, primaryCursor, recordSize, storeFile, + ( readAdapter ) -> doReadInternal( record, primaryCursor, recordSize, headerByte, inUse, + readAdapter ) ); } - } - - if ( recordUnit ) - { - int primaryEndOffset = primaryCursor.getOffset() + recordSize - 1 /*we've already read the header byte*/; - - // This is a record that is split into multiple record units. We need a bit more clever - // data structures here. For the time being this means instantiating one object, - // but the trade-off is a great reduction in complexity. - long secondaryId = Reference.decode( primaryCursor, PAGE_CURSOR_ADAPTER ); - @SuppressWarnings( "resource" ) - SecondaryPageCursorReadDataAdapter readAdapter = new SecondaryPageCursorReadDataAdapter( - primaryCursor, storeFile, - pageIdForRecord( secondaryId, storeFile.pageSize(), recordSize ), - offsetForId( secondaryId, storeFile.pageSize(), recordSize ), - primaryEndOffset, PagedFile.PF_SHARED_READ_LOCK ); - - try ( SecondaryPageCursorControl secondaryPageCursorControl = readAdapter ) - { - do - { - // (re)sets offsets for both cursors - secondaryPageCursorControl.reposition(); - doReadInternal( record, primaryCursor, recordSize, headerByte, inUse, readAdapter ); - } - while ( secondaryPageCursorControl.shouldRetry() ); - - record.setSecondaryId( secondaryId ); - } - } - else - { + } else { doReadInternal( record, primaryCursor, recordSize, headerByte, inUse, PAGE_CURSOR_ADAPTER ); } } @@ -154,20 +130,15 @@ protected final void doWrite( RECORD record, PageCursor primaryCursor, int recor headerByte = set( headerByte, HEADER_BIT_FIRST_RECORD_UNIT, true ); primaryCursor.putByte( headerByte ); - DataAdapter dataAdapter = PAGE_CURSOR_ADAPTER; if ( record.requiresTwoUnits() ) { - int primaryEndOffset = primaryCursor.getOffset() + recordSize - 1 /*we've already written the header byte*/; - - // Write using the normal adapter since the first reference we write cannot really overflow - // into the secondary record - Reference.encode( record.getSecondaryId(), primaryCursor, PAGE_CURSOR_ADAPTER ); - dataAdapter = new SecondaryPageCursorWriteDataAdapter( - pageIdForRecord( record.getSecondaryId(), storeFile.pageSize(), recordSize ), - offsetForId( record.getSecondaryId(), storeFile.pageSize(), recordSize ), primaryEndOffset ); + recordIO.write( record, primaryCursor, recordSize, storeFile, + ( dataAdapter ) -> doWriteInternal( record, primaryCursor, dataAdapter ) ); + } + else + { + doWriteInternal( record, primaryCursor, PAGE_CURSOR_ADAPTER ); } - - doWriteInternal( record, primaryCursor, dataAdapter ); } protected abstract void doWriteInternal( RECORD record, PageCursor cursor, DataAdapter adapter ) diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/NodeRecordFormat.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/NodeRecordFormat.java index c0577b9284ba9..606c9aeb9ca6d 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/NodeRecordFormat.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/NodeRecordFormat.java @@ -35,6 +35,10 @@ class NodeRecordFormat extends BaseHighLimitRecordFormat private static final int HAS_PROPERTY_BIT = 0b0010_0000; private static final int HAS_LABELS_BIT = 0b0100_0000; + public NodeRecordFormat( RecordIO recordIO ) + { + super( fixedRecordSize( RECORD_SIZE ), 0, recordIO ); + } public NodeRecordFormat() { super( fixedRecordSize( RECORD_SIZE ), 0 ); diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/RelationshipGroupRecordFormat.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/RelationshipGroupRecordFormat.java index cc279444d6f95..379fe6589a37e 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/RelationshipGroupRecordFormat.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/RelationshipGroupRecordFormat.java @@ -37,6 +37,11 @@ public RelationshipGroupRecordFormat() super( fixedRecordSize( RECORD_SIZE ), 0 ); } + public RelationshipGroupRecordFormat( RecordIO recordIO ) + { + super( fixedRecordSize( RECORD_SIZE ), 0, recordIO ); + } + @Override public RelationshipGroupRecord newRecord() { diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/RelationshipRecordFormat.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/RelationshipRecordFormat.java index 553b82c604c43..466ab17cbbc94 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/RelationshipRecordFormat.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/RelationshipRecordFormat.java @@ -37,6 +37,10 @@ public RelationshipRecordFormat() { super( fixedRecordSize( RECORD_SIZE ), 0 ); } + public RelationshipRecordFormat( RecordIO recordIO ) + { + super( fixedRecordSize( RECORD_SIZE ), 0, recordIO ); + } @Override public RelationshipRecord newRecord() diff --git a/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/EnterpriseHighLimit.java b/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/EnterpriseHighLimit.java new file mode 100644 index 0000000000000..5246c9d10b01f --- /dev/null +++ b/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/EnterpriseHighLimit.java @@ -0,0 +1,60 @@ +/* + * 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 Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.neo4j.kernel.impl.store.format.highlimit; + + +import org.neo4j.kernel.impl.store.format.RecordFormat; +import org.neo4j.kernel.impl.store.record.NodeRecord; +import org.neo4j.kernel.impl.store.record.RelationshipGroupRecord; +import org.neo4j.kernel.impl.store.record.RelationshipRecord; + +/** + * Record format with very high limits, 50-bit per ID, as well the ability to use two record units per record, while at + * the same time keeping store size small. + * + * @see HighLimit + */ +public class EnterpriseHighLimit extends HighLimit +{ + @Override + public String storeVersion() + { + // Enterprise.HighLimit.Zero + return "vE.H.0"; + } + + @Override + public RecordFormat node() + { + return new NodeRecordFormat( new EnterpriseRecordIO<>() ); + } + + @Override + public RecordFormat relationship() + { + return new RelationshipRecordFormat( new EnterpriseRecordIO<>() ); + } + + @Override + public RecordFormat relationshipGroup() + { + return new RelationshipGroupRecordFormat( new EnterpriseRecordIO<>() ); + } +} diff --git a/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/EnterpriseRecordIO.java b/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/EnterpriseRecordIO.java new file mode 100644 index 0000000000000..dd2bc0adc5e36 --- /dev/null +++ b/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/store/format/highlimit/EnterpriseRecordIO.java @@ -0,0 +1,87 @@ +/* + * 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 Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.neo4j.kernel.impl.store.format.highlimit; + +import java.io.IOException; +import java.util.function.Consumer; + +import org.neo4j.function.ThrowingConsumer; +import org.neo4j.io.pagecache.PageCursor; +import org.neo4j.io.pagecache.PagedFile; +import org.neo4j.kernel.impl.store.format.highlimit.Reference.DataAdapter; +import org.neo4j.kernel.impl.store.record.AbstractBaseRecord; + +import static org.neo4j.kernel.impl.store.RecordPageLocationCalculator.offsetForId; +import static org.neo4j.kernel.impl.store.RecordPageLocationCalculator.pageIdForRecord; +import static org.neo4j.kernel.impl.store.format.highlimit.Reference.PAGE_CURSOR_ADAPTER; + +/** + * Enterprise supports double record units for records. + */ +public class EnterpriseRecordIO implements RecordIO +{ + + @Override + public void read( RECORD record, PageCursor primaryCursor, int recordSize, PagedFile storeFile, + Consumer> reader ) throws IOException + { + int primaryEndOffset = primaryCursor.getOffset() + recordSize - 1 /*we've already read the header byte*/; + + // This is a record that is split into multiple record units. We need a bit more clever + // data structures here. For the time being this means instantiating one object, + // but the trade-off is a great reduction in complexity. + long secondaryId = Reference.decode( primaryCursor, PAGE_CURSOR_ADAPTER ); + @SuppressWarnings( "resource" ) SecondaryPageCursorReadDataAdapter readAdapter = + new SecondaryPageCursorReadDataAdapter( primaryCursor, storeFile, + pageIdForRecord( secondaryId, storeFile.pageSize(), recordSize ), + offsetForId( secondaryId, storeFile.pageSize(), recordSize ), primaryEndOffset, + PagedFile.PF_SHARED_READ_LOCK ); + + try ( SecondaryPageCursorControl secondaryPageCursorControl = readAdapter ) + { + do + { + // (re)sets offsets for both cursors + secondaryPageCursorControl.reposition(); + //doReadInternal( record, primaryCursor, recordSize, headerByte, inUse, readAdapter ); + reader.accept( readAdapter ); + } + while ( secondaryPageCursorControl.shouldRetry() ); + + record.setSecondaryId( secondaryId ); + } + } + + @Override + public void write( RECORD record, PageCursor primaryCursor, int recordSize, PagedFile storeFile, + ThrowingConsumer, IOException> writer ) throws IOException + { + int primaryEndOffset = primaryCursor.getOffset() + recordSize - 1 /*we've already written the header byte*/; + + // Write using the normal adapter since the first reference we write cannot really overflow + // into the secondary record + Reference.encode( record.getSecondaryId(), primaryCursor, PAGE_CURSOR_ADAPTER ); + DataAdapter dataAdapter = new SecondaryPageCursorWriteDataAdapter( + pageIdForRecord( record.getSecondaryId(), storeFile.pageSize(), recordSize ), + offsetForId( record.getSecondaryId(), storeFile.pageSize(), recordSize ), primaryEndOffset ); + + writer.accept( dataAdapter ); + } +}