Skip to content

Commit

Permalink
Create a reusable object to compute the degree of dense nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
davidegrohmann committed May 8, 2017
1 parent ad3080c commit 1824de5
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 58 deletions.
Expand Up @@ -22,5 +22,25 @@
@FunctionalInterface @FunctionalInterface
public interface DegreeVisitor public interface DegreeVisitor
{ {
interface Visitable extends AutoCloseable
{
void accept( DegreeVisitor visitor );

@Override
void close();

default void once( DegreeVisitor visitor )
{
try
{
accept( visitor );
}
finally
{
close();
}
}
}

boolean visitDegree( int type, long outgoing, long incoming, long loop ); boolean visitDegree( int type, long outgoing, long incoming, long loop );
} }
@@ -0,0 +1,122 @@
/*
* Copyright (c) 2002-2017 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.kernel.impl.api.store;

import java.util.function.Consumer;

import org.neo4j.function.Disposable;
import org.neo4j.io.pagecache.PageCursor;
import org.neo4j.kernel.api.StatementConstants;
import org.neo4j.kernel.impl.api.DegreeVisitor;
import org.neo4j.kernel.impl.store.InvalidRecordException;
import org.neo4j.kernel.impl.store.RelationshipGroupStore;
import org.neo4j.kernel.impl.store.RelationshipStore;
import org.neo4j.kernel.impl.store.record.Record;
import org.neo4j.kernel.impl.store.record.RelationshipGroupRecord;
import org.neo4j.kernel.impl.store.record.RelationshipRecord;

import static org.neo4j.kernel.impl.store.record.Record.NO_NEXT_RELATIONSHIP;
import static org.neo4j.kernel.impl.store.record.RecordLoad.FORCE;

public class DegreeVisitable implements DegreeVisitor.Visitable, Disposable
{
private final RelationshipStore relationshipStore;
private final RelationshipRecord relationshipRecord;
private final PageCursor relationshipCursor;
private final RelationshipGroupStore groupStore;
private final RelationshipGroupRecord groupRecord;
private final PageCursor groupCursor;
private final Consumer<DegreeVisitable> cache;

private long nodeId;
private long groupId;

public DegreeVisitable( RelationshipStore relationshipStore, RelationshipGroupStore groupStore,
Consumer<DegreeVisitable> cache )
{
this.relationshipStore = relationshipStore;
this.relationshipRecord = relationshipStore.newRecord();
this.relationshipCursor = relationshipStore.newPageCursor();
this.groupStore = groupStore;
this.groupRecord = groupStore.newRecord();
this.groupCursor = groupStore.newPageCursor();
this.cache = cache;
}

public DegreeVisitable init( long nodeId, long firstGroupId )
{
this.nodeId = nodeId;
this.groupId = firstGroupId;
return this;
}

@Override
public void accept( DegreeVisitor visitor )
{
boolean keepGoing = true;
while ( keepGoing && groupId != NO_NEXT_RELATIONSHIP.longValue() )
{
RelationshipGroupRecord record = StoreStatement.read( groupId, groupStore, groupRecord, FORCE, groupCursor );
if ( record.inUse() )
{
int type = record.getType();
long loop = countByFirstPrevPointer( record.getFirstLoop() );
long outgoing = countByFirstPrevPointer( record.getFirstOut() );
long incoming = countByFirstPrevPointer( record.getFirstIn() );
keepGoing = visitor.visitDegree( type, outgoing, incoming, loop );
}
groupId = record.getNext();
}
}

private long countByFirstPrevPointer( long relationshipId )
{
if ( relationshipId == Record.NO_NEXT_RELATIONSHIP.longValue() )
{
return 0;
}
RelationshipRecord record =
StoreStatement.read( relationshipId, relationshipStore, relationshipRecord, FORCE, relationshipCursor );
if ( record.getFirstNode() == nodeId )
{
return record.getFirstPrevRel();
}
if ( record.getSecondNode() == nodeId )
{
return record.getSecondPrevRel();
}
throw new InvalidRecordException( "Node " + nodeId + " neither start nor end node of " + record );
}

@Override
public void close()
{
nodeId = StatementConstants.NO_SUCH_NODE;
groupId = StatementConstants.NO_SUCH_RELATIONSHIP;
cache.accept( this );
}

@Override
public void dispose()
{
groupCursor.close();
relationshipCursor.close();
}
}
Expand Up @@ -63,7 +63,6 @@
import org.neo4j.kernel.impl.store.UnderlyingStorageException; import org.neo4j.kernel.impl.store.UnderlyingStorageException;
import org.neo4j.kernel.impl.store.counts.CountsTracker; import org.neo4j.kernel.impl.store.counts.CountsTracker;
import org.neo4j.kernel.impl.store.record.IndexRule; import org.neo4j.kernel.impl.store.record.IndexRule;
import org.neo4j.kernel.impl.store.record.Record;
import org.neo4j.kernel.impl.store.record.RelationshipGroupRecord; import org.neo4j.kernel.impl.store.record.RelationshipGroupRecord;
import org.neo4j.kernel.impl.store.record.RelationshipRecord; import org.neo4j.kernel.impl.store.record.RelationshipRecord;
import org.neo4j.kernel.impl.transaction.state.PropertyLoader; import org.neo4j.kernel.impl.transaction.state.PropertyLoader;
Expand Down Expand Up @@ -615,27 +614,14 @@ public void degrees( StorageStatement statement, NodeItem node, DegreeVisitor vi
{ {
if ( node.isDense() ) if ( node.isDense() )
{ {
visitDenseNode( statement, node, visitor ); statement.acquireDenseNodeDegreeCounter( node.id(), node.nextGroupId() ).once( visitor );
} }
else else
{ {
visitNode( statement, node, visitor ); visitNode( statement, node, visitor );
} }
} }


private IndexRule indexRule( IndexDescriptor index )
{
for ( IndexRule rule : schemaCache.indexRules() )
{
if ( rule.getIndexDescriptor().equals( index ) )
{
return rule;
}
}

return schemaStorage.indexGetForSchema( index );
}

@Override @Override
public <T> T getOrCreateSchemaDependantState( Class<T> type, Function<StoreReadLayer,T> factory ) public <T> T getOrCreateSchemaDependantState( Class<T> type, Function<StoreReadLayer,T> factory )
{ {
Expand Down Expand Up @@ -669,35 +655,6 @@ private void visitNode( StorageStatement statement, NodeItem node, DegreeVisitor
} }
} }


private void visitDenseNode( StorageStatement statement, NodeItem node, DegreeVisitor visitor )
{
RelationshipGroupRecord relationshipGroupRecord = relationshipGroupStore.newRecord();
RecordCursor<RelationshipGroupRecord> relationshipGroupCursor = statement.recordCursors().relationshipGroup();
RelationshipRecord relationshipRecord = relationshipStore.newRecord();
RecordCursor<RelationshipRecord> relationshipCursor = statement.recordCursors().relationship();

boolean keepGoing = true;
long groupId = node.nextGroupId();
while ( keepGoing && groupId != NO_NEXT_RELATIONSHIP.longValue() )
{
relationshipGroupCursor.next( groupId, relationshipGroupRecord, FORCE );
if ( relationshipGroupRecord.inUse() )
{
int type = relationshipGroupRecord.getType();

long firstLoop = relationshipGroupRecord.getFirstLoop();
long firstOut = relationshipGroupRecord.getFirstOut();
long firstIn = relationshipGroupRecord.getFirstIn();

long loop = countByFirstPrevPointer( firstLoop, relationshipCursor, node.id(), relationshipRecord );
long outgoing = countByFirstPrevPointer( firstOut, relationshipCursor, node.id(), relationshipRecord );
long incoming = countByFirstPrevPointer( firstIn, relationshipCursor, node.id(), relationshipRecord );
keepGoing = visitor.visitDegree( type, outgoing, incoming, loop );
}
groupId = relationshipGroupRecord.getNext();
}
}

private Direction directionOf( long nodeId, long relationshipId, long startNode, long endNode ) private Direction directionOf( long nodeId, long relationshipId, long startNode, long endNode )
{ {
if ( startNode == nodeId ) if ( startNode == nodeId )
Expand All @@ -713,23 +670,17 @@ private Direction directionOf( long nodeId, long relationshipId, long startNode,
" with startNode:" + startNode + " and endNode:" + endNode ); " with startNode:" + startNode + " and endNode:" + endNode );
} }


private static long countByFirstPrevPointer( long relationshipId, RecordCursor<RelationshipRecord> cursor, private IndexRule indexRule( IndexDescriptor index )
long nodeId, RelationshipRecord relationshipRecord )
{ {
if ( relationshipId == Record.NO_NEXT_RELATIONSHIP.longValue() ) for ( IndexRule rule : schemaCache.indexRules() )
{
return 0;
}
cursor.next( relationshipId, relationshipRecord, FORCE );
if ( relationshipRecord.getFirstNode() == nodeId )
{
return relationshipRecord.getFirstPrevRel();
}
if ( relationshipRecord.getSecondNode() == nodeId )
{ {
return relationshipRecord.getSecondPrevRel(); if ( rule.getIndexDescriptor().equals( index ) )
{
return rule;
}
} }
throw new InvalidRecordException( "Node " + nodeId + " neither start nor end node of " + relationshipRecord );
return schemaStorage.indexGetForSchema( index );
} }


private static Iterator<IndexDescriptor> toIndexDescriptors( Iterable<IndexRule> rules ) private static Iterator<IndexDescriptor> toIndexDescriptors( Iterable<IndexRule> rules )
Expand Down
Expand Up @@ -19,16 +19,23 @@
*/ */
package org.neo4j.kernel.impl.api.store; package org.neo4j.kernel.impl.api.store;


