use of org.neo4j.internal.recordstorage.RelationshipCounter in project neo4j by neo4j.
the class RelationshipChecker method check.
private void check(LongRange nodeIdRange, boolean firstRound, long fromRelationshipId, long toRelationshipId, boolean checkToEndOfIndex) throws Exception {
RelationshipCounter counter = observedCounts.instantiateRelationshipCounter();
long[] typeHolder = new long[1];
try (var cursorContext = new CursorContext(context.pageCacheTracer.createPageCursorTracer(RELATIONSHIP_RANGE_CHECKER_TAG));
RecordReader<RelationshipRecord> relationshipReader = new RecordReader<>(context.neoStores.getRelationshipStore(), true, cursorContext);
BoundedIterable<EntityTokenRange> relationshipTypeReader = getRelationshipTypeIndexReader(fromRelationshipId, toRelationshipId, checkToEndOfIndex, cursorContext);
SafePropertyChainReader property = new SafePropertyChainReader(context, cursorContext);
SchemaComplianceChecker schemaComplianceChecker = new SchemaComplianceChecker(context, mandatoryProperties, indexes, cursorContext, context.memoryTracker)) {
ProgressListener localProgress = progress.threadLocalReporter();
CacheAccess.Client client = cacheAccess.client();
MutableIntObjectMap<Value> propertyValues = new IntObjectHashMap<>();
Iterator<EntityTokenRange> relationshipTypeRangeIterator = relationshipTypeReader.iterator();
EntityTokenIndexCheckState typeIndexState = new EntityTokenIndexCheckState(null, fromRelationshipId - 1);
for (long relationshipId = fromRelationshipId; relationshipId < toRelationshipId && !context.isCancelled(); relationshipId++) {
localProgress.add(1);
RelationshipRecord relationshipRecord = relationshipReader.read(relationshipId);
if (!relationshipRecord.inUse()) {
continue;
}
// Start/end nodes
long startNode = relationshipRecord.getFirstNode();
boolean startNodeIsWithinRange = nodeIdRange.isWithinRangeExclusiveTo(startNode);
boolean startNodeIsNegativeOnFirstRound = startNode < 0 && firstRound;
if (startNodeIsWithinRange || startNodeIsNegativeOnFirstRound) {
checkRelationshipVsNode(client, relationshipRecord, startNode, relationshipRecord.isFirstInFirstChain(), (relationship, node) -> reporter.forRelationship(relationship).sourceNodeNotInUse(node), (relationship, node) -> reporter.forRelationship(relationship).sourceNodeDoesNotReferenceBack(node), (relationship, node) -> reporter.forNode(node).relationshipNotFirstInSourceChain(relationship), (relationship, node) -> reporter.forRelationship(relationship).sourceNodeHasNoRelationships(node), relationship -> reporter.forRelationship(relationship).illegalSourceNode(), cursorContext);
}
long endNode = relationshipRecord.getSecondNode();
boolean endNodeIsWithinRange = nodeIdRange.isWithinRangeExclusiveTo(endNode);
boolean endNodeIsNegativeOnFirstRound = endNode < 0 && firstRound;
if (endNodeIsWithinRange || endNodeIsNegativeOnFirstRound) {
checkRelationshipVsNode(client, relationshipRecord, endNode, relationshipRecord.isFirstInSecondChain(), (relationship, node) -> reporter.forRelationship(relationship).targetNodeNotInUse(node), (relationship, node) -> reporter.forRelationship(relationship).targetNodeDoesNotReferenceBack(node), (relationship, node) -> reporter.forNode(node).relationshipNotFirstInTargetChain(relationship), (relationship, node) -> reporter.forRelationship(relationship).targetNodeHasNoRelationships(node), relationship -> reporter.forRelationship(relationship).illegalTargetNode(), cursorContext);
}
if (firstRound) {
if (startNode >= context.highNodeId) {
reporter.forRelationship(relationshipRecord).sourceNodeNotInUse(context.recordLoader.node(startNode, cursorContext));
}
if (endNode >= context.highNodeId) {
reporter.forRelationship(relationshipRecord).targetNodeNotInUse(context.recordLoader.node(endNode, cursorContext));
}
// Properties
typeHolder[0] = relationshipRecord.getType();
lightClear(propertyValues);
boolean propertyChainIsOk = property.read(propertyValues, relationshipRecord, reporter::forRelationship, cursorContext);
if (propertyChainIsOk) {
schemaComplianceChecker.checkContainsMandatoryProperties(relationshipRecord, typeHolder, propertyValues, reporter::forRelationship);
// gets checked this way, larger indexes will be checked in IndexChecker
if (context.consistencyFlags.isCheckIndexes()) {
schemaComplianceChecker.checkCorrectlyIndexed((RelationshipRecord) relationshipRecord, typeHolder, propertyValues, reporter::forRelationship);
}
}
// Type and count
checkValidToken(relationshipRecord, relationshipRecord.getType(), tokenHolders.relationshipTypeTokens(), neoStores.getRelationshipTypeTokenStore(), (rel, token) -> reporter.forRelationship(rel).illegalRelationshipType(), (rel, token) -> reporter.forRelationship(rel).relationshipTypeNotInUse(token), cursorContext);
observedCounts.incrementRelationshipTypeCounts(counter, relationshipRecord);
// Relationship type index
if (relationshipTypeReader.maxCount() != 0) {
checkRelationshipVsRelationshipTypeIndex(relationshipRecord, relationshipTypeRangeIterator, typeIndexState, relationshipId, relationshipRecord.getType(), fromRelationshipId, cursorContext);
}
}
observedCounts.incrementRelationshipNodeCounts(counter, relationshipRecord, startNodeIsWithinRange, endNodeIsWithinRange);
}
if (firstRound && !context.isCancelled() && relationshipTypeReader.maxCount() != 0) {
reportRemainingRelationshipTypeIndexEntries(relationshipTypeRangeIterator, typeIndexState, checkToEndOfIndex ? Long.MAX_VALUE : toRelationshipId, cursorContext);
}
localProgress.done();
}
}
use of org.neo4j.internal.recordstorage.RelationshipCounter in project neo4j by neo4j.
the class CountsState method checker.
CountsChecker checker(ConsistencyReporter reporter) {
return new CountsChecker() {
final RelationshipCounter relationshipCounter = instantiateRelationshipCounter();
@Override
public void visitNodeCount(int labelId, long count) {
if (isValidLabelId(labelId)) {
long pos = labelIdArrayPos(labelId);
long expected = unmarkCountVisited(nodeCounts.get(pos));
if (expected != count) {
reporter.forCounts(new CountsEntry(nodeKey(labelId), count)).inconsistentNodeCount(expected);
}
nodeCounts.set(pos, markCountVisited(expected));
} else {
AtomicLong expected = nodeCountsStray.remove(nodeKey(labelId));
if (expected != null) {
if (expected.longValue() != count) {
reporter.forCounts(new CountsEntry(nodeKey(labelId), count)).inconsistentNodeCount(expected.longValue());
}
} else {
reporter.forCounts(new CountsEntry(nodeKey(labelId), count)).inconsistentNodeCount(0);
}
}
}
@Override
public void visitRelationshipCount(int startLabelId, int relTypeId, int endLabelId, long count) {
CountsKey countsKey = relationshipKey(startLabelId, relTypeId, endLabelId);
if (relationshipCounter.isValid(startLabelId, relTypeId, endLabelId)) {
long expected = unmarkCountVisited(relationshipCounter.get(startLabelId, relTypeId, endLabelId));
if (expected != count) {
reporter.forCounts(new CountsEntry(countsKey, count)).inconsistentRelationshipCount(expected);
}
relationshipCounter.set(startLabelId, relTypeId, endLabelId, markCountVisited(expected));
} else {
AtomicLong expected = relationshipCountsStray.remove(countsKey);
if (expected != null) {
if (expected.longValue() != count) {
reporter.forCounts(new CountsEntry(countsKey, count)).inconsistentRelationshipCount(expected.longValue());
}
} else {
reporter.forCounts(new CountsEntry(countsKey, count)).inconsistentRelationshipCount(0);
}
}
}
@Override
public void close() {
// Report counts that have been collected in this consistency check, but aren't in the existing store
for (int labelId = 0; labelId < highLabelId; labelId++) {
long count = nodeCounts.get(labelId);
if (!hasVisitedCountMark(count) && count > 0) {
reporter.forCounts(new CountsEntry(nodeKey(labelId), 0)).inconsistentNodeCount(count);
}
}
for (int start = ANY_LABEL; start < highLabelId; start++) {
for (int end = ANY_LABEL; end < highLabelId; end++) {
for (int type = ANY_RELATIONSHIP_TYPE; type < highRelationshipTypeId; type++) {
if (// we only keep counts for where at least one of start/end is ANY
start == ANY_LABEL || end == ANY_LABEL) {
long count = relationshipCounter.get(start, type, end);
if (!hasVisitedCountMark(count) && count > 0) {
reporter.forCounts(new CountsEntry(relationshipKey(start, type, end), 0)).inconsistentRelationshipCount(count);
}
}
}
}
}
nodeCountsStray.forEach((countsKey, count) -> reporter.forCounts(new CountsEntry(countsKey, count.get())));
relationshipCountsStray.forEach((countsKey, count) -> reporter.forCounts(new CountsEntry(countsKey, count.get())));
}
};
}
use of org.neo4j.internal.recordstorage.RelationshipCounter in project neo4j by neo4j.
the class CountsStateTest method shouldReportRelationshipCountMismatches.
@Test
void shouldReportRelationshipCountMismatches() {
// when
RelationshipCounter counter = countsState.instantiateRelationshipCounter();
long node1 = 1;
long node2 = 2;
long node3 = 3;
putLabelsOnNodes(nodeLabels(node1, 7), nodeLabels(node2, 6), nodeLabels(node3, 70));
incrementCounts(counter, relationship(node1, 1, node2));
incrementCounts(counter, relationship(node1, 1, node3));
incrementCounts(counter, relationship(node1, 100, node2));
incrementCounts(counter, relationship(node1, 100, node3));
// then
try (CountsState.CountsChecker checker = countsState.checker(inconsistencyReporter)) {
// visiting unseen relationship counts
checker.visitRelationshipCount(2, 1, ANY_LABEL, 1);
checker.visitRelationshipCount(6, 2, ANY_LABEL, 1);
checker.visitRelationshipCount(ANY_LABEL, 1, 8, 1);
checker.visitRelationshipCount(ANY_LABEL, 100, 71, 1);
checker.visitRelationshipCount(7, 99, ANY_LABEL, 1);
checker.visitRelationshipCount(ANY_LABEL, 100, 7, 1);
// visiting wrong counts
checker.visitRelationshipCount(ANY_LABEL, 1, 6, 999);
checker.visitRelationshipCount(7, 100, ANY_LABEL, 999);
// not visiting 10 counts
}
verify(inconsistencyReporter, times(18)).forCounts(any());
}
Aggregations