@Override public long valueFrom( NodeRecord node ) { return node.getNextRel(); }
@Override public long valueFrom( NodeRecord record ) { return record.getNextRel(); } }
public boolean hasRelationship( NodeRecord node ) { return node.getNextRel() != Record.NO_NEXT_RELATIONSHIP.intValue(); }
@Override public void checkConsistency( NodeRecord node, CheckerEngine<NodeRecord, ConsistencyReport.NodeConsistencyReport> engine, RecordAccess records ) { if ( !Record.NO_NEXT_RELATIONSHIP.is( node.getNextRel() ) ) { engine.comparativeCheck( records.relationship( node.getNextRel() ), this ); } }
@Override public void checkConsistency( NodeRecord node, CheckerEngine<NodeRecord, NodeConsistencyReport> engine, RecordAccess records ) { if ( !Record.NO_NEXT_RELATIONSHIP.is( node.getNextRel() ) ) { engine.comparativeCheck( records.relationshipGroup( node.getNextRel() ), this ); } }
private void connect( NodeRecord node, RelationshipRecord rel, RecordAccess<RelationshipRecord, Void> relRecords, ResourceLocker locks ) { connect( node.getId(), node.getNextRel(), rel, relRecords, locks ); }
public void validateNodeRecord( NodeRecord record ) throws TransactionFailureException { if ( !record.inUse() && record.getNextRel() != Record.NO_NEXT_RELATIONSHIP.intValue() ) { throw new ConstraintViolationTransactionFailureException( "Cannot delete node<" + record.getId() + ">, because it still has relationships. " + "To delete this node, you must first delete its relationships." ); } }
public RecordSet<RelationshipRecord> findRelationshipChainsThatThisRecordShouldBelongTo( RelationshipRecord relationship ) { RecordSet<RelationshipRecord> records = new RecordSet<>(); for ( RelationshipNodeField field : RelationshipNodeField.values() ) { long nodeId = field.get( relationship ); nodeStore.getRecord( nodeId, nodeRecord, RecordLoad.FORCE ); records.addAll( relationshipChainExplorer.followChainFromNode( nodeId, nodeRecord.getNextRel() ) ); } return records; } }
@Override protected void processCache() { cacheAccess.clearCache(); long[] fields = new long[] {1, 0, -1}; CacheAccess.Client client = cacheAccess.client(); try ( ResourceIterator<NodeRecord> nodeRecords = nodes.iterator() ) { while ( nodeRecords.hasNext() ) { NodeRecord node = nodeRecords.next(); if ( node.inUse() ) { fields[CacheSlots.NextRelationship.SLOT_RELATIONSHIP_ID] = node.getNextRel(); client.putToCache( node.getId(), fields ); } } } } }
@Override public RecordKey<NodeRecord> node() { return ( written, read ) -> { assertEquals( written.getNextProp(), read.getNextProp() ); assertEquals( written.getNextRel(), read.getNextRel() ); assertEquals( written.getLabelField(), read.getLabelField() ); assertEquals( written.isDense(), read.isDense() ); }; }
private void convertNodeToDenseIfNecessary( NodeRecord node, RecordAccess<RelationshipRecord, Void> relRecords, RecordAccess<RelationshipGroupRecord, Integer> relGroupRecords, ResourceLocker locks ) { if ( node.isDense() ) { return; } long relId = node.getNextRel(); if ( relId != Record.NO_NEXT_RELATIONSHIP.intValue() ) { RecordProxy<RelationshipRecord, Void> relChange = relRecords.getOrLoad( relId, null ); RelationshipRecord rel = relChange.forReadingLinkage(); if ( relCount( node.getId(), rel ) >= denseNodeThreshold ) { locks.acquireExclusive( LockTracer.NONE, ResourceTypes.RELATIONSHIP, relId ); // Re-read the record after we've locked it since another transaction might have // changed in the meantime. relChange = relRecords.getOrLoad( relId, null ); convertNodeToDenseNode( node, relChange.forChangingLinkage(), relRecords, relGroupRecords, locks ); } } }
private String diff( NodeRecord expected, NodeRecord actual ) { if ( actual.getId() == expected.getId() && actual.getNextRel() == expected.getNextRel() && actual.getLabelField() == expected.getLabelField() && actual.getNextProp() == expected.getNextProp() && actual.isDense() == expected.isDense() && actual.isLight() == expected.isLight() ) { return null; } return describeDiff( expected.toString(), actual.toString() ); }
public RelationshipGroupPosition getRelationshipGroup( NodeRecord node, int type, RecordAccess<RelationshipGroupRecord, Integer> relGroupRecords ) { long groupId = node.getNextRel(); long previousGroupId = Record.NO_NEXT_RELATIONSHIP.intValue(); RecordProxy<RelationshipGroupRecord, Integer> previous = null; RecordProxy<RelationshipGroupRecord, Integer> current; while ( groupId != Record.NO_NEXT_RELATIONSHIP.intValue() ) { current = relGroupRecords.getOrLoad( groupId, null ); RelationshipGroupRecord record = current.forReadingData(); record.setPrev( previousGroupId ); // not persistent so not a "change" if ( record.getType() == type ) { return new RelationshipGroupPosition( previous, current ); } else if ( record.getType() > type ) { // The groups are sorted in the chain, so if we come too far we can return // empty handed right away return new RelationshipGroupPosition( previous, null ); } previousGroupId = groupId; groupId = record.getNext(); previous = current; } return new RelationshipGroupPosition( previous, null ); }
private void assertRelationshipGroupsInOrder( NeoStores neoStores, long nodeId, int... types ) { NodeStore nodeStore = neoStores.getNodeStore(); NodeRecord node = nodeStore.getRecord( nodeId, nodeStore.newRecord(), NORMAL ); assertTrue( "Node should be dense, is " + node, node.isDense() ); long groupId = node.getNextRel(); int cursor = 0; List<RelationshipGroupRecord> seen = new ArrayList<>(); while ( groupId != Record.NO_NEXT_RELATIONSHIP.intValue() ) { RecordStore<RelationshipGroupRecord> relationshipGroupStore = neoStores.getRelationshipGroupStore(); RelationshipGroupRecord group = relationshipGroupStore.getRecord( groupId, relationshipGroupStore.newRecord(), NORMAL ); seen.add( group ); assertEquals( "Invalid type, seen groups so far " + seen, types[cursor++], group.getType() ); groupId = group.getNext(); } assertEquals( "Not enough relationship group records found in chain for " + node, types.length, cursor ); }
private void markRelGroupNotInUse( long nodeId, TestRelType... types ) { NodeRecord node = getNodeRecord( nodeId ); assertTrue( node.isDense() ); Set<TestRelType> typesToRemove = asSet( types ); long relGroupId = node.getNextRel(); while ( relGroupId != NO_NEXT_RELATIONSHIP.intValue() ) { RelationshipGroupRecord relGroup = getRelGroupRecord( relGroupId ); TestRelType type = relTypeForId( relGroup.getType() ); if ( typesToRemove.contains( type ) ) { relGroup.setInUse( false ); update( relGroup ); } relGroupId = relGroup.getNext(); } }
@Test public void cloneShouldProduceExactCopy() { // Given long relId = 1337L; long propId = 1338L; long inlinedLabels = 12L; NodeRecord node = new NodeRecord( 1L, false, relId, propId ); node.setLabelField( inlinedLabels, asList( new DynamicRecord( 1L ), new DynamicRecord( 2L ) ) ); node.setInUse( true ); // When NodeRecord clone = node.clone(); // Then assertEquals( node.inUse(), clone.inUse() ); assertEquals( node.getLabelField(), clone.getLabelField() ); assertEquals( node.getNextProp(), clone.getNextProp() ); assertEquals( node.getNextRel(), clone.getNextRel() ); assertThat( clone.getDynamicLabelRecords(), equalTo( node.getDynamicLabelRecords() ) ); }
private static RecordProxy<RelationshipGroupRecord, Integer> getRelationshipGroup( RecordChangeSet recordChangeSet, NodeRecord node, int type ) { long groupId = node.getNextRel(); long previousGroupId = Record.NO_NEXT_RELATIONSHIP.intValue(); while ( groupId != Record.NO_NEXT_RELATIONSHIP.intValue() ) { RecordProxy<RelationshipGroupRecord, Integer> change = recordChangeSet.getRelGroupRecords().getOrLoad( groupId, type ); RelationshipGroupRecord record = change.forReadingData(); record.setPrev( previousGroupId ); // not persistent so not a "change" if ( record.getType() == type ) { return change; } previousGroupId = groupId; groupId = record.getNext(); } return null; }
private void markRandomRelsInGroupNotInUse( long nodeId, TestRelType type ) { NodeRecord node = getNodeRecord( nodeId ); assertTrue( node.isDense() ); long relGroupId = node.getNextRel(); while ( relGroupId != NO_NEXT_RELATIONSHIP.intValue() ) { RelationshipGroupRecord relGroup = getRelGroupRecord( relGroupId ); if ( type == relTypeForId( relGroup.getType() ) ) { markRandomRelsInChainNotInUse( relGroup.getFirstOut() ); markRandomRelsInChainNotInUse( relGroup.getFirstIn() ); markRandomRelsInChainNotInUse( relGroup.getFirstLoop() ); return; } relGroupId = relGroup.getNext(); } throw new IllegalStateException( "No relationship group with type: " + type + " found" ); }
private void writeNodeRecord( WritableChannel channel, NodeRecord record ) throws IOException { byte flags = bitFlags( bitFlag( record.inUse(), Record.IN_USE.byteValue() ), bitFlag( record.isCreated(), Record.CREATED_IN_TX ), bitFlag( record.requiresSecondaryUnit(), Record.REQUIRE_SECONDARY_UNIT ), bitFlag( record.hasSecondaryUnitId(), Record.HAS_SECONDARY_UNIT ), bitFlag( record.isUseFixedReferences(), Record.USES_FIXED_REFERENCE_FORMAT ) ); channel.put( flags ); if ( record.inUse() ) { channel.put( record.isDense() ? (byte) 1 : (byte) 0 ); channel.putLong( record.getNextRel() ).putLong( record.getNextProp() ); channel.putLong( record.getLabelField() ); if ( record.hasSecondaryUnitId() ) { channel.putLong( record.getSecondaryUnitId() ); } } // Always write dynamic label records because we want to know which ones have been deleted // especially if the node has been deleted. writeDynamicRecords( channel, record.getDynamicLabelRecords() ); } }
@Test public void verifyGroupIsDeletedWhenNeeded() { // TODO test on a lower level instead newDb( 2 ); Transaction tx = db.beginTx(); Node node = db.createNode(); Relationship rel1 = node.createRelationshipTo( db.createNode(), MyRelTypes.TEST ); Relationship rel2 = node.createRelationshipTo( db.createNode(), MyRelTypes.TEST ); Relationship rel3 = node.createRelationshipTo( db.createNode(), MyRelTypes.TEST ); Relationship rel4 = node.createRelationshipTo( db.createNode(), MyRelTypes.TEST2 ); Relationship rel5 = node.createRelationshipTo( db.createNode(), MyRelTypes.TEST2 ); Relationship rel6 = node.createRelationshipTo( db.createNode(), MyRelTypes.TEST2 ); tx.success(); tx.close(); NeoStores neoStores = db.getDependencyResolver().resolveDependency( RecordStorageEngine.class ).testAccessNeoStores(); NodeStore nodeStore = neoStores.getNodeStore(); NodeRecord nodeRecord = getRecord( nodeStore, node.getId() ); long group = nodeRecord.getNextRel(); RecordStore<RelationshipGroupRecord> groupStore = neoStores.getRelationshipGroupStore(); RelationshipGroupRecord groupRecord = getRecord( groupStore, group ); assertNotEquals( groupRecord.getNext(), -1 ); RelationshipGroupRecord otherGroupRecord = groupStore.getRecord( groupRecord.getNext(), groupStore.newRecord(), NORMAL ); assertEquals( -1, otherGroupRecord.getNext() ); // TODO Delete all relationships of one type and see to that the correct group is deleted. }