Skip to content

Commit

Permalink
Use custom code for counting dense node degrees
Browse files Browse the repository at this point in the history
Using visitors was causing regressions
  • Loading branch information
davidegrohmann committed May 8, 2017
1 parent b84649f commit c2cf079
Show file tree
Hide file tree
Showing 12 changed files with 233 additions and 210 deletions.
Expand Up @@ -79,8 +79,8 @@ private void decrementCountForLabelsAndRelationships( NodeItem node )
return false; return false;
} ); } );


storeLayer.degrees( statement, node, ALWAYS_TRUE_INT, storeLayer.degrees( statement, node,
( type, out, in, loop ) -> updateRelationshipsCountsFromDegrees( labelIds, type, -out, -in, -loop ) ); ( type, out, in ) -> updateRelationshipsCountsFromDegrees( labelIds, type, -out, -in ) );
} }


@Override @Override
Expand Down Expand Up @@ -125,40 +125,37 @@ public void visitNodeLabelChanges( long id, final Set<Integer> added, final Set<
// get the relationship counts from *before* this transaction, // get the relationship counts from *before* this transaction,
// the relationship changes will compensate for what happens during the transaction // the relationship changes will compensate for what happens during the transaction
storeLayer.nodeCursor( statement, id, null ) storeLayer.nodeCursor( statement, id, null )
.forAll( node -> storeLayer.degrees( statement, node, ALWAYS_TRUE_INT, ( type, out, in, loop ) -> .forAll( node -> storeLayer.degrees( statement, node, ( type, out, in ) ->
{ {
added.forEach( label -> updateRelationshipsCountsFromDegrees( type, label, out, in, loop ) ); added.forEach( label -> updateRelationshipsCountsFromDegrees( type, label, out, in ) );
removed.forEach( removed.forEach(
label -> updateRelationshipsCountsFromDegrees( type, label, -out, -in, -loop ) ); label -> updateRelationshipsCountsFromDegrees( type, label, -out, -in ) );
return true;
} ) ); } ) );
} }
super.visitNodeLabelChanges( id, added, removed ); super.visitNodeLabelChanges( id, added, removed );
} }


private boolean updateRelationshipsCountsFromDegrees( PrimitiveIntCollection labels, int type, long outgoing, private void updateRelationshipsCountsFromDegrees( PrimitiveIntCollection labels, int type, long out, long in )
long incoming, long loop )
{ {
labels.visitKeys( label -> updateRelationshipsCountsFromDegrees( type, label, outgoing, incoming, loop ) ); labels.visitKeys( label -> updateRelationshipsCountsFromDegrees( type, label, out, in ) );
return true;
} }


private boolean updateRelationshipsCountsFromDegrees( int type, int label, long out, long in, long loop ) private boolean updateRelationshipsCountsFromDegrees( int type, int label, long out, long in )
{ {
// untyped // untyped
counts.incrementRelationshipCount( label, ANY_RELATIONSHIP_TYPE, ANY_LABEL, out + loop ); counts.incrementRelationshipCount( label, ANY_RELATIONSHIP_TYPE, ANY_LABEL, out );
counts.incrementRelationshipCount( ANY_LABEL, ANY_RELATIONSHIP_TYPE, label, in + loop ); counts.incrementRelationshipCount( ANY_LABEL, ANY_RELATIONSHIP_TYPE, label, in );
// typed // typed
counts.incrementRelationshipCount( label, type, ANY_LABEL, out + loop ); counts.incrementRelationshipCount( label, type, ANY_LABEL, out );
counts.incrementRelationshipCount( ANY_LABEL, type, label, in + loop ); counts.incrementRelationshipCount( ANY_LABEL, type, label, in );
return false; return false;
} }


