From 46b4df583cdd98b1855a2e6fe00371ab027e562a Mon Sep 17 00:00:00 2001 From: Anton Klaren Date: Tue, 30 May 2017 14:42:41 +0200 Subject: [PATCH] Moved highId responsibility to IdGenerator --- .../kernel/impl/store/id/FreeIdKeeper.java | 17 ++-- .../kernel/impl/store/id/HighIdKeeper.java | 41 -------- .../neo4j/kernel/impl/store/id/IdFile.java | 97 ++++++++----------- .../kernel/impl/store/id/IdGeneratorImpl.java | 33 ++++--- .../impl/store/id/FreeIdKeeperTest.java | 71 +++++++++----- 5 files changed, 109 insertions(+), 150 deletions(-) delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/store/id/HighIdKeeper.java diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/id/FreeIdKeeper.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/id/FreeIdKeeper.java index ce12969cedaca..8309faa08a8a6 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/id/FreeIdKeeper.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/id/FreeIdKeeper.java @@ -27,6 +27,8 @@ import org.neo4j.io.fs.StoreChannel; import org.neo4j.kernel.impl.store.UnderlyingStorageException; +import static org.neo4j.kernel.impl.store.id.IdFile.NO_RESULT; + /** * Instances of this class maintain a list of free ids with the potential to overflow to disk if the number * of free ids becomes too large. @@ -41,8 +43,8 @@ */ public class FreeIdKeeper implements Closeable { - public static final long NO_RESULT = -1; - public static final int ID_ENTRY_SIZE = Long.BYTES; + + private static final int ID_ENTRY_SIZE = Long.BYTES; private final LinkedList freeIds = new LinkedList<>(); private final LinkedList readFromDisk = new LinkedList<>(); private final StoreChannel channel; @@ -66,22 +68,17 @@ public class FreeIdKeeper implements Closeable private long maxReadPosition; private long readPosition; // the place from where we read. Always <= maxReadPosition - public FreeIdKeeper( StoreChannel channel, int threshold, boolean aggressiveReuse ) throws IOException + public FreeIdKeeper( StoreChannel channel, int threshold, boolean aggressiveReuse, int initialPosition ) throws IOException { this.channel = channel; this.threshold = threshold; this.aggressiveReuse = aggressiveReuse; - this.lowWatermarkForChannelPosition = channel.position(); + this.lowWatermarkForChannelPosition = initialPosition; readPosition = lowWatermarkForChannelPosition; - restoreIdsOnStartup(); - } - private void restoreIdsOnStartup() throws IOException - { // this is always true regardless of aggressiveReuse. It only matters once we start writing - maxReadPosition = channel.size(); + maxReadPosition = this.channel.size(); defraggedIdCount = ( maxReadPosition - lowWatermarkForChannelPosition ) / ID_ENTRY_SIZE; - readIdBatch(); } public void freeId( long id ) diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/id/HighIdKeeper.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/id/HighIdKeeper.java deleted file mode 100644 index 34345233e7db7..0000000000000 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/id/HighIdKeeper.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2002-2017 "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 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.kernel.impl.store.id; - -import org.apache.commons.lang3.mutable.MutableLong; - -/** - * Created by klaren on 2017-05-30. - */ -public class HighIdKeeper -{ - private final MutableLong highId = new MutableLong( -1 ); - - public long getHighId() - { - return highId.longValue(); - } - - public void setHighId( long id ) - { - this.highId.setValue( id ); - } - -} diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/id/IdFile.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/id/IdFile.java index e889f611ff67e..b1e286c37c3cb 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/id/IdFile.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/id/IdFile.java @@ -19,7 +19,6 @@ */ package org.neo4j.kernel.impl.store.id; -import java.io.Closeable; import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; @@ -29,11 +28,11 @@ import org.neo4j.kernel.impl.store.InvalidIdGeneratorException; import org.neo4j.kernel.impl.store.UnderlyingStorageException; -import static java.lang.Math.max; - -public class IdFile implements Closeable +public class IdFile { + public static final long NO_RESULT = -1; + // sticky(byte), nextFreeId(long) public static final int HEADER_SIZE = Byte.BYTES + Long.BYTES; @@ -47,14 +46,15 @@ public class IdFile implements Closeable private StoreChannel fileChannel; private FreeIdKeeper freeIdKeeper; - private final HighIdKeeper highIdKeeper = new HighIdKeeper(); private final int grabSize; private final boolean aggressiveReuse; + private long initialHighId; + private boolean closed = true; - public IdFile( FileSystemAbstraction fs, File file, int grabSize, boolean aggressiveReuse, long highId ) + public IdFile( FileSystemAbstraction fs, File file, int grabSize, boolean aggressiveReuse ) { if ( grabSize < 1 ) { @@ -65,7 +65,6 @@ public IdFile( FileSystemAbstraction fs, File file, int grabSize, boolean aggres this.fs = fs; this.grabSize = grabSize; this.aggressiveReuse = aggressiveReuse; - highIdKeeper.setHighId( highId ); } // initialize the id generator and performs a simple validation @@ -74,13 +73,10 @@ void init() try { fileChannel = fs.open( file, "rw" ); + initialHighId = readAndValidateHeader(); + markAsSticky(); - ByteBuffer buffer = readHeader(); - highIdKeeper.setHighId( max( buffer.getLong(), highIdKeeper.getHighId() ) ); - markAsSticky( buffer ); - - fileChannel.position( HEADER_SIZE ); - this.freeIdKeeper = new FreeIdKeeper( fileChannel, grabSize, aggressiveReuse ); + this.freeIdKeeper = new FreeIdKeeper( fileChannel, grabSize, aggressiveReuse, HEADER_SIZE ); closed = false; } catch ( IOException e ) @@ -95,6 +91,11 @@ public boolean isClosed() return closed; } + public long getInitialHighId() + { + return initialHighId; + } + void assertStillOpen() { if ( closed ) @@ -103,13 +104,11 @@ void assertStillOpen() } } - private ByteBuffer readHeader() throws IOException + private long readAndValidateHeader() throws IOException { try { - ByteBuffer buffer = readHighIdFromHeader( fileChannel, file ); - - return buffer; + return readAndValidate( fileChannel, file ); } catch ( InvalidIdGeneratorException e ) { @@ -118,7 +117,7 @@ private ByteBuffer readHeader() throws IOException } } - private static ByteBuffer readHighIdFromHeader( StoreChannel channel, File fileName ) throws IOException + private static long readAndValidate( StoreChannel channel, File fileName ) throws IOException { ByteBuffer buffer = ByteBuffer.allocate( HEADER_SIZE ); int read = channel.read( buffer ); @@ -134,32 +133,36 @@ private static ByteBuffer readHighIdFromHeader( StoreChannel channel, File fileN throw new InvalidIdGeneratorException( "Sticky generator[ " + fileName + "] delete this id file and build a new one" ); } - return buffer; + return buffer.getLong(); } public static long readHighId( FileSystemAbstraction fileSystem, File file ) throws IOException { try ( StoreChannel channel = fileSystem.open( file, "r" ) ) { - return readHighIdFromHeader( channel, file ).getLong(); + return readAndValidate( channel, file ); } } - /** - * Made available for testing purposes. - * Marks an id generator as sticky, i.e. not cleanly shut down. - */ - public void markAsSticky( ByteBuffer buffer ) throws IOException + private void markAsSticky() throws IOException { - buffer.clear(); - buffer.put( STICKY_GENERATOR ).limit( 1 ).flip(); + ByteBuffer buffer = ByteBuffer.allocate( Byte.BYTES ); + buffer.put( STICKY_GENERATOR ).flip(); fileChannel.position( 0 ); fileChannel.write( buffer ); fileChannel.force( false ); } - @Override - public void close() + private void markAsCleanlyClosed( ) throws IOException + { + // remove sticky + ByteBuffer buffer = ByteBuffer.allocate( Byte.BYTES ); + buffer.put( CLEAN_GENERATOR ).flip(); + fileChannel.position( 0 ); + fileChannel.write( buffer ); + } + + public void close( long highId ) { if ( closed ) { @@ -169,11 +172,10 @@ public void close() try { freeIdKeeper.close(); // first write out free ids, then mark as clean - ByteBuffer buffer = ByteBuffer.allocate( HEADER_SIZE ); - writeHeader( buffer ); + writeHeader( highId ); fileChannel.force( false ); - markAsCleanlyClosed( buffer ); + markAsCleanlyClosed( ); closeChannel(); } @@ -191,26 +193,13 @@ private void closeChannel() throws IOException fileChannel.close(); fileChannel = null; closed = true; - // make this generator unusable - highIdKeeper.setHighId( -1L ); - } - - private void markAsCleanlyClosed( ByteBuffer buffer ) throws IOException - { - // remove sticky - buffer.clear(); - buffer.put( CLEAN_GENERATOR ); - buffer.limit( 1 ); - buffer.flip(); - fileChannel.position( 0 ); - fileChannel.write( buffer ); } - private void writeHeader( ByteBuffer buffer ) throws IOException + private void writeHeader( long highId ) throws IOException { + ByteBuffer buffer = ByteBuffer.allocate( HEADER_SIZE ); + buffer.put( STICKY_GENERATOR ).putLong( highId ).flip(); fileChannel.position( 0 ); - buffer.put( STICKY_GENERATOR ).putLong( highIdKeeper.getHighId() ); - buffer.flip(); fileChannel.write( buffer ); } @@ -243,16 +232,6 @@ public long getReuseableId() return freeIdKeeper.getId(); } - public long getHighId() - { - return highIdKeeper.getHighId(); - } - - public void setHighId( long highId ) - { - highIdKeeper.setHighId( highId ); - } - public void freeId( long id ) { freeIdKeeper.freeId( id ); @@ -307,7 +286,7 @@ public static void createEmptyIdFile( FileSystemAbstraction fs, File fileName, l public String toString() { return "IdFile{" + "file=" + file + ", fs=" + fs + ", fileChannel=" + fileChannel + ", defragCount=" + - freeIdKeeper.getCount() + ", highIdKeeper=" + highIdKeeper + ", grabSize=" + grabSize + ", aggressiveReuse=" + + freeIdKeeper.getCount() + ", grabSize=" + grabSize + ", aggressiveReuse=" + aggressiveReuse + ", closed=" + closed + '}'; } } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/id/IdGeneratorImpl.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/id/IdGeneratorImpl.java index e44ecfcd025cd..620a67e7ab22b 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/id/IdGeneratorImpl.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/id/IdGeneratorImpl.java @@ -26,6 +26,8 @@ import org.neo4j.kernel.impl.store.UnderlyingStorageException; import org.neo4j.kernel.impl.store.id.validation.IdValidator; +import static java.lang.Math.max; + /** * This class generates unique ids for a resource type. For example, nodes in a * nodes space are connected to each other via relationships. On nodes and @@ -68,6 +70,8 @@ public class IdGeneratorImpl implements IdGenerator private IdFile idFile; + private long highId = INTEGER_MINUS_ONE; + /** * Opens the id generator represented by fileName. The * grabSize means how many defragged ids we should keep in @@ -99,8 +103,9 @@ public IdGeneratorImpl( FileSystemAbstraction fs, File file, int grabSize, long { this.max = max; - this.idFile = new IdFile( fs, file, grabSize, aggressiveReuse, highId ); + this.idFile = new IdFile( fs, file, grabSize, aggressiveReuse ); this.idFile.init(); + this.highId = max( idFile.getInitialHighId(), highId ); } /** @@ -119,19 +124,17 @@ public synchronized long nextId() { assertStillOpen(); long nextDefragId = idFile.getReuseableId(); - if ( nextDefragId != -1 ) + if ( nextDefragId != IdFile.NO_RESULT ) { return nextDefragId; } - long id = idFile.getHighId(); - if ( IdValidator.isReservedId( id ) ) + if ( IdValidator.isReservedId( highId ) ) { - id++; + highId++; } - IdValidator.assertValidId( id, max ); - idFile.setHighId( id + 1 ); - return id; + IdValidator.assertValidId( highId, max ); + return highId++; } private void assertStillOpen() @@ -163,7 +166,7 @@ public synchronized IdRange nextIdBatch( int size ) System.arraycopy( tmpArray, 0, defragIds, 0, count ); int sizeLeftForRange = size - count; - long start = idFile.getHighId(); + long start = highId; setHighId( start + sizeLeftForRange ); return new IdRange( defragIds, start, sizeLeftForRange ); } @@ -178,7 +181,7 @@ public synchronized IdRange nextIdBatch( int size ) public void setHighId( long id ) { IdValidator.assertIdWithinCapacity( id, max ); - idFile.setHighId( id ); + highId = id; } /** @@ -190,7 +193,7 @@ public void setHighId( long id ) @Override public long getHighId() { - return idFile.getHighId(); + return highId; } @Override @@ -222,9 +225,9 @@ public synchronized void freeId( long id ) return; } - if ( id < 0 || id >= idFile.getHighId() ) + if ( id < 0 || id >= highId ) { - throw new IllegalArgumentException( "Illegal id[" + id + "], highId is " + idFile.getHighId() ); + throw new IllegalArgumentException( "Illegal id[" + id + "], highId is " + highId ); } idFile.freeId( id ); } @@ -241,7 +244,7 @@ public synchronized void freeId( long id ) @Override public synchronized void close() { - idFile.close(); + idFile.close( highId ); } /** @@ -265,7 +268,7 @@ public static long readHighId( FileSystemAbstraction fileSystem, File file ) thr @Override public synchronized long getNumberOfIdsInUse() { - return idFile.getHighId() - getDefragCount(); + return highId - getDefragCount(); } @Override diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/store/id/FreeIdKeeperTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/store/id/FreeIdKeeperTest.java index aba82da96d09e..9a5ec16bf3b82 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/store/id/FreeIdKeeperTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/store/id/FreeIdKeeperTest.java @@ -23,6 +23,7 @@ import org.junit.Test; import java.io.File; +import java.io.IOException; import java.nio.ByteBuffer; import java.util.HashSet; import java.util.Set; @@ -39,7 +40,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; -import static org.neo4j.kernel.impl.store.id.FreeIdKeeper.NO_RESULT; +import static org.neo4j.kernel.impl.store.id.IdFile.NO_RESULT; public class FreeIdKeeperTest { @@ -52,7 +53,7 @@ public void newlyConstructedInstanceShouldReportProperDefaultValues() throws Exc // Given StoreChannel channel = mock( StoreChannel.class ); int threshold = 10; - FreeIdKeeper keeper = new FreeIdKeeper( channel, threshold, true ); + FreeIdKeeper keeper = getFreeIdKeeper( channel, threshold ); // when // then @@ -66,7 +67,7 @@ public void freeingAnIdShouldReturnThatIdAndUpdateTheCountWhenAggressiveReuseIsS // Given StoreChannel channel = mock( StoreChannel.class ); int threshold = 10; - FreeIdKeeper keeper = new FreeIdKeeper( channel, threshold, true ); + FreeIdKeeper keeper = getFreeIdKeeperAggressive( channel, threshold ); // when keeper.freeId( 13 ); @@ -88,7 +89,7 @@ public void shouldReturnMinusOneWhenRunningOutOfIds() throws Exception // Given StoreChannel channel = mock( StoreChannel.class ); int threshold = 10; - FreeIdKeeper keeper = new FreeIdKeeper( channel, threshold, true ); + FreeIdKeeper keeper = getFreeIdKeeperAggressive( channel, threshold ); // when keeper.freeId( 13 ); @@ -106,7 +107,7 @@ public void shouldOnlyOverflowWhenThresholdIsReached() throws Exception StoreChannel channel = spy( fs.get().open( new File( "id.file" ), "rw" ) ); int threshold = 10; - FreeIdKeeper keeper = new FreeIdKeeper( channel, threshold, true ); + FreeIdKeeper keeper = getFreeIdKeeper( channel, threshold ); reset( channel ); // because we get the position in the constructor, we need to reset all calls on the spy // when @@ -130,10 +131,10 @@ public void shouldOnlyOverflowWhenThresholdIsReached() throws Exception public void shouldReadBackPersistedIdsWhenAggressiveReuseIsSet() throws Exception { // given - StoreChannel channel = fs.get().open( new File( "id.file" ), "rw" ); + StoreChannel channel = getStoreChannel(); int threshold = 10; - FreeIdKeeper keeper = new FreeIdKeeper( channel, threshold, true ); + FreeIdKeeper keeper = getFreeIdKeeperAggressive( channel, threshold ); // when // we store enough ids to cause overflow to file @@ -154,10 +155,10 @@ public void shouldReadBackPersistedIdsWhenAggressiveReuseIsSet() throws Exceptio public void shouldReadBackManyPersistedIdBatchesWhenAggressiveReuseIsSet() throws Exception { // given - StoreChannel channel = fs.get().open( new File( "id.file" ), "rw" ); + StoreChannel channel = getStoreChannel(); int threshold = 10; - FreeIdKeeper keeper = new FreeIdKeeper( channel, threshold, true ); + FreeIdKeeper keeper = getFreeIdKeeperAggressive( channel, threshold ); Set freeIds = new HashSet<>(); // when @@ -182,10 +183,10 @@ public void shouldFirstReturnNonPersistedIdsAndThenPersistedOnesWhenAggressiveRe { // this is testing the stack property, but from the viewpoint of avoiding unnecessary disk reads // given - StoreChannel channel = fs.get().open( new File( "id.file" ), "rw" ); + StoreChannel channel = getStoreChannel(); int threshold = 10; - FreeIdKeeper keeper = new FreeIdKeeper( channel, threshold, true ); + FreeIdKeeper keeper = getFreeIdKeeperAggressive( channel, threshold ); // when // we store enough ids to cause overflow to file @@ -218,10 +219,10 @@ public void shouldFirstReturnNonPersistedIdsAndThenPersistedOnesWhenAggressiveRe public void persistedIdsShouldStillBeCounted() throws Exception { // given - StoreChannel channel = fs.get().open( new File( "id.file" ), "rw" ); + StoreChannel channel = getStoreChannel(); int threshold = 10; - FreeIdKeeper keeper = new FreeIdKeeper( channel, threshold, true ); + FreeIdKeeper keeper = new FreeIdKeeper( channel, threshold, true, 0 ); // when // we store enough ids to cause overflow to file @@ -245,10 +246,10 @@ public void persistedIdsShouldStillBeCounted() throws Exception public void shouldStoreAndRestoreIds() throws Exception { // given - StoreChannel channel = fs.get().open( new File( "id.file" ), "rw" ); + StoreChannel channel = getStoreChannel(); int threshold = 10; - FreeIdKeeper keeper = new FreeIdKeeper( channel, threshold, true ); + FreeIdKeeper keeper = getFreeIdKeeper( channel, threshold ); Set freeIds = new HashSet<>(); // stack guarantees are not maintained between restarts // when @@ -270,7 +271,7 @@ public void shouldStoreAndRestoreIds() throws Exception channel.close(); // and then we open a new one over the same file channel = fs.get().open( new File( "id.file" ), "rw" ); - keeper = new FreeIdKeeper( channel, threshold, true ); + keeper = getFreeIdKeeper( channel, threshold ); // then // the count should be returned correctly @@ -288,10 +289,10 @@ public void shouldStoreAndRestoreIds() throws Exception public void shouldNotReturnNewlyReleasedIdsIfAggressiveIsFalse() throws Exception { // given - StoreChannel channel = fs.get().open( new File( "id.file" ), "rw" ); + StoreChannel channel = getStoreChannel(); int threshold = 10; - FreeIdKeeper keeper = new FreeIdKeeper( channel, threshold, false ); + FreeIdKeeper keeper = getFreeIdKeeper( (StoreChannel) channel, (int) threshold ); // when keeper.freeId( 1 ); @@ -308,7 +309,7 @@ public void shouldNotReturnIdsPersistedDuringThisRunIfAggressiveIsFalse() throws StoreChannel channel = spy( fs.get().open( new File( "id.file" ), "rw" ) ); int threshold = 10; - FreeIdKeeper keeper = new FreeIdKeeper( channel, threshold, false ); + FreeIdKeeper keeper = getFreeIdKeeper( (StoreChannel) channel, (int) threshold ); // when // enough ids are persisted to overflow @@ -328,10 +329,10 @@ public void shouldNotReturnIdsPersistedDuringThisRunIfAggressiveIsFalse() throws public void shouldReturnIdsRestoredAndIgnoreNewlyReleasedIfAggressiveReuseIsFalse() throws Exception { // given - StoreChannel channel = fs.get().open( new File( "id.file" ), "rw" ); + StoreChannel channel = getStoreChannel(); int threshold = 10; - FreeIdKeeper keeper = new FreeIdKeeper( channel, threshold, false ); + FreeIdKeeper keeper = getFreeIdKeeper( (StoreChannel) channel, (int) threshold ); Set freeIds = new HashSet<>(); for ( long i = 0; i < threshold; i++ ) { @@ -342,7 +343,7 @@ public void shouldReturnIdsRestoredAndIgnoreNewlyReleasedIfAggressiveReuseIsFals channel.close(); // and then we open a new one over the same file channel = fs.get().open( new File( "id.file" ), "rw" ); - keeper = new FreeIdKeeper( channel, threshold, false ); + keeper = getFreeIdKeeper( (StoreChannel) channel, (int) threshold ); // when // we release some ids that spill to disk @@ -368,10 +369,10 @@ public void shouldReturnNoResultIfIdsAreRestoredAndExhaustedAndThereAreFreeIdsFr throws Exception { // given - StoreChannel channel = fs.get().open( new File( "id.file" ), "rw" ); + StoreChannel channel = getStoreChannel(); int threshold = 10; - FreeIdKeeper keeper = new FreeIdKeeper( channel, threshold, false ); + FreeIdKeeper keeper = getFreeIdKeeper( (StoreChannel) channel, (int) threshold ); Set freeIds = new HashSet<>(); for ( long i = 0; i < threshold; i++ ) { @@ -382,7 +383,7 @@ public void shouldReturnNoResultIfIdsAreRestoredAndExhaustedAndThereAreFreeIdsFr channel.close(); // and then we open a new one over the same file channel = fs.get().open( new File( "id.file" ), "rw" ); - keeper = new FreeIdKeeper( channel, threshold, false ); + keeper = getFreeIdKeeper( (StoreChannel) channel, (int) threshold ); // when - then // we exhaust all ids restored @@ -402,4 +403,24 @@ public void shouldReturnNoResultIfIdsAreRestoredAndExhaustedAndThereAreFreeIdsFr // we should have no ids to return assertEquals( NO_RESULT, keeper.getId() ); } + + private FreeIdKeeper getFreeIdKeeper( StoreChannel channel, int threshold ) throws IOException + { + return getFreeIdKeeper( channel, threshold, false ); + } + + private FreeIdKeeper getFreeIdKeeperAggressive( StoreChannel channel, int threshold ) throws IOException + { + return getFreeIdKeeper( channel, threshold, true ); + } + + private FreeIdKeeper getFreeIdKeeper( StoreChannel channel, int threshold, boolean aggresiveReuse ) throws IOException + { + return new FreeIdKeeper( channel, threshold, aggresiveReuse, 0 ); + } + + private StoreChannel getStoreChannel() throws IOException + { + return fs.get().open( new File( "id.file" ), "rw" ); + } }