All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.neo4j.consistency.checking.full.OwnerCheck Maven / Gradle / Ivy

There is a newer version: 5.26.0
Show newest version
/*
 * Copyright (c) 2002-2020 "Neo4j,"
 * Neo4j Sweden AB [http://neo4j.com]
 *
 * This file is part of Neo4j.
 *
 * Neo4j is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */
package org.neo4j.consistency.checking.full;

import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.neo4j.consistency.RecordType;
import org.neo4j.consistency.checking.CheckDecorator;
import org.neo4j.consistency.checking.CheckerEngine;
import org.neo4j.consistency.checking.ComparativeRecordChecker;
import org.neo4j.consistency.checking.DynamicStore;
import org.neo4j.consistency.checking.OwningRecordCheck;
import org.neo4j.consistency.checking.RecordCheck;
import org.neo4j.consistency.report.ConsistencyReport;
import org.neo4j.consistency.report.ConsistencyReport.DynamicConsistencyReport;
import org.neo4j.consistency.report.ConsistencyReport.LabelTokenConsistencyReport;
import org.neo4j.consistency.report.ConsistencyReport.NeoStoreConsistencyReport;
import org.neo4j.consistency.report.ConsistencyReport.NodeConsistencyReport;
import org.neo4j.consistency.report.ConsistencyReport.PropertyConsistencyReport;
import org.neo4j.consistency.report.ConsistencyReport.PropertyKeyTokenConsistencyReport;
import org.neo4j.consistency.report.ConsistencyReport.RelationshipConsistencyReport;
import org.neo4j.consistency.report.ConsistencyReport.RelationshipGroupConsistencyReport;
import org.neo4j.consistency.report.ConsistencyReport.RelationshipTypeConsistencyReport;
import org.neo4j.consistency.store.RecordAccess;
import org.neo4j.helpers.progress.ProgressListener;
import org.neo4j.helpers.progress.ProgressMonitorFactory;
import org.neo4j.kernel.impl.store.PropertyType;
import org.neo4j.kernel.impl.store.record.DynamicRecord;
import org.neo4j.kernel.impl.store.record.LabelTokenRecord;
import org.neo4j.kernel.impl.store.record.NeoStoreRecord;
import org.neo4j.kernel.impl.store.record.NodeRecord;
import org.neo4j.kernel.impl.store.record.PrimitiveRecord;
import org.neo4j.kernel.impl.store.record.PropertyBlock;
import org.neo4j.kernel.impl.store.record.PropertyKeyTokenRecord;
import org.neo4j.kernel.impl.store.record.PropertyRecord;
import org.neo4j.kernel.impl.store.record.Record;
import org.neo4j.kernel.impl.store.record.RelationshipGroupRecord;
import org.neo4j.kernel.impl.store.record.RelationshipRecord;
import org.neo4j.kernel.impl.store.record.RelationshipTypeTokenRecord;
import org.neo4j.kernel.impl.store.record.TokenRecord;

import static java.util.Collections.unmodifiableMap;
import static org.neo4j.consistency.RecordType.ARRAY_PROPERTY;
import static org.neo4j.consistency.RecordType.PROPERTY_KEY_NAME;
import static org.neo4j.consistency.RecordType.RELATIONSHIP_TYPE_NAME;
import static org.neo4j.consistency.RecordType.STRING_PROPERTY;

class OwnerCheck implements CheckDecorator
{
    private final ConcurrentMap owners;
    private final Map> dynamics;

    OwnerCheck( boolean active, DynamicStore... stores )
    {
        this.owners = active ? new ConcurrentHashMap<>( 16, 0.75f, 4 ) : null;
        this.dynamics = active ? initialize( stores ) : null;
    }

    private static Map> initialize( DynamicStore[] stores )
    {
        EnumMap> map =
                new EnumMap<>( RecordType.class );
        for ( DynamicStore store : stores )
        {
            map.put( store.type, new ConcurrentHashMap<>( 16, 0.75f, 4 ) );
        }
        return unmodifiableMap( map );
    }

    void scanForOrphanChains( ProgressMonitorFactory progressFactory )
    {
        List tasks = new ArrayList<>();
        ProgressMonitorFactory.MultiPartBuilder progress = progressFactory
                .multipleParts( "Checking for orphan chains" );
        if ( owners != null )
        {
            tasks.add( new OrphanCheck( RecordType.PROPERTY, owners, progress ) );
        }
        if ( dynamics != null )
        {
            for ( Map.Entry> entry : dynamics.entrySet() )
            {
                tasks.add( new OrphanCheck( entry.getKey(), entry.getValue(), progress ) );
            }
        }
        for ( Runnable task : tasks )
        {
            task.run();
        }
    }