private void updateRelationshipCount( long startNode, int type, long endNode, int delta ) private void updateRelationshipCount( long startNode, int type, long endNode, int delta )
{ {
updateRelationshipsCountsFromDegrees( type, ANY_LABEL, delta, 0, 0 ); updateRelationshipsCountsFromDegrees( type, ANY_LABEL, delta, 0 );
visitLabels( startNode, ( labelId ) -> updateRelationshipsCountsFromDegrees( type, labelId, delta, 0, 0 ) ); visitLabels( startNode, ( labelId ) -> updateRelationshipsCountsFromDegrees( type, labelId, delta, 0) );
visitLabels( endNode, ( labelId ) -> updateRelationshipsCountsFromDegrees( type, labelId, 0, delta, 0 ) ); visitLabels( endNode, ( labelId ) -> updateRelationshipsCountsFromDegrees( type, labelId, 0, delta ) );
} }


private void visitLabels( long nodeId, PrimitiveIntVisitor<RuntimeException> visitor ) private void visitLabels( long nodeId, PrimitiveIntVisitor<RuntimeException> visitor )
Expand Down

This file was deleted.

Expand Up @@ -22,13 +22,5 @@
@FunctionalInterface @FunctionalInterface
public interface DegreeVisitor public interface DegreeVisitor
{ {
interface Visitable extends AutoCloseable void visitDegree( int type, long outgoing, long incoming );
{
void accept( DegreeVisitor visitor );

@Override
void close();
}

boolean visitDegree( int type, long outgoing, long incoming, long loop );
} }
Expand Up @@ -1650,30 +1650,14 @@ public PrimitiveIntSet relationshipTypes( KernelStatement statement, NodeItem no
@Override @Override
public int degree( KernelStatement statement, NodeItem node, Direction direction ) public int degree( KernelStatement statement, NodeItem node, Direction direction )
{ {
int degree = statement.hasTxStateWithChanges() && statement.txState().nodeIsAddedInThisTx( node.id() ) ReadableTransactionState state = statement.hasTxStateWithChanges() ? statement.txState() : null;
? 0 return storeLayer.countDegrees( statement.getStoreStatement(), node, direction, state );
: countDegrees( statement, node, new CountingDegreeVisitor( direction, false ) );

return statement.hasTxStateWithChanges()
? statement.txState().getNodeState( node.id() ).augmentDegree( direction, degree )
: degree;
} }


@Override @Override
public int degree( KernelStatement statement, NodeItem node, Direction direction, int relType ) public int degree( KernelStatement statement, NodeItem node, Direction direction, int relType )
{ {
int degree = statement.hasTxStateWithChanges() && statement.txState().nodeIsAddedInThisTx( node.id() ) ReadableTransactionState state = statement.hasTxStateWithChanges() ? statement.txState() : null;
? 0 return storeLayer.countDegrees( statement.getStoreStatement(), node, direction, relType, state );
: countDegrees( statement, node, new CountingDegreeVisitor( direction, relType, node.isDense() ) );

return statement.hasTxStateWithChanges()
? statement.txState().getNodeState( node.id() ).augmentDegree( direction, degree, relType )
: degree;
}

private int countDegrees( KernelStatement statement, NodeItem node, CountingDegreeVisitor countingDegreeVisitor )
{
storeLayer.degrees( statement.getStoreStatement(), node, countingDegreeVisitor, countingDegreeVisitor );
return countingDegreeVisitor.count();
} }
} }
Expand Up @@ -20,38 +20,35 @@
package org.neo4j.kernel.impl.api.store; package org.neo4j.kernel.impl.api.store;


import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.IntPredicate;


import org.neo4j.function.Disposable;
import org.neo4j.io.pagecache.PageCursor; import org.neo4j.io.pagecache.PageCursor;
import org.neo4j.kernel.api.StatementConstants; import org.neo4j.kernel.api.StatementConstants;
import org.neo4j.kernel.impl.api.DegreeVisitor; import org.neo4j.kernel.impl.api.DegreeVisitor;
import org.neo4j.kernel.impl.store.InvalidRecordException; import org.neo4j.kernel.impl.store.InvalidRecordException;
import org.neo4j.kernel.impl.store.RelationshipGroupStore; import org.neo4j.kernel.impl.store.RelationshipGroupStore;
import org.neo4j.kernel.impl.store.RelationshipStore; 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.RelationshipGroupRecord;
import org.neo4j.kernel.impl.store.record.RelationshipRecord; import org.neo4j.kernel.impl.store.record.RelationshipRecord;
import org.neo4j.storageengine.api.Direction;


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


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


