use of org.neo4j.io.pagecache.context.CursorContext in project neo4j by neo4j.
the class RelationshipGroupChecker method checkToRelationship.
/**
* Check relationship groups to first in chain relationship. Run only on first node-range
*/
private void checkToRelationship(long fromGroupId, long toGroupId, PageCacheTracer pageCacheTracer) {
try (var cursorContext = new CursorContext(pageCacheTracer.createPageCursorTracer(RELATIONSHIP_GROUPS_CHECKER_TAG));
RecordReader<RelationshipGroupRecord> groupReader = new RecordReader<>(neoStores.getRelationshipGroupStore(), true, cursorContext);
RecordReader<RelationshipGroupRecord> comparativeReader = new RecordReader<>(neoStores.getRelationshipGroupStore(), false, cursorContext);
RecordStorageReader reader = new RecordStorageReader(neoStores);
RecordRelationshipScanCursor relationshipCursor = reader.allocateRelationshipScanCursor(cursorContext)) {
for (long id = fromGroupId; id < toGroupId && !context.isCancelled(); id++) {
RelationshipGroupRecord record = groupReader.read(id);
if (!record.inUse()) {
continue;
}
long owningNode = record.getOwningNode();
if (owningNode < 0) {
reporter.forRelationshipGroup(record).illegalOwner();
}
checkValidToken(record, record.getType(), context.tokenHolders.relationshipTypeTokens(), neoStores.getRelationshipTypeTokenStore(), (group, token) -> reporter.forRelationshipGroup(group).illegalRelationshipType(), (group, token) -> reporter.forRelationshipGroup(group).relationshipTypeNotInUse(token), cursorContext);
if (!NULL_REFERENCE.is(record.getNext())) {
RelationshipGroupRecord comparativeRecord = comparativeReader.read(record.getNext());
if (!comparativeRecord.inUse()) {
reporter.forRelationshipGroup(record).nextGroupNotInUse();
} else {
if (record.getType() >= comparativeRecord.getType()) {
reporter.forRelationshipGroup(record).invalidTypeSortOrder();
}
if (owningNode != comparativeRecord.getOwningNode()) {
reporter.forRelationshipGroup(record).nextHasOtherOwner(comparativeRecord);
}
}
}
checkRelationshipGroupRelationshipLink(relationshipCursor, record, record.getFirstOut(), RelationshipGroupLink.OUT, group -> reporter.forRelationshipGroup(group).firstOutgoingRelationshipNotInUse(), group -> reporter.forRelationshipGroup(group).firstOutgoingRelationshipNotFirstInChain(), group -> reporter.forRelationshipGroup(group).firstOutgoingRelationshipOfOtherType(), (group, rel) -> reporter.forRelationshipGroup(group).firstOutgoingRelationshipDoesNotShareNodeWithGroup(rel), cursorContext);
checkRelationshipGroupRelationshipLink(relationshipCursor, record, record.getFirstIn(), RelationshipGroupLink.IN, group -> reporter.forRelationshipGroup(group).firstIncomingRelationshipNotInUse(), group -> reporter.forRelationshipGroup(group).firstIncomingRelationshipNotFirstInChain(), group -> reporter.forRelationshipGroup(group).firstIncomingRelationshipOfOtherType(), (group, rel) -> reporter.forRelationshipGroup(group).firstIncomingRelationshipDoesNotShareNodeWithGroup(rel), cursorContext);
checkRelationshipGroupRelationshipLink(relationshipCursor, record, record.getFirstLoop(), RelationshipGroupLink.LOOP, group -> reporter.forRelationshipGroup(group).firstLoopRelationshipNotInUse(), group -> reporter.forRelationshipGroup(group).firstLoopRelationshipNotFirstInChain(), group -> reporter.forRelationshipGroup(group).firstLoopRelationshipOfOtherType(), (group, rel) -> reporter.forRelationshipGroup(group).firstLoopRelationshipDoesNotShareNodeWithGroup(rel), cursorContext);
}
}
}
use of org.neo4j.io.pagecache.context.CursorContext in project neo4j by neo4j.
the class SchemaChecker method checkTokens.
private static <R extends TokenRecord> void checkTokens(TokenStore<R> store, Function<R, ConsistencyReport.NameConsistencyReport> report, Function<DynamicRecord, ConsistencyReport.DynamicConsistencyReport> dynamicRecordReport, PageCacheTracer pageCacheTracer) {
DynamicStringStore nameStore = store.getNameStore();
DynamicRecord nameRecord = nameStore.newRecord();
long highId = store.getHighId();
MutableLongSet seenNameRecordIds = LongSets.mutable.empty();
int blockSize = store.getNameStore().getRecordDataSize();
try (var cursorContext = new CursorContext(pageCacheTracer.createPageCursorTracer(CONSISTENCY_TOKEN_CHECKER_TAG));
RecordReader<R> tokenReader = new RecordReader<>(store, true, cursorContext);
RecordReader<DynamicRecord> nameReader = new RecordReader<>(store.getNameStore(), false, cursorContext)) {
for (long id = 0; id < highId; id++) {
R record = tokenReader.read(id);
if (record.inUse() && !NULL_REFERENCE.is(record.getNameId())) {
safeLoadDynamicRecordChain(r -> {
}, nameReader, seenNameRecordIds, record.getNameId(), blockSize, (i, r) -> dynamicRecordReport.apply(nameRecord).circularReferenceNext(r), (i, r) -> report.apply(record).nameBlockNotInUse(nameRecord), (i, r) -> dynamicRecordReport.apply(nameRecord).nextNotInUse(r), (i, r) -> dynamicRecordReport.apply(r).emptyBlock(), r -> dynamicRecordReport.apply(r).recordNotFullReferencesNext(), r -> dynamicRecordReport.apply(r).invalidLength());
}
}
}
}
use of org.neo4j.io.pagecache.context.CursorContext in project neo4j by neo4j.
the class AllStoreHolder method countsForRelationshipWithoutTxState.
@Override
public long countsForRelationshipWithoutTxState(int startLabelId, int typeId, int endLabelId) {
AccessMode mode = ktx.securityContext().mode();
CursorContext cursorContext = ktx.cursorContext();
if (mode.allowsTraverseRelType(typeId) && mode.allowsTraverseNode(startLabelId) && mode.allowsTraverseNode(endLabelId)) {
return storageReader.countsForRelationship(startLabelId, typeId, endLabelId, cursorContext);
}
if (mode.disallowsTraverseRelType(typeId) || mode.disallowsTraverseLabel(startLabelId) || mode.disallowsTraverseLabel(endLabelId)) {
// so the count will be 0.
return 0;
}
// token index scan can only scan for single relationship type
if (typeId != TokenRead.ANY_RELATIONSHIP_TYPE) {
try {
var index = findUsableTokenIndex(EntityType.RELATIONSHIP);
if (index != IndexDescriptor.NO_INDEX) {
long count = 0;
try (DefaultRelationshipTypeIndexCursor relationshipsWithType = cursors.allocateRelationshipTypeIndexCursor(cursorContext);
DefaultRelationshipScanCursor relationship = cursors.allocateRelationshipScanCursor(cursorContext);
DefaultNodeCursor sourceNode = cursors.allocateNodeCursor(cursorContext);
DefaultNodeCursor targetNode = cursors.allocateNodeCursor(cursorContext)) {
var session = tokenReadSession(index);
this.relationshipTypeScan(session, relationshipsWithType, unconstrained(), new TokenPredicate(typeId));
while (relationshipsWithType.next()) {
relationshipsWithType.relationship(relationship);
count += countRelationshipsWithEndLabels(relationship, sourceNode, targetNode, startLabelId, endLabelId);
}
}
return count - countsForRelationshipInTxState(startLabelId, typeId, endLabelId);
}
} catch (KernelException ignored) {
// ignore, fallback to allRelationshipsScan
}
}
long count;
try (DefaultRelationshipScanCursor rels = cursors.allocateRelationshipScanCursor(cursorContext);
DefaultNodeCursor sourceNode = cursors.allocateFullAccessNodeCursor(cursorContext);
DefaultNodeCursor targetNode = cursors.allocateFullAccessNodeCursor(cursorContext)) {
this.allRelationshipsScan(rels);
Predicate<RelationshipScanCursor> predicate = typeId == TokenRead.ANY_RELATIONSHIP_TYPE ? alwaysTrue() : CursorPredicates.hasType(typeId);
var filteredCursor = new FilteringRelationshipScanCursorWrapper(rels, predicate);
count = countRelationshipsWithEndLabels(filteredCursor, sourceNode, targetNode, startLabelId, endLabelId);
}
return count - countsForRelationshipInTxState(startLabelId, typeId, endLabelId);
}
use of org.neo4j.io.pagecache.context.CursorContext in project neo4j by neo4j.
the class RelationshipModifier method acquireMostOfTheNodeAndGroupsLocks.
private void acquireMostOfTheNodeAndGroupsLocks(RelationshipModifications modifications, RecordAccessSet recordChanges, ResourceLocker locks, LockTracer lockTracer, MutableLongObjectMap<NodeContext> contexts, MappedNodeDataLookup nodeDataLookup) {
/* Here we're going to figure out if we need to make changes to any node and/or relationship group records and lock them if we do. */
// We check modifications for each node, it might need locking. The iteration here is always sorted by node id
modifications.forEachSplit(byNode -> {
long nodeId = byNode.nodeId();
RecordProxy<NodeRecord, Void> nodeProxy = recordChanges.getNodeRecords().getOrLoad(nodeId, null, cursorContext);
// optimistic (unlocked) read
NodeRecord node = nodeProxy.forReadingLinkage();
boolean nodeIsAddedInTx = node.isCreated();
if (// we can not trust this as the node is not locked
!node.isDense()) {
if (// to avoid locking unnecessarily
!nodeIsAddedInTx) {
// lock and re-read, now we can trust it
locks.acquireExclusive(lockTracer, NODE, nodeId);
nodeProxy = recordChanges.getNodeRecords().getOrLoad(nodeId, null, cursorContext);
node = nodeProxy.forReadingLinkage();
if (node.isDense()) {
// another transaction just turned this node dense, unlock and let it be handled below
locks.releaseExclusive(NODE, nodeId);
} else if (byNode.hasCreations()) {
// Sparse node with added relationships. We might turn this node dense, at which point the group lock will be needed, so lock it
locks.acquireExclusive(lockTracer, RELATIONSHIP_GROUP, nodeId);
}
}
}
if (// the node is not locked but the dense node is a one-way transform so we can trust it
node.isDense()) {
// Stabilize first in chains, in case they are deleted or needed for chain degrees.
// We are preventing any changes to the group which in turn blocks any other relationship becomming the first in chain
locks.acquireShared(lockTracer, RELATIONSHIP_GROUP, nodeId);
// Creations
NodeContext nodeContext = NodeContext.createNodeContext(nodeProxy, memoryTracker);
contexts.put(nodeId, nodeContext);
if (byNode.hasCreations()) {
// We have some creations on a dense node. If the group exists we can use that, otherwise we create it
byNode.forEachCreationSplit(byType -> {
RelationshipGroupGetter.RelationshipGroupPosition groupPosition = findRelationshipGroup(recordChanges, nodeContext, byType);
nodeContext.setCurrentGroup(groupPosition.group() != null ? groupPosition.group() : groupPosition.closestPrevious());
RecordProxy<RelationshipGroupRecord, Integer> groupProxy = groupPosition.group();
if (groupProxy == null) {
// The group did not exist
if (!nodeContext.hasExclusiveGroupLock()) {
// And we did not already have the lock, so we need to upgrade to exclusive create it
locks.releaseShared(RELATIONSHIP_GROUP, nodeId);
// Note the small window here where we dont hold any group lock, things might change so we can not trust previous group reads
locks.acquireExclusive(lockTracer, NODE, nodeId);
locks.acquireExclusive(lockTracer, RELATIONSHIP_GROUP, nodeId);
}
nodeContext.setNode(recordChanges.getNodeRecords().getOrLoad(nodeId, null, cursorContext));
long groupStartingId = nodeContext.node().forReadingLinkage().getNextRel();
long groupStartingPrevId = NULL_REFERENCE.longValue();
if (groupPosition.closestPrevious() != null) {
groupStartingId = groupPosition.closestPrevious().getKey();
groupStartingPrevId = groupPosition.closestPrevious().forReadingLinkage().getPrev();
}
// At this point the group is locked so we can create it
groupProxy = relGroupGetter.getOrCreateRelationshipGroup(nodeContext.node(), byType.type(), recordChanges.getRelGroupRecords(), groupStartingPrevId, groupStartingId);
// another transaction might beat us at this point, so we are not guaranteed to be the creator but we can trust it to exist
if (!nodeContext.hasExclusiveGroupLock()) {
nodeContext.markExclusiveGroupLock();
} else if (groupProxy.isCreated()) {
// When a new group is created we can no longer trust the pointers of the cache
nodeContext.clearDenseContext();
}
}
nodeContext.denseContext(byType.type()).setGroup(groupProxy);
});
if (!nodeContext.hasExclusiveGroupLock()) {
// No other path has given us the exclusive lock yet
byNode.forEachCreationSplitInterruptible(byType -> {
// But if we are creating relationships to a chain that does not exist on the group
// or we might need to flip the external degrees flag
RelationshipGroupRecord group = nodeContext.denseContext(byType.type()).group().forReadingLinkage();
if (byType.hasOut() && (!group.hasExternalDegreesOut() || isNull(group.getFirstOut())) || byType.hasIn() && (!group.hasExternalDegreesIn() || isNull(group.getFirstIn())) || byType.hasLoop() && (!group.hasExternalDegreesLoop() || isNull(group.getFirstLoop()))) {
// Then we need the exclusive lock to change it
locks.releaseShared(RELATIONSHIP_GROUP, nodeId);
// Note the small window here where we dont hold any group lock, things might change so we can not trust previous group reads
locks.acquireExclusive(lockTracer, RELATIONSHIP_GROUP, nodeId);
nodeContext.markExclusiveGroupLock();
// And we can abort the iteration as the group lock is protecting all relationship group records of the node
return true;
}
return false;
});
}
}
// Deletions
if (byNode.hasDeletions()) {
if (// no need to do anything if it is already locked by additions
!nodeContext.hasExclusiveGroupLock()) {
byNode.forEachDeletionSplitInterruptible(byType -> {
NodeContext.DenseContext denseContext = nodeContext.denseContext(byType.type());
RelationshipGroupRecord group = denseContext.getOrLoadGroup(relGroupGetter, nodeContext.node().forReadingLinkage(), byType.type(), recordChanges.getRelGroupRecords(), cursorContext);
// here we have the shared lock, so we can trust the read
if (byType.hasOut() && !group.hasExternalDegreesOut() || byType.hasIn() && !group.hasExternalDegreesIn() || byType.hasLoop() && !group.hasExternalDegreesLoop()) {
// We have deletions but without external degrees, we might need to flip that so we lock it
locks.releaseShared(RELATIONSHIP_GROUP, nodeId);
// Note the small window here where we dont hold any group lock, things might change so we can not trust previous group reads
locks.acquireExclusive(lockTracer, RELATIONSHIP_GROUP, nodeId);
nodeContext.markExclusiveGroupLock();
return true;
} else {
// We have deletions and only external degrees
boolean hasAnyFirst = batchContains(byType.out(), group.getFirstOut()) || batchContains(byType.in(), group.getFirstIn()) || batchContains(byType.loop(), group.getFirstLoop());
if (hasAnyFirst) {
// But we're deleting the first in the chain so the group needs to be updated
locks.releaseShared(RELATIONSHIP_GROUP, nodeId);
// Note the small window here where we dont hold any group lock, things might change so we can not trust previous group reads
locks.acquireExclusive(lockTracer, RELATIONSHIP_GROUP, nodeId);
nodeContext.markExclusiveGroupLock();
return true;
}
}
return false;
});
}
}
// Look for an opportunity to delete empty groups that we noticed while looking for groups above
if (nodeContext.hasExclusiveGroupLock() && nodeContext.hasAnyEmptyGroup()) {
// There may be one or more empty groups that we can delete
if (locks.tryExclusiveLock(NODE_RELATIONSHIP_GROUP_DELETE, nodeId)) {
// We got the EXCLUSIVE group lock so we can go ahead and try to remove any potentially empty groups
if (!nodeContext.hasEmptyFirstGroup() || locks.tryExclusiveLock(NODE, nodeId)) {
if (nodeContext.hasEmptyFirstGroup()) {
// It's possible that we need to delete the first group, i.e. we just now locked the node and therefore need to re-read it
nodeContext.setNode(recordChanges.getNodeRecords().getOrLoad(nodeId, null, cursorContext));
}
Predicate<RelationshipGroupRecord> canDeleteGroup = group -> !byNode.hasCreations(group.getType());
if (RelationshipGroupGetter.deleteEmptyGroups(nodeContext.node(), canDeleteGroup, nodeDataLookup)) {
nodeContext.clearDenseContext();
}
}
}
}
}
});
}
use of org.neo4j.io.pagecache.context.CursorContext in project neo4j by neo4j.
the class SnapshotExecutionEngineTest method setUp.
@BeforeEach
void setUp() throws Exception {
transactionalContext = mock(TransactionalContext.class, RETURNS_DEEP_STUBS);
KernelStatement kernelStatement = mock(KernelStatement.class);
executor = mock(SnapshotExecutionEngine.QueryExecutor.class);
versionContext = mock(VersionContext.class);
statistics = mock(QueryStatistics.class);
executionEngine = new SnapshotExecutionEngine(new GraphDatabaseCypherService(db), config, TestExecutorCaffeineCacheFactory$.MODULE$, NullLogProvider.getInstance(), mock(CompilerFactory.class));
when(transactionalContext.kernelTransaction().cursorContext()).thenReturn(new CursorContext(NULL, versionContext));
when(transactionalContext.statement()).thenReturn(kernelStatement);
var innerExecution = mock(QueryExecution.class);
when(executor.execute(any())).thenAnswer((Answer<QueryExecution>) invocationOnMock -> {
MaterialisedResult materialisedResult = invocationOnMock.getArgument(0);
materialisedResult.onResultCompleted(statistics);
return innerExecution;
});
}
Aggregations