import java.io.IOException;
import java.util.function.Supplier; import java.util.function.Supplier;


import org.neo4j.cursor.Cursor; import org.neo4j.cursor.Cursor;
import org.neo4j.io.pagecache.PageCursor;
import org.neo4j.kernel.api.exceptions.index.IndexNotFoundKernelException; import org.neo4j.kernel.api.exceptions.index.IndexNotFoundKernelException;
import org.neo4j.kernel.api.schema.index.IndexDescriptor; import org.neo4j.kernel.api.schema.index.IndexDescriptor;
import org.neo4j.kernel.impl.api.DegreeVisitor;
import org.neo4j.kernel.impl.api.IndexReaderFactory; import org.neo4j.kernel.impl.api.IndexReaderFactory;
import org.neo4j.kernel.impl.locking.Lock; import org.neo4j.kernel.impl.locking.Lock;
import org.neo4j.kernel.impl.locking.LockService; import org.neo4j.kernel.impl.locking.LockService;
import org.neo4j.kernel.impl.store.CommonAbstractStore;
import org.neo4j.kernel.impl.store.NeoStores; import org.neo4j.kernel.impl.store.NeoStores;
import org.neo4j.kernel.impl.store.RecordCursors; import org.neo4j.kernel.impl.store.RecordCursors;
import org.neo4j.kernel.impl.store.UnderlyingStorageException;
import org.neo4j.kernel.impl.store.record.AbstractBaseRecord;
import org.neo4j.kernel.impl.store.record.RecordLoad;
import org.neo4j.kernel.impl.util.InstanceCache; import org.neo4j.kernel.impl.util.InstanceCache;
import org.neo4j.storageengine.api.Direction; import org.neo4j.storageengine.api.Direction;
import org.neo4j.storageengine.api.NodeItem; import org.neo4j.storageengine.api.NodeItem;
Expand All @@ -55,6 +62,7 @@ public class StoreStatement implements StorageStatement
private final InstanceCache<StoreNodeRelationshipCursor> nodeRelationshipsCursor; private final InstanceCache<StoreNodeRelationshipCursor> nodeRelationshipsCursor;
private final InstanceCache<StorePropertyCursor> propertyCursorCache; private final InstanceCache<StorePropertyCursor> propertyCursorCache;
private final InstanceCache<StoreSinglePropertyCursor> singlePropertyCursorCache; private final InstanceCache<StoreSinglePropertyCursor> singlePropertyCursorCache;
private final InstanceCache<DegreeVisitable> degreeVisitableCache;
private final NeoStores neoStores; private final NeoStores neoStores;
private final Supplier<IndexReaderFactory> indexReaderFactorySupplier; private final Supplier<IndexReaderFactory> indexReaderFactorySupplier;
private final Supplier<LabelScanReader> labelScanStore; private final Supplier<LabelScanReader> labelScanStore;
Expand Down Expand Up @@ -123,6 +131,15 @@ protected StoreSinglePropertyCursor create()
return new StoreSinglePropertyCursor( neoStores.getPropertyStore(), this ); return new StoreSinglePropertyCursor( neoStores.getPropertyStore(), this );
} }
}; };
degreeVisitableCache = new InstanceCache<DegreeVisitable>()
{
@Override
protected DegreeVisitable create()
{
return new DegreeVisitable( neoStores.getRelationshipStore(), neoStores.getRelationshipGroupStore(),
this );
}
};
} }


