org.neo4j.consistency.checking.full.OwnerCheck Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of neo4j-consistency-check Show documentation
Show all versions of neo4j-consistency-check Show documentation
Tool for checking consistency of a Neo4j data store.
/*
* 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