Search in sources :

Example 1 with RelationshipCounter

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();
    }
}
Also used : IntObjectHashMap(org.eclipse.collections.impl.map.mutable.primitive.IntObjectHashMap) CacheAccess(org.neo4j.consistency.checking.cache.CacheAccess) RelationshipRecord(org.neo4j.kernel.impl.store.record.RelationshipRecord) CursorContext(org.neo4j.io.pagecache.context.CursorContext) EntityTokenRange(org.neo4j.kernel.impl.index.schema.EntityTokenRange) RelationshipCounter(org.neo4j.internal.recordstorage.RelationshipCounter) ProgressListener(org.neo4j.internal.helpers.progress.ProgressListener) Value(org.neo4j.values.storable.Value)

Example 2 with RelationshipCounter

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())));
        }
    };
}
Also used : AtomicLong(java.util.concurrent.atomic.AtomicLong) RelationshipCounter(org.neo4j.internal.recordstorage.RelationshipCounter) CountsEntry(org.neo4j.consistency.store.synthetic.CountsEntry) CountsKey(org.neo4j.internal.counts.CountsKey)

Example 3 with RelationshipCounter

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());
}
Also used : RelationshipCounter(org.neo4j.internal.recordstorage.RelationshipCounter) Test(org.junit.jupiter.api.Test)

Aggregations

RelationshipCounter (org.neo4j.internal.recordstorage.RelationshipCounter)3 AtomicLong (java.util.concurrent.atomic.AtomicLong)1 IntObjectHashMap (org.eclipse.collections.impl.map.mutable.primitive.IntObjectHashMap)1 Test (org.junit.jupiter.api.Test)1 CacheAccess (org.neo4j.consistency.checking.cache.CacheAccess)1 CountsEntry (org.neo4j.consistency.store.synthetic.CountsEntry)1 CountsKey (org.neo4j.internal.counts.CountsKey)1 ProgressListener (org.neo4j.internal.helpers.progress.ProgressListener)1 CursorContext (org.neo4j.io.pagecache.context.CursorContext)1 EntityTokenRange (org.neo4j.kernel.impl.index.schema.EntityTokenRange)1 RelationshipRecord (org.neo4j.kernel.impl.store.record.RelationshipRecord)1 Value (org.neo4j.values.storable.Value)1