/** * Copyright (c) 2002-2011 "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.kernel.ha; import java.util.EnumMap; import java.util.Map; import org.neo4j.helpers.Pair; import org.neo4j.kernel.CommonFactories; import org.neo4j.kernel.IdGeneratorFactory; import org.neo4j.kernel.IdType; import org.neo4j.kernel.ha.zookeeper.Machine; import org.neo4j.kernel.ha.zookeeper.ZooKeeperException; import org.neo4j.kernel.impl.nioneo.store.IdGenerator; import org.neo4j.kernel.impl.nioneo.store.IdRange; import org.neo4j.kernel.impl.nioneo.store.NeoStore; public class SlaveIdGenerator implements IdGenerator { private static final long VALUE_REPRESENTING_NULL = -1; public static class SlaveIdGeneratorFactory implements IdGeneratorFactory { private final Broker broker; private final ResponseReceiver receiver; private final Map<IdType, SlaveIdGenerator> generators = new EnumMap<IdType, SlaveIdGenerator>( IdType.class ); private final IdGeneratorFactory localFactory = CommonFactories.defaultIdGeneratorFactory(); public SlaveIdGeneratorFactory( Broker broker, ResponseReceiver receiver ) { this.broker = broker; this.receiver = receiver; } public IdGenerator open( String fileName, int grabSize, IdType idType, long highestIdInUse ) { IdGenerator localIdGenerator = localFactory.open( fileName, grabSize, idType, highestIdInUse ); SlaveIdGenerator generator = new SlaveIdGenerator( idType, highestIdInUse, broker, receiver, localIdGenerator ); generators.put( idType, generator ); return generator; } public void create( String fileName ) { localFactory.create( fileName ); } public IdGenerator get( IdType idType ) { return generators.get( idType ); } public void updateIdGenerators( NeoStore store ) { store.updateIdGenerators(); } public void forgetIdAllocationsFromMaster() { for ( SlaveIdGenerator idGenerator : generators.values() ) { idGenerator.forgetIdAllocationFromMaster(); } } }; private final Broker broker; private final ResponseReceiver receiver; private volatile long highestIdInUse; private volatile long defragCount; private volatile IdRangeIterator idQueue = EMPTY_ID_RANGE_ITERATOR; private volatile int allocationMaster; private final IdType idType; private final IdGenerator localIdGenerator; public SlaveIdGenerator( IdType idType, long highestIdInUse, Broker broker, ResponseReceiver receiver, IdGenerator localIdGenerator ) { this.idType = idType; this.broker = broker; this.receiver = receiver; this.localIdGenerator = localIdGenerator; } private void forgetIdAllocationFromMaster() { this.idQueue = EMPTY_ID_RANGE_ITERATOR; } public void close() { this.localIdGenerator.close(); } public void freeId( long id ) { } public long getHighId() { return Math.max( this.localIdGenerator.getHighId(), highestIdInUse ); } public long getNumberOfIdsInUse() { return Math.max( this.localIdGenerator.getNumberOfIdsInUse(), highestIdInUse-defragCount ); } public synchronized long nextId() { try { long nextId = nextLocalId(); Pair<Master, Machine> master = broker.getMaster(); if ( nextId == VALUE_REPRESENTING_NULL ) { // If we dont have anymore grabbed ids from master, grab a bunch IdAllocation allocation = master.first().allocateIds( idType ); allocationMaster = master.other().getMachineId(); nextId = storeLocally( allocation ); } else { assert master.other().getMachineId() == allocationMaster; } return nextId; } catch ( ZooKeeperException e ) { receiver.newMaster( null, e ); throw e; } catch ( HaCommunicationException e ) { receiver.newMaster( null, e ); throw e; } } public IdRange nextIdBatch( int size ) { throw new UnsupportedOperationException( "Should never be called" ); } private long storeLocally( IdAllocation allocation ) { this.highestIdInUse = allocation.getHighestIdInUse(); this.defragCount = allocation.getDefragCount(); this.idQueue = new IdRangeIterator( allocation.getIdRange() ); updateLocalIdGenerator(); return idQueue.next(); } private void updateLocalIdGenerator() { long localHighId = this.localIdGenerator.getHighId(); if ( this.highestIdInUse > localHighId ) { this.localIdGenerator.setHighId( this.highestIdInUse ); } } private long nextLocalId() { return this.idQueue.next(); } public void setHighId( long id ) { this.localIdGenerator.setHighId( id ); } public long getDefragCount() { return this.defragCount; } private static class IdRangeIterator { private int position = 0; private final long[] defrag; private final long start; private final int length; IdRangeIterator( IdRange idRange ) { this.defrag = idRange.getDefragIds(); this.start = idRange.getRangeStart(); this.length = idRange.getRangeLength(); } long next() { try { if ( position < defrag.length ) { return defrag[position]; } else { int offset = position - defrag.length; return ( offset < length ) ? ( start + offset ) : VALUE_REPRESENTING_NULL; } } finally { ++position; } } } private static IdRangeIterator EMPTY_ID_RANGE_ITERATOR = new IdRangeIterator( new IdRange( new long[0], 0, 0 ) ) { @Override long next() { return VALUE_REPRESENTING_NULL; }; }; }