    private static class OrphanCheck implements Runnable
    {
        private final ConcurrentMap owners;
        private final ProgressListener progress;

        OrphanCheck( RecordType property, ConcurrentMap owners,
                     ProgressMonitorFactory.MultiPartBuilder progress )
        {
            this.owners = owners;
            this.progress = progress.progressForPart( "Checking for orphan " + property.name() + " chains",
                                                      owners.size() );
        }

        @Override
        public void run()
        {
            for ( Owner owner : owners.values() )
            {
                owner.checkOrphanage();
                progress.add( 1 );
            }
            progress.done();
        }
    }

    @Override
    public void prepare()
    {
    }

    @Override
    public OwningRecordCheck decorateNeoStoreChecker(
            OwningRecordCheck checker )
    {
        if ( owners == null )
        {
            return checker;
        }
        return new PrimitiveCheckerDecorator( checker )
        {
            @Override
            PropertyOwner owner( NeoStoreRecord record )
            {
                return PropertyOwner.OWNING_GRAPH;
            }
        };
    }

    @Override
    public OwningRecordCheck decorateNodeChecker(
            OwningRecordCheck checker )
    {
        if ( owners == null )
        {
            return checker;
        }
        return new PrimitiveCheckerDecorator( checker )
        {
            @Override
            PropertyOwner owner( NodeRecord record )
            {
                return new PropertyOwner.OwningNode( record );
            }
        };
    }

    @Override
    public OwningRecordCheck decorateRelationshipChecker(
            OwningRecordCheck checker )
    {
        if ( owners == null )
        {
            return checker;
        }
        return new PrimitiveCheckerDecorator(
                checker )
        {
            @Override
            PropertyOwner owner( RelationshipRecord record )
            {
                return new PropertyOwner.OwningRelationship( record );
            }
        };
    }

    @Override
    public RecordCheck decoratePropertyChecker(
            final RecordCheck checker )
    {
        if ( owners == null && dynamics == null )
        {
            return checker;
        }
        return ( record, engine, records ) ->
        {
            if ( record.inUse() )
            {
                if ( owners != null && Record.NO_PREVIOUS_PROPERTY.is( record.getPrevProp() ) )
                { // this record is first in a chain
                    PropertyOwner.UnknownOwner owner = new PropertyOwner.UnknownOwner();
                    engine.comparativeCheck( owner, ORPHAN_CHECKER );
                    if ( null == owners.putIfAbsent( record.getId(), owner ) )
                    {
                        owner.markInCustody();
                    }
                }
                if ( dynamics != null )
                {
                    for ( PropertyBlock block : record )
                    {
                        RecordType type = recordType( block.forceGetType() );
                        if ( type != null )
                        {
                            ConcurrentMap dynamicOwners = dynamics.get( type );
                            if ( dynamicOwners != null )
                            {
                                long id = block.getSingleValueLong();
                                DynamicOwner.Property owner = new DynamicOwner.Property( type, record );
                                DynamicOwner prev = dynamicOwners.put( id, owner );
                                if ( prev != null )
                                {
                                    engine.comparativeCheck( prev.record( records ), owner );
                                }
                            }
                        }
                    }
                }
            }
            checker.check( record, engine, records );
        };
    }

    private RecordType recordType( PropertyType type )
    {
        if ( type == null )
        {
            return null;
        }

        switch ( type )
        {
        case STRING:
            return STRING_PROPERTY;
        case ARRAY:
            return ARRAY_PROPERTY;
        default:
            return null;
        }
    }

    @Override
    public RecordCheck decoratePropertyKeyTokenChecker(
            RecordCheck checker )
    {
        ConcurrentMap dynamicOwners = dynamicOwners( PROPERTY_KEY_NAME );
        if ( dynamicOwners == null )
        {
            return checker;
        }
        return new NameCheckerDecorator
                ( checker, dynamicOwners )
        {
            @Override
            DynamicOwner.NameOwner owner( PropertyKeyTokenRecord record )
            {
                return new DynamicOwner.PropertyKey( record );
            }
        };
    }