@Override @Override
Expand Down Expand Up @@ -184,6 +201,12 @@ public Cursor<PropertyItem> acquireSinglePropertyCursor( long propertyId, int pr
return singlePropertyCursorCache.get().init( propertyKeyId, propertyId, lock, state ); return singlePropertyCursorCache.get().init( propertyKeyId, propertyId, lock, state );
} }


@Override
public DegreeVisitor.Visitable acquireDenseNodeDegreeCounter( long nodeId, long groupId )
{
return degreeVisitableCache.get().init( nodeId, groupId );
}

@Override @Override
public void release() public void release()
{ {
Expand All @@ -204,6 +227,7 @@ public void close()
nodeRelationshipsCursor.close(); nodeRelationshipsCursor.close();
propertyCursorCache.close(); propertyCursorCache.close();
singlePropertyCursorCache.close(); singlePropertyCursorCache.close();
degreeVisitableCache.close();
recordCursors.close(); recordCursors.close();
closed = true; closed = true;
} }
Expand Down Expand Up @@ -252,4 +276,19 @@ public RecordCursors recordCursors()
{ {
return recordCursors; return recordCursors;
} }

public static <RECORD extends AbstractBaseRecord, STORE extends CommonAbstractStore<RECORD,?>> RECORD read( long id,
STORE store, RECORD record, RecordLoad mode, PageCursor cursor )
{
try
{
record.clear();
store.readIntoRecord( id, record, mode, cursor );
return record;
}
catch ( IOException e )
{
throw new UnderlyingStorageException( e );
}
}
} }
Expand Up @@ -22,6 +22,7 @@
import org.neo4j.cursor.Cursor; import org.neo4j.cursor.Cursor;
import org.neo4j.kernel.api.exceptions.index.IndexNotFoundKernelException; import org.neo4j.kernel.api.exceptions.index.IndexNotFoundKernelException;
import org.neo4j.kernel.api.schema.index.IndexDescriptor; import org.neo4j.kernel.api.schema.index.IndexDescriptor;
import org.neo4j.kernel.impl.api.DegreeVisitor;
import org.neo4j.kernel.impl.locking.Lock; import org.neo4j.kernel.impl.locking.Lock;
import org.neo4j.kernel.impl.store.RecordCursors; import org.neo4j.kernel.impl.store.RecordCursors;
import org.neo4j.storageengine.api.schema.IndexReader; import org.neo4j.storageengine.api.schema.IndexReader;
Expand Down Expand Up @@ -122,6 +123,8 @@ Cursor<RelationshipItem> acquireNodeRelationshipCursor( boolean isDense, long no


Cursor<PropertyItem> acquireSinglePropertyCursor( long propertyId, int propertyKeyId, Lock shortLivedReadLock, PropertyContainerState state ); Cursor<PropertyItem> acquireSinglePropertyCursor( long propertyId, int propertyKeyId, Lock shortLivedReadLock, PropertyContainerState state );


DegreeVisitor.Visitable acquireDenseNodeDegreeCounter( long nodeId, long groupId );

/** /**
* @return {@link LabelScanReader} capable of reading nodes for specific label ids. * @return {@link LabelScanReader} capable of reading nodes for specific label ids.
*/ */
Expand Down

0 comments on commit 1824de5

Please sign in to comment.