/**
* 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.full;
import static org.neo4j.consistency.store.RecordReference.SkippingReference.skipReference;
import java.util.ArrayList;
import java.util.List;
import org.neo4j.consistency.checking.CheckDecorator;
import org.neo4j.consistency.report.ConsistencyLogger;
import org.neo4j.consistency.report.ConsistencyReporter;
import org.neo4j.consistency.report.ConsistencySummaryStatistics;
import org.neo4j.consistency.store.DiffRecordAccess;
import org.neo4j.consistency.store.RecordReference;
import org.neo4j.kernel.impl.nioneo.store.DynamicRecord;
import org.neo4j.kernel.impl.nioneo.store.NodeRecord;
import org.neo4j.kernel.impl.nioneo.store.PropertyRecord;
import org.neo4j.kernel.impl.nioneo.store.RecordStore;
import org.neo4j.kernel.impl.nioneo.store.RelationshipRecord;
import org.neo4j.kernel.impl.nioneo.store.StoreAccess;
enum MultiPassStore
{
NODES()
{
@Override
RecordStore getRecordStore( StoreAccess storeAccess )
{
return storeAccess.getNodeStore();
}
@Override
DiffRecordAccess filter( final DiffRecordAccess recordAccess, final int iPass,
final long recordsPerPass )
{
return new SkipAllButCached( recordAccess )
{
@Override
public RecordReference<NodeRecord> node( long id )
{
if ( recordInCurrentPass( id, iPass, recordsPerPass ) )
{
return recordAccess.node( id );
}
return skipReference();
}
};
}
},
RELATIONSHIPS()
{
@Override
RecordStore getRecordStore( StoreAccess storeAccess )
{
return storeAccess.getRelationshipStore();
}
@Override
DiffRecordAccess filter( final DiffRecordAccess recordAccess, final int iPass,
final long recordsPerPass )
{
return new SkipAllButCached( recordAccess )
{
@Override
public RecordReference<RelationshipRecord> relationship( long id )
{
if ( recordInCurrentPass( id, iPass, recordsPerPass ) )
{
return recordAccess.relationship( id );
}
return skipReference();
}
};
}
},
PROPERTIES()
{
@Override
RecordStore getRecordStore( StoreAccess storeAccess )
{
return storeAccess.getPropertyStore();
}
@Override
DiffRecordAccess filter( final DiffRecordAccess recordAccess, final int iPass,
final long recordsPerPass )
{
return new SkipAllButCached( recordAccess )
{
@Override
public RecordReference<PropertyRecord> property( long id )
{
if ( recordInCurrentPass( id, iPass, recordsPerPass ) )
{
return recordAccess.property( id );
}
return skipReference();
}
};
}
},
STRINGS()
{
@Override
RecordStore getRecordStore( StoreAccess storeAccess )
{
return storeAccess.getNodeStore();
}
@Override
DiffRecordAccess filter( final DiffRecordAccess recordAccess, final int iPass,
final long recordsPerPass )
{
return new SkipAllButCached( recordAccess )
{
@Override
public RecordReference<DynamicRecord> string( long id )
{
if ( recordInCurrentPass( id, iPass, recordsPerPass ) )
{
return recordAccess.string( id );
}
return skipReference();
}
};
}
},
ARRAYS()
{
@Override
RecordStore getRecordStore( StoreAccess storeAccess )
{
return storeAccess.getNodeStore();
}
@Override
DiffRecordAccess filter( final DiffRecordAccess recordAccess, final int iPass,
final long recordsPerPass )
{
return new SkipAllButCached( recordAccess )
{
@Override
public RecordReference<DynamicRecord> array( long id )
{
if ( recordInCurrentPass( id, iPass, recordsPerPass ) )
{
return recordAccess.array( id );
}
return skipReference();
}
};
}
};
private static boolean recordInCurrentPass( long id, int iPass, long recordsPerPass )
{
return id >= iPass * recordsPerPass && id < (iPass + 1) * recordsPerPass;
}
public List<DiffRecordAccess> multiPassFilters( long memoryPerPass, StoreAccess storeAccess,
DiffRecordAccess recordAccess )
{
ArrayList<DiffRecordAccess> filters = new ArrayList<DiffRecordAccess>();
RecordStore recordStore = getRecordStore( storeAccess );
long recordsPerPass = memoryPerPass / recordStore.getRecordSize();
long highId = recordStore.getHighId();
for ( int iPass = 0; iPass * recordsPerPass <= highId; iPass++ )
{
filters.add( filter( recordAccess, iPass, recordsPerPass ) );
}
return filters;
}
abstract RecordStore getRecordStore( StoreAccess storeAccess );
abstract DiffRecordAccess filter( DiffRecordAccess recordAccess, int iPass, long recordsPerPass );
static class Factory
{
private final CheckDecorator decorator;
private final ConsistencyLogger logger;
private final DiffRecordAccess recordAccess;
private final ConsistencySummaryStatistics summary;
private final long totalMappedMemory;
private final StoreAccess storeAccess;
Factory( CheckDecorator decorator, ConsistencyLogger logger, long totalMappedMemory,
StoreAccess storeAccess, DiffRecordAccess recordAccess, ConsistencySummaryStatistics summary )
{
this.decorator = decorator;
this.logger = logger;
this.totalMappedMemory = totalMappedMemory;
this.storeAccess = storeAccess;
this.recordAccess = recordAccess;
this.summary = summary;
}
StoreProcessor[] createAll( MultiPassStore... stores )
{
List<StoreProcessor> result = new ArrayList<StoreProcessor>();
for ( MultiPassStore store : stores )
{
List<DiffRecordAccess> filters = store.multiPassFilters( totalMappedMemory, storeAccess, recordAccess );
for ( DiffRecordAccess filter : filters )
{
result.add( new StoreProcessor( decorator, new ConsistencyReporter( logger, filter, summary ) ) );
}
}
return result.toArray( new StoreProcessor[result.size()] );
}
}
}