    @Override
    public RecordCheck decorateRelationshipTypeTokenChecker(
            RecordCheck checker )
    {
        ConcurrentMap dynamicOwners = dynamicOwners( RELATIONSHIP_TYPE_NAME );
        if ( dynamicOwners == null )
        {
            return checker;
        }
        return new NameCheckerDecorator(
                checker, dynamicOwners )
        {
            @Override
            DynamicOwner.NameOwner owner( RelationshipTypeTokenRecord record )
            {
                return new DynamicOwner.RelationshipTypeToken( record );
            }
        };
    }

    @Override
    public RecordCheck decorateLabelTokenChecker(
            RecordCheck checker )
    {
        ConcurrentMap dynamicOwners = dynamicOwners( RELATIONSHIP_TYPE_NAME );
        if ( dynamicOwners == null )
        {
            return checker;
        }
        return new NameCheckerDecorator( checker, dynamicOwners )
        {
            @Override
            DynamicOwner.NameOwner owner( LabelTokenRecord record )
            {
                return new DynamicOwner.LabelToken( record );
            }
        };
    }

    RecordCheck decorateDynamicChecker(
            final RecordType type, final RecordCheck checker )
    {
        final ConcurrentMap dynamicOwners = dynamicOwners( type );
        if ( dynamicOwners == null )
        {
            return checker;
        }
        return ( record, engine, records ) ->
        {
            if ( record.inUse() )
            {
                DynamicOwner.Unknown owner = new DynamicOwner.Unknown();
                engine.comparativeCheck( owner, DynamicOwner.ORPHAN_CHECK );
                if ( null == dynamicOwners.putIfAbsent( record.getId(), owner ) )
                {
                    owner.markInCustody();
                }
                if ( !Record.NO_NEXT_BLOCK.is( record.getNextBlock() ) )
                {
                    DynamicOwner.Dynamic nextOwner = new DynamicOwner.Dynamic( type, record );
                    DynamicOwner prevOwner = dynamicOwners.put( record.getNextBlock(), nextOwner );
                    if ( prevOwner != null )
                    {
                        engine.comparativeCheck( prevOwner.record( records ), nextOwner );
                    }
                }
            }
            checker.check( record, engine, records );
        };
    }

    @Override
    public RecordCheck decorateRelationshipGroupChecker(
            RecordCheck checker )
    {
        return checker;
    }

    private ConcurrentMap dynamicOwners( RecordType type )
    {
        return dynamics == null ? null : dynamics.get( type );
    }

    private abstract class PrimitiveCheckerDecorator
            implements OwningRecordCheck
    {
        private final OwningRecordCheck checker;

        PrimitiveCheckerDecorator( OwningRecordCheck checker )
        {
            this.checker = checker;
        }

        @Override
        @SuppressWarnings( "unchecked" )
        public void check( RECORD record, CheckerEngine engine, RecordAccess records )
        {
            if ( record.inUse() )
            {
                long prop = record.getNextProp();
                if ( !Record.NO_NEXT_PROPERTY.is( prop ) )
                {
                    PropertyOwner previous = owners.put( prop, owner( record ) );
                    if ( previous != null )
                    {
                        engine.comparativeCheck( previous.record( records ), checker.ownerCheck() );
                    }
                }
            }
            checker.check( record, engine, records );
        }

        @Override
        public ComparativeRecordChecker ownerCheck()
        {
            return checker.ownerCheck();
        }

        abstract PropertyOwner owner( RECORD record );
    }

    private abstract static class NameCheckerDecorator
            
            implements RecordCheck
    {
        private final RecordCheck checker;
        private final ConcurrentMap owners;

        NameCheckerDecorator( RecordCheck checker, ConcurrentMap owners )
        {
            this.checker = checker;
            this.owners = owners;
        }

        @SuppressWarnings( "unchecked" )
        @Override
        public void check( RECORD record, CheckerEngine engine, RecordAccess records )
        {
            if ( record.inUse() )
            {
                DynamicOwner.NameOwner owner = owner( record );
                DynamicOwner prev = owners.put( (long)record.getNameId(), owner );
                if ( prev != null )
                {
                    engine.comparativeCheck( prev.record( records ), owner );
                }
            }
            checker.check( record, engine, records );
        }

        abstract DynamicOwner.NameOwner owner( RECORD record );
    }

    private static final ComparativeRecordChecker ORPHAN_CHECKER =
            ( record, primitiveRecord, engine, records ) -> engine.report().orphanPropertyChain();
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy