use of org.neo4j.kernel.impl.store.record.RelationshipRecord in project neo4j by neo4j.
the class RelationshipDeleter method relationshipDelete.
/**
* Deletes relationships found in {@code ids}.
* Here we assume that all required locks are taken do do the changes needed
*
* @param deletions The ids of all relationships to delete in this transaction.
* @param recordChanges {@link RecordAccessSet} to coordinate and keep changes to records.
* @param groupDegreesUpdater for recording updates to degrees for the degrees store.
* @param nodeDataLookup additional lookup for groups.
* @param locks {@link ResourceLocker} for optimistic locking for deleting groups.
*/
void relationshipDelete(RelationshipBatch deletions, RecordAccessSet recordChanges, RelationshipGroupDegreesStore.Updater groupDegreesUpdater, MappedNodeDataLookup nodeDataLookup, ResourceLocker locks) {
deletions.forEach((id, type, startNode, endNode) -> {
RelationshipRecord record = recordChanges.getRelRecords().getOrLoad(id, null, cursorContext).forChangingLinkage();
propertyChainDeleter.deletePropertyChain(record, recordChanges.getPropertyRecords());
disconnectRelationship(record, recordChanges.getRelRecords());
updateNodesForDeletedRelationship(record, recordChanges, groupDegreesUpdater, nodeDataLookup, locks);
record.setInUse(false);
record.setType(-1);
});
}
use of org.neo4j.kernel.impl.store.record.RelationshipRecord in project neo4j by neo4j.
the class RelationshipLockHelper method findAndLockInsertionPoint.
/**
* Traverses a relationship chain and tries to exclusively lock two consecutive relationships, and if successful that location
* can be used as an insertion point for new relationships.
*
* @param firstInChain first relationship to start traversing from.
* @param nodeId node id, i.e. which side of the relationship to follow when traversing.
* @param relRecords for coordinate changes in.
* @param locks used to try and lock the relationships.
* @param lockTracer to go with the locks.
* @param cursorContext for tracing page cache access.
* @return the insertion point, if found. Otherwise {@code null}.
*/
static RecordAccess.RecordProxy<RelationshipRecord, Void> findAndLockInsertionPoint(long firstInChain, long nodeId, RecordAccess<RelationshipRecord, Void> relRecords, ResourceLocker locks, LockTracer lockTracer, CursorContext cursorContext) {
long nextRel = firstInChain;
RecordAccess.RecordProxy<RelationshipRecord, Void> rBefore = null;
// Start walking the relationship chain and see where we can insert it
if (!isNull(nextRel)) {
while (!isNull(nextRel)) {
boolean r1Locked = locks.tryExclusiveLock(RELATIONSHIP, nextRel);
RecordAccess.RecordProxy<RelationshipRecord, Void> r1 = relRecords.getOrLoad(nextRel, null, ALWAYS, cursorContext);
RelationshipRecord r1Record = r1.forReadingLinkage();
if (!r1Locked || !r1Record.inUse()) {
nextRel = r1Record.getNextRel(nodeId);
if (r1Locked) {
locks.releaseExclusive(RELATIONSHIP, r1.getKey());
}
continue;
}
long r2Id = r1Record.getNextRel(nodeId);
if (!isNull(r2Id)) {
boolean r2Locked = locks.tryExclusiveLock(RELATIONSHIP, r2Id);
RecordAccess.RecordProxy<RelationshipRecord, Void> r2 = relRecords.getOrLoad(r2Id, null, ALWAYS, cursorContext);
RelationshipRecord r2Record = r2.forReadingLinkage();
if (!r2Locked || !r2Record.inUse()) {
nextRel = r2Record.getNextRel(nodeId);
locks.releaseExclusive(RELATIONSHIP, r1.getKey());
if (r2Locked) {
locks.releaseExclusive(RELATIONSHIP, r2.getKey());
}
continue;
}
// We can insert in between r1 and r2 here
}
// We can insert at the end here
rBefore = r1;
break;
}
if (rBefore == null) {
// Group is minimum read locked, so no need to re-read
locks.acquireExclusive(lockTracer, RELATIONSHIP, firstInChain);
RecordAccess.RecordProxy<RelationshipRecord, Void> firstProxy = relRecords.getOrLoad(firstInChain, null, ALWAYS, cursorContext);
long secondRel = firstProxy.forReadingLinkage().getNextRel(nodeId);
if (!isNull(secondRel)) {
locks.acquireExclusive(lockTracer, RELATIONSHIP, secondRel);
}
rBefore = firstProxy;
}
}
return rBefore;
}
use of org.neo4j.kernel.impl.store.record.RelationshipRecord in project neo4j by neo4j.
the class RelationshipLockHelper method lockMultipleRelationships.
private static void lockMultipleRelationships(RelationshipModifications.RelationshipBatch ids, long optionalFirstInChain, RecordAccess<RelationshipRecord, Void> relRecords, ResourceLocker locks, CursorContext cursorContext, MemoryTracker memoryTracker) {
/*
* The idea here is to take all locks in sorted order to avoid deadlocks
* We start locking by optimistic reading of relationship neighbours
* Once all neighbours plus the relationship itself is locked, we verify if there are changes.
* If not -> then just continue
* Changes means we need to rewind and unlock, to get the new changes in correct order
*/
int upperLimitOfLocks = ids.size() * 5 + /* self and 4 neighbours */
1;
try (MemoryTracker scopedMemoryTracker = memoryTracker.getScopedMemoryTracker()) {
HeapTrackingLongObjectHashMap<RelationshipRecord> optimistic = HeapTrackingCollections.newLongObjectMap(scopedMemoryTracker);
scopedMemoryTracker.allocateHeap(sizeOfLongArray(upperLimitOfLocks));
// First will build the list of locks we need
SortedLockList lockList = new SortedLockList(upperLimitOfLocks);
// The locklist does not accept NULL(-1) values, so we don't need to care about that
lockList.add(optionalFirstInChain);
ids.forEach((id, type, startNode, endNode) -> {
RelationshipRecord relationship = relRecords.getOrLoad(id, null, cursorContext).forReadingLinkage();
optimistic.put(id, relationship);
lockList.add(relationship.getId());
lockList.add(START_NEXT.get(relationship));
lockList.add(START_PREV.get(relationship));
lockList.add(END_NEXT.get(relationship));
lockList.add(END_PREV.get(relationship));
});
// Then we start traversing and locking
while (lockList.nextUnique()) {
long id = lockList.currentHighestLockedId();
// This could be either a relationship we're deleting, a neighbour or the first-in-chain. They all needs to be locked
locks.acquireExclusive(NONE, RELATIONSHIP, id);
RelationshipRecord old = optimistic.get(id);
if (old != null) {
// This is a relationship we we're deleting
// No when it is locked we can check if the optimistic read is stable
RelationshipRecord actual = relRecords.getOrLoad(id, null, cursorContext).forReadingLinkage();
if (recordHasLinkageChanges(old, actual)) {
// Something changed, so we need to retry, by unlocking and trying again
rewindAndUnlockChanged(locks, lockList, old, actual);
optimistic.put(id, actual);
}
}
}
}
}
use of org.neo4j.kernel.impl.store.record.RelationshipRecord in project neo4j by neo4j.
the class RelationshipModifier method checkAndLockRelationshipsIfNodeIsGoingToBeDense.
private boolean checkAndLockRelationshipsIfNodeIsGoingToBeDense(NodeRecord node, RelationshipModifications.NodeRelationshipIds byNode, RecordAccess<RelationshipRecord, Void> relRecords, ResourceLocker locks, LockTracer lockTracer) {
// We have an exclusively locked sparse not that may turn dense
long nextRel = node.getNextRel();
if (!isNull(nextRel)) {
RelationshipRecord rel = relRecords.getOrLoad(nextRel, null, cursorContext).forReadingData();
long nodeId = node.getId();
if (!rel.isFirstInChain(nodeId)) {
throw new IllegalStateException("Expected node " + rel + " to be first in chain for node " + nodeId);
}
int currentDegree = relCount(nodeId, rel);
if (currentDegree + byNode.creations().size() >= denseNodeThreshold) {
// The current length plus our additions in this transaction is above threshold, it will be converted so we need to lock all the relationships
// Since it is sparse and locked we can trust this chain read to be stable
// find all id's and lock them as we will create new chains based on type and direction
MutableLongList ids = LongLists.mutable.withInitialCapacity(currentDegree);
do {
ids.add(nextRel);
nextRel = relRecords.getOrLoad(nextRel, null, cursorContext).forReadingData().getNextRel(nodeId);
} while (!isNull(nextRel));
locks.acquireExclusive(lockTracer, RELATIONSHIP, ids.toSortedArray());
return true;
}
}
return false;
}
use of org.neo4j.kernel.impl.store.record.RelationshipRecord in project neo4j by neo4j.
the class RelationshipCreator method convertNodeToDenseIfNecessary.
private void convertNodeToDenseIfNecessary(RecordProxy<NodeRecord, Void> nodeChange, RecordAccess<RelationshipRecord, Void> relRecords, RelationshipGroupDegreesStore.Updater groupDegreesUpdater, NodeDataLookup nodeDataLookup) {
NodeRecord node = nodeChange.forReadingLinkage();
if (node.isDense()) {
return;
}
long relId = node.getNextRel();
if (!isNull(relId)) {
RecordProxy<RelationshipRecord, Void> relProxy = relRecords.getOrLoad(relId, null, cursorContext);
if (relCount(node.getId(), relProxy.forReadingData()) >= denseNodeThreshold) {
convertNodeToDenseNode(nodeChange, relProxy.forChangingLinkage(), relRecords, groupDegreesUpdater, nodeDataLookup);
}
}
}
Aggregations