/** * Copyright (c) 2002-2012 "Neo Technology," * Network Engine for Objects in Lund AB [http://neotechnology.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 Affero 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.neo4j.consistency.checking.old; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import org.neo4j.consistency.repair.RelationshipChainField; import org.neo4j.consistency.repair.RelationshipNodeField; import org.neo4j.consistency.store.DiffRecordStore; import org.neo4j.helpers.progress.ProgressListener; import org.neo4j.helpers.progress.ProgressMonitorFactory; import org.neo4j.kernel.IdType; import org.neo4j.kernel.impl.nioneo.store.AbstractBaseRecord; import org.neo4j.kernel.impl.nioneo.store.DynamicRecord; import org.neo4j.kernel.impl.nioneo.store.NodeRecord; import org.neo4j.kernel.impl.nioneo.store.PrimitiveRecord; import org.neo4j.kernel.impl.nioneo.store.PropertyBlock; import org.neo4j.kernel.impl.nioneo.store.PropertyIndexRecord; import org.neo4j.kernel.impl.nioneo.store.PropertyRecord; import org.neo4j.kernel.impl.nioneo.store.PropertyType; import org.neo4j.kernel.impl.nioneo.store.Record; import org.neo4j.kernel.impl.nioneo.store.RecordStore; import org.neo4j.kernel.impl.nioneo.store.RelationshipRecord; import org.neo4j.kernel.impl.nioneo.store.RelationshipTypeRecord; import org.neo4j.kernel.impl.nioneo.store.StoreAccess; import static org.neo4j.consistency.checking.old.InconsistencyType.PropertyBlockInconsistency.BlockInconsistencyType.DYNAMIC_NOT_IN_USE; import static org.neo4j.consistency.checking.old.InconsistencyType.PropertyBlockInconsistency.BlockInconsistencyType.ILLEGAL_PROPERTY_TYPE; import static org.neo4j.consistency.checking.old.InconsistencyType.PropertyBlockInconsistency.BlockInconsistencyType.INVALID_PROPERTY_KEY; import static org.neo4j.consistency.checking.old.InconsistencyType.PropertyBlockInconsistency.BlockInconsistencyType.UNUSED_PROPERTY_KEY; import static org.neo4j.consistency.checking.old.InconsistencyType.PropertyOwnerInconsistency.OwnerInconsistencyType.MULTIPLE_OWNERS; import static org.neo4j.consistency.checking.old.InconsistencyType.PropertyOwnerInconsistency.OwnerInconsistencyType.PROPERTY_CHANGED_FOR_WRONG_OWNER; import static org.neo4j.consistency.checking.old.InconsistencyType.ReferenceInconsistency.DYNAMIC_LENGTH_TOO_LARGE; import static org.neo4j.consistency.checking.old.InconsistencyType.ReferenceInconsistency.INVALID_TYPE_ID; import static org.neo4j.consistency.checking.old.InconsistencyType.ReferenceInconsistency.NEXT_DYNAMIC_NOT_IN_USE; import static org.neo4j.consistency.checking.old.InconsistencyType.ReferenceInconsistency.NEXT_DYNAMIC_NOT_REMOVED; import static org.neo4j.consistency.checking.old.InconsistencyType.ReferenceInconsistency.NEXT_PROPERTY_NOT_IN_USE; import static org.neo4j.consistency.checking.old.InconsistencyType.ReferenceInconsistency.NON_FULL_DYNAMIC_WITH_NEXT; import static org.neo4j.consistency.checking.old.InconsistencyType.ReferenceInconsistency.ORPHANED_PROPERTY; import static org.neo4j.consistency.checking.old.InconsistencyType.ReferenceInconsistency.OVERWRITE_USED_DYNAMIC; import static org.neo4j.consistency.checking.old.InconsistencyType.ReferenceInconsistency.OWNER_DOES_NOT_REFERENCE_BACK; import static org.neo4j.consistency.checking.old.InconsistencyType.ReferenceInconsistency.OWNER_NOT_IN_USE; import static org.neo4j.consistency.checking.old.InconsistencyType.ReferenceInconsistency.PREV_PROPERTY_NOT_IN_USE; import static org.neo4j.consistency.checking.old.InconsistencyType.ReferenceInconsistency.PROPERTY_CHANGED_WITHOUT_OWNER; import static org.neo4j.consistency.checking.old.InconsistencyType.ReferenceInconsistency.PROPERTY_FOR_OTHER; import static org.neo4j.consistency.checking.old.InconsistencyType.ReferenceInconsistency.PROPERTY_NEXT_WRONG_BACKREFERENCE; import static org.neo4j.consistency.checking.old.InconsistencyType.ReferenceInconsistency.PROPERTY_NOT_IN_USE; import static org.neo4j.consistency.checking.old.InconsistencyType.ReferenceInconsistency.PROPERTY_PREV_WRONG_BACKREFERENCE; import static org.neo4j.consistency.checking.old.InconsistencyType.ReferenceInconsistency.RELATIONSHIP_FOR_OTHER_NODE; import static org.neo4j.consistency.checking.old.InconsistencyType.ReferenceInconsistency.RELATIONSHIP_NOT_IN_USE; import static org.neo4j.consistency.checking.old.InconsistencyType.ReferenceInconsistency.RELATIONSHIP_NOT_REMOVED_FOR_DELETED_NODE; import static org.neo4j.consistency.checking.old.InconsistencyType.ReferenceInconsistency.REMOVED_PROPERTY_STILL_REFERENCED; import static org.neo4j.consistency.checking.old.InconsistencyType.ReferenceInconsistency.REMOVED_RELATIONSHIP_STILL_REFERENCED; import static org.neo4j.consistency.checking.old.InconsistencyType.ReferenceInconsistency.REPLACED_PROPERTY; import static org.neo4j.consistency.checking.old.InconsistencyType.ReferenceInconsistency.TYPE_NOT_IN_USE; import static org.neo4j.consistency.checking.old.InconsistencyType.ReferenceInconsistency.UNUSED_KEY_NAME; import static org.neo4j.consistency.checking.old.InconsistencyType.ReferenceInconsistency.UNUSED_TYPE_NAME; @Deprecated public class ConsistencyRecordProcessor extends RecordStore.Processor implements Runnable { private final RecordStore<NodeRecord> nodes; private final RecordStore<RelationshipRecord> rels; private final RecordStore<PropertyRecord> props; private final RecordStore<DynamicRecord> strings, arrays; private final RecordStore<PropertyIndexRecord> propIndexes; private final RecordStore<RelationshipTypeRecord> relTypes; private final RecordStore<DynamicRecord> propKeys; private final RecordStore<DynamicRecord> typeNames; private final HashMap<Long/*property record id*/, PropertyOwner> propertyOwners; private long brokenNodes, brokenRels, brokenProps, brokenStrings, brokenArrays, brokenTypes, brokenKeys; private final InconsistencyReport report; private final static RelationshipNodeField[] nodeFields = RelationshipNodeField.values(); private final static RelationshipChainField[] relFields = RelationshipChainField.values(); private final ProgressMonitorFactory progressFactory; public ConsistencyRecordProcessor( StoreAccess stores, InconsistencyReport report) { this( stores, false, report, ProgressMonitorFactory.NONE ); } /** * Creates a standard checker. * * @param stores the stores to check. */ public ConsistencyRecordProcessor( StoreAccess stores, InconsistencyReport report, ProgressMonitorFactory progressFactory ) { this( stores, false, report, progressFactory ); } /** * Creates a standard checker or a checker that validates property owners. * * Property ownership validation validates that each property record is only * referenced once. This check has a bit of memory overhead. * * @param stores the stores to check. * @param checkPropertyOwners if <code>true</code> ownership validation will */ public ConsistencyRecordProcessor( StoreAccess stores, boolean checkPropertyOwners, InconsistencyReport report, ProgressMonitorFactory progressFactory ) { this.nodes = stores.getNodeStore(); this.rels = stores.getRelationshipStore(); this.props = stores.getPropertyStore(); this.strings = stores.getStringStore(); this.arrays = stores.getArrayStore(); this.relTypes = stores.getRelationshipTypeStore(); this.propIndexes = stores.getPropertyIndexStore(); this.propKeys = stores.getPropertyKeyStore(); this.typeNames = stores.getTypeNameStore(); this.propertyOwners = checkPropertyOwners ? new HashMap<Long, PropertyOwner>() : null; this.report = report; this.progressFactory = progressFactory; } @Override public void processNode( RecordStore<NodeRecord> store, NodeRecord node ) { if ( checkNode( node ) ) brokenNodes++; } @Override public void processRelationship( RecordStore<RelationshipRecord> store, RelationshipRecord rel ) { if ( checkRelationship( rel ) ) brokenRels++; } @Override public void processProperty( RecordStore<PropertyRecord> store, PropertyRecord property ) { if ( checkProperty( property ) ) brokenProps++; } @Override public void processString( RecordStore<DynamicRecord> store, DynamicRecord string, IdType idType ) { if ( checkDynamic( store, string ) ) { brokenStrings++; } } @Override public void processArray( RecordStore<DynamicRecord> store, DynamicRecord array ) { if ( checkDynamic( store, array ) ) brokenArrays++; } @Override public void processRelationshipType( RecordStore<RelationshipTypeRecord> store, RelationshipTypeRecord type ) { if ( checkType( type ) ) brokenTypes++; } @Override public void processPropertyIndex( RecordStore<PropertyIndexRecord> store, PropertyIndexRecord index ) { if ( checkKey( index ) ) brokenKeys++; } private boolean checkNode( NodeRecord node ) { boolean fail = false; if ( !node.inUse() ) { NodeRecord old = nodes.forceGetRaw( node ); if ( old.inUse() ) // Check that referenced records are also removed { if ( !Record.NO_NEXT_RELATIONSHIP.is( old.getNextRel() ) ) { // NOTE: with reuse in the same tx this check is invalid RelationshipRecord rel = rels.forceGetRecord( old.getNextRel() ); if ( rel.inUse() ) fail |= report.inconsistent( nodes, node, rels, rel, RELATIONSHIP_NOT_REMOVED_FOR_DELETED_NODE ); } fail |= checkPropertyReference( node, nodes, new PropertyOwner.OwningNode( node.getId() ) ); } return fail; } long relId = node.getNextRel(); if ( !Record.NO_NEXT_RELATIONSHIP.is( relId ) ) { RelationshipRecord rel = rels.forceGetRecord( relId ); if ( !rel.inUse() ) fail |= report.inconsistent( nodes, node, rels, rel, RELATIONSHIP_NOT_IN_USE ); else if ( !( rel.getFirstNode() == node.getId() || rel.getSecondNode() == node.getId() ) ) fail |= report.inconsistent( nodes, node, rels, rel, RELATIONSHIP_FOR_OTHER_NODE ); } fail |= checkPropertyReference( node, nodes, new PropertyOwner.OwningNode( node.getId() ) ); return fail; } private <R extends PrimitiveRecord> boolean checkPropertyReference( R primitive, RecordStore<R> store, PropertyOwner owner ) { boolean fail = false; if ( props != null ) { R old = store.forceGetRaw( primitive ); if ( primitive.inUse() ) { if ( !Record.NO_NEXT_PROPERTY.is( primitive.getNextProp() ) ) { PropertyRecord prop = props.forceGetRecord( primitive.getNextProp() ); fail |= checkPropertyOwner( prop, owner ); if ( !prop.inUse() ) fail |= report.inconsistent( store, primitive, props, prop, PROPERTY_NOT_IN_USE ); else if ( owner.otherOwnerOf( prop ) != -1 || ( owner.ownerOf( prop ) != -1 && owner.ownerOf( prop ) != primitive.getId() ) ) fail |= report.inconsistent( store, primitive, props, prop, PROPERTY_FOR_OTHER ); } if ( old.inUse() && old.getNextProp() != primitive.getNextProp() ) { // first property changed for this primitive record ... if ( !Record.NO_NEXT_PROPERTY.is( old.getNextProp() ) ) { PropertyRecord oldProp = props.forceGetRecord( old.getNextProp() ); if ( owner.ownerOf( oldProp ) != primitive.getId() ) // ... but the old first property record didn't change accordingly fail |= report.inconsistent( props, oldProp, store, primitive, ORPHANED_PROPERTY ); } } } else { if ( !Record.NO_NEXT_PROPERTY.is( old.getNextProp() ) ) { // NOTE: with reuse in the same tx this check is invalid PropertyRecord prop = props.forceGetRecord( old.getNextProp() ); if ( prop.inUse() ) fail |= report.inconsistent( store, primitive, props, prop, owner.propertyNotRemoved() ); } } } return fail; } private boolean checkRelationship( RelationshipRecord rel ) { boolean fail = false; if ( !rel.inUse() ) { RelationshipRecord old = rels.forceGetRaw( rel ); if ( old.inUse() ) { for (RelationshipChainField field : relFields) { long otherId = field.relOf( old ); if (otherId == field.none) { Long nodeId = field.nodeOf( old ); if (nodeId != null) { NodeRecord node = nodes.forceGetRecord( nodeId ); if (node.inUse() && node.getNextRel() == old.getId()) fail |= report.inconsistent( rels, rel, nodes, node, REMOVED_RELATIONSHIP_STILL_REFERENCED ); } } else { RelationshipRecord other = rels.forceGetRecord( otherId ); if (other.inUse() && field.invConsistent( old, other )) fail |= report.inconsistent( rels,rel, other, REMOVED_RELATIONSHIP_STILL_REFERENCED ); } } fail |= checkPropertyReference( rel, rels, new PropertyOwner.OwningRelationship( rel.getId() ) ); } return fail; } if ( rel.getType() < 0 ) fail |= report.inconsistent( rels, rel, INVALID_TYPE_ID ); else { RelationshipTypeRecord type = relTypes.forceGetRecord( rel.getType() ); if ( !type.inUse() ) fail |= report.inconsistent( rels, rel, relTypes, type, TYPE_NOT_IN_USE ); } for ( RelationshipChainField field : relFields ) { long otherId = field.relOf( rel ); if ( otherId == field.none ) { Long nodeId = field.nodeOf( rel ); if ( nodeId != null ) { NodeRecord node = nodes.forceGetRecord( nodeId ); if ( !node.inUse() || node.getNextRel() != rel.getId() ) fail |= report.inconsistent( rels, rel, nodes, node, field.noBackReference ); } } else { RelationshipRecord other = rels.forceGetRecord( otherId ); if ( !other.inUse() ) fail |= report.inconsistent( rels, rel, other, field.notInUse ); else if ( !field.invConsistent( rel, other ) ) fail |= report.inconsistent( rels, rel, other, field.differentChain ); } } for ( RelationshipNodeField field : nodeFields ) { long nodeId = field.get( rel ); if ( nodeId < 0 ) fail |= report.inconsistent( rels, rel, field.invalidReference); else { NodeRecord node = nodes.forceGetRecord( nodeId ); if ( !node.inUse() ) fail |= report.inconsistent( rels, rel, nodes, node, field.notInUse ); } } fail |= checkPropertyReference( rel, rels, new PropertyOwner.OwningRelationship( rel.getId() ) ); return fail; } private boolean checkPropertyOwner( PropertyRecord prop, PropertyOwner newOwner ) { if (propertyOwners == null) return false; Long propId = Long.valueOf( prop.getId() ); PropertyOwner oldOwner = propertyOwners.put( propId, newOwner ); if ( oldOwner != null ) { @SuppressWarnings( "unchecked" ) RecordStore<PrimitiveRecord> oldStore = (RecordStore<PrimitiveRecord>) oldOwner.storeFrom( nodes, rels ), newStore = (RecordStore<PrimitiveRecord>) newOwner.storeFrom( nodes, rels ); return report.inconsistent( oldStore, oldStore.getRecord( oldOwner.id ), newStore, newStore.getRecord( newOwner.id ), MULTIPLE_OWNERS.forProperty( prop ) ); } else { return false; } } private boolean checkProperty( PropertyRecord property ) { boolean fail = false; if ( !property.inUse() ) { PropertyRecord old = props.forceGetRaw( property ); if ( old.inUse() ) { if ( !Record.NO_NEXT_PROPERTY.is( old.getNextProp() ) ) { PropertyRecord next = props.forceGetRecord( old.getNextProp() ); if ( next.inUse() && next.getPrevProp() == old.getId() ) fail |= report.inconsistent( props, property, next, REMOVED_PROPERTY_STILL_REFERENCED ); } if ( !Record.NO_PREVIOUS_PROPERTY.is( old.getPrevProp() ) ) { PropertyRecord prev = props.forceGetRecord( old.getPrevProp() ); if ( prev.inUse() && prev.getNextProp() == old.getId() ) fail |= report.inconsistent( props, property, prev, REMOVED_PROPERTY_STILL_REFERENCED ); } else // property was first in chain { if ( property.getNodeId() != -1 ) fail |= checkPropertyOwnerReference( property, property.getNodeId(), nodes ); else if ( property.getRelId() != -1 ) fail |= checkPropertyOwnerReference( property, property.getRelId(), rels ); else if ( ((DiffRecordStore<PropertyRecord>)props).isModified( property.getId() ) ) fail |= report.inconsistent( props, property, PROPERTY_CHANGED_WITHOUT_OWNER ); // only a warning } fail |= checkOwnerChain( property ); } return fail; } long nextId = property.getNextProp(); if ( !Record.NO_NEXT_PROPERTY.is( nextId ) ) { PropertyRecord next = props.forceGetRecord( nextId ); if ( !next.inUse() ) fail |= report.inconsistent( props, property, next, NEXT_PROPERTY_NOT_IN_USE ); if ( next.getPrevProp() != property.getId() ) fail |= report.inconsistent( props, property, next, PROPERTY_NEXT_WRONG_BACKREFERENCE ); } long prevId = property.getPrevProp(); if ( !Record.NO_PREVIOUS_PROPERTY.is( prevId ) ) { PropertyRecord prev = props.forceGetRecord( prevId ); if ( !prev.inUse() ) fail |= report.inconsistent( props, property, prev, PREV_PROPERTY_NOT_IN_USE ); if ( prev.getNextProp() != property.getId() ) fail |= report.inconsistent( props, property, prev, PROPERTY_PREV_WRONG_BACKREFERENCE ); } else // property is first in chain { if ( property.getNodeId() != -1 ) fail |= checkPropertyOwnerReference( property, property.getNodeId(), nodes ); else if ( property.getRelId() != -1 ) fail |= checkPropertyOwnerReference( property, property.getRelId(), rels ); else if ( props instanceof DiffRecordStore<?> && ( (DiffRecordStore<PropertyRecord>) props ).isModified( property.getId() ) ) fail |= report.inconsistent( props, property, PROPERTY_CHANGED_WITHOUT_OWNER ); // only a warning // else - this information is only available from logs through DiffRecordStore } fail |= checkOwnerChain( property ); for ( PropertyBlock block : property.getPropertyBlocks() ) { if ( block.getKeyIndexId() < 0 ) fail |= report.inconsistent( props, property, INVALID_PROPERTY_KEY.forBlock( block ) ); else { PropertyIndexRecord key = propIndexes.forceGetRecord( block.getKeyIndexId() ); if ( !key.inUse() ) fail |= report.inconsistent( props, property, propIndexes, key, UNUSED_PROPERTY_KEY.forBlock( block ) ); } RecordStore<DynamicRecord> dynStore = null; PropertyType type = block.forceGetType(); if ( type == null ) { fail |= report.inconsistent( props, property, ILLEGAL_PROPERTY_TYPE.forBlock( block ) ); } else switch ( block.getType() ) { case STRING: dynStore = strings; break; case ARRAY: dynStore = arrays; break; } if ( dynStore != null ) { DynamicRecord dynrec = dynStore.forceGetRecord( block.getSingleValueLong() ); if ( !dynrec.inUse() ) fail |= report.inconsistent( props, property, dynStore, dynrec, DYNAMIC_NOT_IN_USE.forBlock( block ) ); } } return fail; } private boolean checkOwnerChain( PropertyRecord property ) { boolean fail = false; RecordStore<? extends PrimitiveRecord> store = null; long ownerId = -1; if ( property.getNodeId() != -1 ) { store = nodes; ownerId = property.getNodeId(); } else if ( property.getRelId() != -1 ) { store = rels; ownerId = property.getRelId(); } if ( store != null ) { PrimitiveRecord owner = store.forceGetRecord( ownerId ); if ( !property.inUse() ) { owner = ((RecordStore<PrimitiveRecord>) store).forceGetRaw( owner ); } List<PropertyRecord> chain = new ArrayList<PropertyRecord>( 2 ); PropertyRecord prop = null; for ( long propId = owner.getNextProp(), target = property.getId(); propId != target; propId = prop.getNextProp() ) { if ( Record.NO_NEXT_PROPERTY.is( propId ) ) { fail |= report.inconsistent( props, property, store, owner, PROPERTY_CHANGED_FOR_WRONG_OWNER.forProperties( chain ) ); break; // chain ended, not found } prop = props.forceGetRecord( propId ); if ( !property.inUse() ) { prop = props.forceGetRaw( prop ); } chain.add( prop ); } } else if ( props instanceof DiffRecordStore<?> && ( (DiffRecordStore<?>) props ).isModified( property.getId() ) ) fail |= report.inconsistent( props, property, PROPERTY_CHANGED_WITHOUT_OWNER ); // only a warning return fail; } private boolean checkPropertyOwnerReference( PropertyRecord property, long ownerId, RecordStore<? extends PrimitiveRecord> entityStore ) { boolean fail = false; PrimitiveRecord entity = entityStore.forceGetRecord( ownerId ); if ( !property.inUse() ) { if ( entity.inUse() ) { if ( entity.getNextProp() == property.getId() ) fail |= report.inconsistent( props, property, entityStore, entity, REMOVED_PROPERTY_STILL_REFERENCED ); } return fail; } if ( !entity.inUse() ) fail |= report.inconsistent( props, property, entityStore, entity, OWNER_NOT_IN_USE ); else if ( entity.getNextProp() != property.getId() ) fail |= report.inconsistent( props, property, entityStore, entity, OWNER_DOES_NOT_REFERENCE_BACK ); if ( entityStore instanceof DiffRecordStore<?> ) { DiffRecordStore<PrimitiveRecord> diffs = (DiffRecordStore<PrimitiveRecord>) entityStore; if ( diffs.isModified( entity.getId() ) ) { PrimitiveRecord old = diffs.forceGetRaw( entity ); // IF old is in use and references a property record if ( old.inUse() && !Record.NO_NEXT_PROPERTY.is( old.getNextProp() ) ) // AND that property record is not the same as this property record if ( old.getNextProp() != property.getId() ) // THEN that property record must also have been updated! if ( !( (DiffRecordStore<?>) props ).isModified( old.getNextProp() ) ) fail |= report.inconsistent( props, property, entityStore, entity, REPLACED_PROPERTY ); } } return fail; } private boolean checkDynamic( RecordStore<DynamicRecord> store, DynamicRecord record ) { boolean fail = false; if ( !record.inUse() ) { if ( store instanceof DiffRecordStore<?> ) { DynamicRecord old = store.forceGetRaw( record ); if ( old.inUse() && !Record.NO_NEXT_BLOCK.is( old.getNextBlock() ) ) { DynamicRecord next = store.forceGetRecord( old.getNextBlock() ); if ( next.inUse() ) // the entire chain must be removed fail |= report.inconsistent( store, record, next, NEXT_DYNAMIC_NOT_REMOVED ); } } return fail; } long nextId = record.getNextBlock(); if ( !Record.NO_NEXT_BLOCK.is( nextId ) ) { // If next is set, then it must be in use DynamicRecord next = store.forceGetRecord( nextId ); if ( !next.inUse() ) fail |= report.inconsistent( store, record, next, NEXT_DYNAMIC_NOT_IN_USE ); // If next is set, then the size must be max if ( record.getLength() < store.getRecordSize() - store.getRecordHeaderSize() ) fail |= report.inconsistent( store, record, NON_FULL_DYNAMIC_WITH_NEXT ); } if ( record.getId() != 0 && record.getLength() > store.getRecordSize() - store.getRecordHeaderSize() ) { /* * The length must always be less than or equal to max, * except for the first dynamic record in a store, which * does not conform to the usual format */ fail |= report.inconsistent( store, record, DYNAMIC_LENGTH_TOO_LARGE ); } if ( store instanceof DiffRecordStore<?> ) { DiffRecordStore<DynamicRecord> diffs = (DiffRecordStore<DynamicRecord>) store; if ( diffs.isModified( record.getId() ) && !record.isLight() ) { // if the record is really modified it will be heavy DynamicRecord prev = diffs.forceGetRaw( record ); if ( prev.inUse() ) fail |= report.inconsistent( store, record, prev, OVERWRITE_USED_DYNAMIC ); } } return fail; } private boolean checkType( RelationshipTypeRecord type ) { if ( !type.inUse() ) return false; // no check for unused records if ( Record.NO_NEXT_BLOCK.is( type.getNameId() ) ) return false; // accept this DynamicRecord record = typeNames.forceGetRecord( type.getNameId() ); if ( !record.inUse() ) return report.inconsistent( relTypes, type, typeNames, record, UNUSED_TYPE_NAME ); return false; } private boolean checkKey( PropertyIndexRecord key ) { if ( !key.inUse() ) return false; // no check for unused records if ( Record.NO_NEXT_BLOCK.is( key.getNameId() ) ) return false; // accept this DynamicRecord record = propKeys.forceGetRecord( key.getNameId() ); if ( !record.inUse() ) return report.inconsistent( propIndexes, key, propKeys, record, UNUSED_KEY_NAME ); return false; } @Override public void run() { ProgressMonitorFactory.MultiPartBuilder builder = progressFactory.multipleParts( "ConsistencyCheck" ); List<Runnable> tasks = new ArrayList<Runnable>( 9 ); tasks.add( storeProcessor( nodes, builder ) ); tasks.add( storeProcessor( rels, builder ) ); // free up some heap space that isn't needed anymore if ( propertyOwners != null ) propertyOwners.clear(); // TODO: invoke in proper order tasks.add( storeProcessor( props, builder ) ); // free up some heap space that isn't needed anymore if ( propertyOwners != null ) propertyOwners.clear(); // TODO: invoke in proper order tasks.add( storeProcessor( strings, builder ) ); tasks.add( storeProcessor( arrays, builder ) ); tasks.add( storeProcessor( relTypes, builder ) ); tasks.add( storeProcessor( propIndexes, builder ) ); tasks.add( storeProcessor( propKeys, builder ) ); tasks.add( storeProcessor( typeNames, builder ) ); builder.build(); for ( Runnable task : tasks ) { task.run(); } } private <R extends AbstractBaseRecord> StoreProcessor<R> storeProcessor( RecordStore<R> store, ProgressMonitorFactory.MultiPartBuilder builder ) { return new StoreProcessor<R>( store, builder ); } private class StoreProcessor<R extends AbstractBaseRecord> implements Runnable { private final RecordStore<R> store; private final ProgressListener progressListener; private StoreProcessor( RecordStore<R> store, ProgressMonitorFactory.MultiPartBuilder builder ) { this.store = store; String name = store.getStorageFileName(); this.progressListener = builder.progressForPart( name.substring( name.lastIndexOf( '/' ) + 1 ), store.getHighId() ); } @Override @SuppressWarnings( "unchecked" ) public void run() { applyFiltered( store, progressListener, RecordStore.IN_USE ); } } }