private long nodeId; private long nodeId;
private long groupId; private long groupId;
private IntPredicate types;


DegreeVisitable( RelationshipStore relationshipStore, RelationshipGroupStore groupStore, DenseNodeDegreeCounter( RelationshipStore relationshipStore, RelationshipGroupStore groupStore,
Consumer<DegreeVisitable> cache ) Consumer<DenseNodeDegreeCounter> cache )
{ {
this.relationshipStore = relationshipStore; this.relationshipStore = relationshipStore;
this.relationshipRecord = relationshipStore.newRecord(); this.relationshipRecord = relationshipStore.newRecord();
Expand All @@ -62,39 +59,45 @@ public class DegreeVisitable implements DegreeVisitor.Visitable, Disposable
this.cache = cache; this.cache = cache;
} }


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


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


@Override
public int count( Direction direction )
{
return countRelationshipsInGroup( direction, -1 );
}

public int count( Direction direction, int relType )
{
return countRelationshipsInGroup( direction, relType );
}

private long countByFirstPrevPointer( long relationshipId ) private long countByFirstPrevPointer( long relationshipId )
{ {
if ( Record.NO_NEXT_RELATIONSHIP.is( relationshipId ) ) if ( NO_NEXT_RELATIONSHIP.is( relationshipId ) )
{ {
return 0; return 0;
} }
Expand All @@ -111,12 +114,59 @@ private long countByFirstPrevPointer( long relationshipId )
throw new InvalidRecordException( "Node " + nodeId + " neither start nor end node of " + record ); throw new InvalidRecordException( "Node " + nodeId + " neither start nor end node of " + record );
} }


private int countRelationshipsInGroup( Direction direction, int type )
{
int count = 0;
while ( !NO_NEXT_RELATIONSHIP.is( groupId ) )
{
RelationshipGroupRecord record = groupStore.readRecord( groupId, groupRecord, FORCE, groupCursor );
if ( record.inUse() && ( type == -1 || groupRecord.getType() == type ) )
{
count += nodeDegreeByDirection( direction );
if ( type != -1 )
{
// we have read the only type we were interested on, so break the look
break;
}
}
groupId = groupRecord.getNext();
}
return count;
}

private long nodeDegreeByDirection( Direction direction )
{
long firstLoop = groupRecord.getFirstLoop();
long loopCount = countByFirstPrevPointer( firstLoop );
switch ( direction )
{
case OUTGOING:
{
long firstOut = groupRecord.getFirstOut();
return countByFirstPrevPointer( firstOut ) + loopCount;
}
case INCOMING:
{
long firstIn = groupRecord.getFirstIn();
return countByFirstPrevPointer( firstIn ) + loopCount;
}
case BOTH:
{
long firstOut = groupRecord.getFirstOut();
long firstIn = groupRecord.getFirstIn();
return countByFirstPrevPointer( firstOut ) +
countByFirstPrevPointer( firstIn ) + loopCount;
}
default:
throw new IllegalArgumentException( direction.name() );
}
}

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


Expand Down
@@ -0,0 +1,37 @@
/*
* 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 org.neo4j.function.Disposable;
import org.neo4j.kernel.impl.api.DegreeVisitor;
import org.neo4j.storageengine.api.Direction;

public interface NodeDegreeCounter extends AutoCloseable, Disposable
{
void accept( DegreeVisitor visitor );

int count( Direction direction );

int count( Direction direction, int relType );

@Override
void close();

}

0 comments on commit c2cf079

Please sign in to comment.