@Override public void freeId( long id ) { delegate.freeId( id ); }
/** * Frees an id for this store's {@link IdGenerator}. * * @param id The id to free */ @Override public void freeId( long id ) { IdGenerator generator = this.idGenerator; if ( generator != null ) { generator.freeId( id ); } // else we're deleting records as part of applying transactions during recovery, and that's fine }
@Test void createContextWithProvidedReusabilityCheck() { IdReuseEligibility reuseEligibility = mock( IdReuseEligibility.class ); IdContextFactory contextFactory = IdContextFactoryBuilder.of( fs, jobScheduler ).withIdReuseEligibility( reuseEligibility ).build(); DatabaseIdContext idContext = contextFactory.createIdContext( "database" ); IdGeneratorFactory bufferedGeneratorFactory = idContext.getIdGeneratorFactory(); assertThat( bufferedGeneratorFactory, instanceOf( BufferingIdGeneratorFactory.class ) ); BufferingIdGeneratorFactory bufferedFactory = (BufferingIdGeneratorFactory) bufferedGeneratorFactory; KernelTransactionsSnapshot snapshot = mock( KernelTransactionsSnapshot.class ); when( snapshot.allClosed() ).thenReturn( true ); bufferedFactory.initialize( () -> snapshot ); try ( IdGenerator idGenerator = bufferedFactory.open( testDirectory.file( "a" ), IdType.PROPERTY, () -> 100, 100 ) ) { idGenerator.freeId( 15 ); bufferedFactory.maintenance(); verify( reuseEligibility ).isEligible( snapshot ); } }
if ( rIndex < releaseIndex && currentIdCount > 0 ) idGenerator.freeId( idsTaken.remove( random.nextInt( currentIdCount ) ).intValue() ); currentIdCount--;
@Test public void shouldFreeSecondaryUnitIdOfDeletedRecord() throws Exception { // GIVEN EphemeralFileSystemAbstraction fs = efs.get(); nodeStore = newNodeStore( fs ); NodeRecord record = new NodeRecord( 5L ); record.setRequiresSecondaryUnit( true ); record.setSecondaryUnitId( 10L ); record.setInUse( true ); nodeStore.updateRecord( record ); nodeStore.setHighestPossibleIdInUse( 10L ); // WHEN record.setInUse( false ); nodeStore.updateRecord( record ); // THEN IdGenerator idGenerator = idGeneratorFactory.get( IdType.NODE ); verify( idGenerator ).freeId( 5L ); verify( idGenerator ).freeId( 10L ); }
@Test public void shouldFreeSecondaryUnitIdOfShrunkRecord() throws Exception { // GIVEN EphemeralFileSystemAbstraction fs = efs.get(); nodeStore = newNodeStore( fs ); NodeRecord record = new NodeRecord( 5L ); record.setRequiresSecondaryUnit( true ); record.setSecondaryUnitId( 10L ); record.setInUse( true ); nodeStore.updateRecord( record ); nodeStore.setHighestPossibleIdInUse( 10L ); // WHEN record.setRequiresSecondaryUnit( false ); nodeStore.updateRecord( record ); // THEN IdGenerator idGenerator = idGeneratorFactory.get( IdType.NODE ); verify( idGenerator, never() ).freeId( 5L ); verify( idGenerator ).freeId( 10L ); }
@Test public void shouldDelayFreeingOfAggressivelyReusedIds() { // GIVEN MockedIdGeneratorFactory actual = new MockedIdGeneratorFactory(); ControllableSnapshotSupplier boundaries = new ControllableSnapshotSupplier(); BufferingIdGeneratorFactory bufferingIdGeneratorFactory = new BufferingIdGeneratorFactory( actual, IdReuseEligibility.ALWAYS, new CommunityIdTypeConfigurationProvider() ); bufferingIdGeneratorFactory.initialize( boundaries ); IdGenerator idGenerator = bufferingIdGeneratorFactory.open( new File( "doesnt-matter" ), 10, IdType.STRING_BLOCK, () -> 0L, Integer.MAX_VALUE ); // WHEN idGenerator.freeId( 7 ); verifyNoMoreInteractions( actual.get( IdType.STRING_BLOCK ) ); // after some maintenance and transaction still not closed bufferingIdGeneratorFactory.maintenance(); verifyNoMoreInteractions( actual.get( IdType.STRING_BLOCK ) ); // although after transactions have all closed boundaries.setMostRecentlyReturnedSnapshotToAllClosed(); bufferingIdGeneratorFactory.maintenance(); // THEN verify( actual.get( IdType.STRING_BLOCK ) ).freeId( 7 ); }
@Test public void shouldDelayFreeingOfAggressivelyReusedIdsConsideringTimeAsWell() { // GIVEN MockedIdGeneratorFactory actual = new MockedIdGeneratorFactory(); final FakeClock clock = Clocks.fakeClock(); final long safeZone = MINUTES.toMillis( 1 ); ControllableSnapshotSupplier boundaries = new ControllableSnapshotSupplier(); BufferingIdGeneratorFactory bufferingIdGeneratorFactory = new BufferingIdGeneratorFactory( actual, t -> clock.millis() - t.snapshotTime() >= safeZone, new CommunityIdTypeConfigurationProvider() ); bufferingIdGeneratorFactory.initialize( boundaries ); IdGenerator idGenerator = bufferingIdGeneratorFactory.open( new File( "doesnt-matter" ), 10, IdType.STRING_BLOCK, () -> 0L, Integer.MAX_VALUE ); // WHEN idGenerator.freeId( 7 ); verifyNoMoreInteractions( actual.get( IdType.STRING_BLOCK ) ); // after some maintenance and transaction still not closed bufferingIdGeneratorFactory.maintenance(); verifyNoMoreInteractions( actual.get( IdType.STRING_BLOCK ) ); // although after transactions have all closed boundaries.setMostRecentlyReturnedSnapshotToAllClosed(); bufferingIdGeneratorFactory.maintenance(); // ... the clock would still say "nope" so no interaction verifyNoMoreInteractions( actual.get( IdType.STRING_BLOCK ) ); // then finally after time has passed as well clock.forward( 70, SECONDS ); bufferingIdGeneratorFactory.maintenance(); // THEN verify( actual.get( IdType.STRING_BLOCK ) ).freeId( 7 ); }
idGenerator.freeId( id );
idGenerator.freeId( 1 ); idGenerator.freeId( 3 ); idGenerator.freeId( 5 ); assertEquals( 7L, idGenerator.nextId() ); idGenerator.freeId( 6 ); closeIdGenerator( idGenerator ); idGenerator = new IdGeneratorImpl( fs, idGeneratorFile(), 5, 1000, false, IdType.NODE, () -> 0L ); idGenerator.freeId( 2 ); idGenerator.freeId( 4 ); assertEquals( 1L, idGenerator.nextId() ); idGenerator.freeId( 1 ); assertEquals( 3L, idGenerator.nextId() ); idGenerator.freeId( 3 ); assertEquals( 5L, idGenerator.nextId() ); idGenerator.freeId( 5 ); assertEquals( 6L, idGenerator.nextId() ); idGenerator.freeId( 6 ); assertEquals( 8L, idGenerator.nextId() ); idGenerator.freeId( 8 ); assertEquals( 9L, idGenerator.nextId() ); idGenerator.freeId( 9 ); closeIdGenerator( idGenerator ); idGenerator = new IdGeneratorImpl( fs, idGeneratorFile(), 3, 1000, false, IdType.NODE, () -> 0L );
@Test public void makeSureMagicMinusOneCannotBeReturnedEvenIfFreed() { IdGeneratorImpl.createGenerator( fs, idGeneratorFile(), 0, false ); IdGenerator idGenerator = new IdGeneratorImpl( fs, idGeneratorFile(), 1, new NodeRecordFormat().getMaxId(), false, IdType.NODE, () -> 0L ); long magicMinusOne = (long) Math.pow( 2, 32 ) - 1; idGenerator.setHighId( magicMinusOne ); assertEquals( magicMinusOne + 1, idGenerator.nextId() ); idGenerator.freeId( magicMinusOne - 1 ); idGenerator.freeId( magicMinusOne ); closeIdGenerator( idGenerator ); idGenerator = new IdGeneratorImpl( fs, idGeneratorFile(), 1, new NodeRecordFormat().getMaxId(), false, IdType.NODE, () -> 0L ); assertEquals( magicMinusOne - 1, idGenerator.nextId() ); assertEquals( magicMinusOne + 2, idGenerator.nextId() ); closeIdGenerator( idGenerator ); }
@Test public void correctDefragCountWhenHaveIdsInFile() { IdGeneratorImpl.createGenerator( fsr.get(), file, 100, false ); IdGenerator idGenerator = new IdGeneratorImpl( fsr.get(), file, 100, 100, true, IdType.NODE, () -> 100L ); idGenerator.freeId( 5 ); idGenerator.close(); IdGenerator reloadedIdGenerator = new IdGeneratorImpl( fsr.get(), file, 100, 100, true, IdType.NODE, () -> 100L ); assertEquals( 1, reloadedIdGenerator.getDefragCount() ); assertEquals( 5, reloadedIdGenerator.nextId() ); assertEquals( 0, reloadedIdGenerator.getDefragCount() ); }
long id = idGenerator.nextId(); assertEquals( recordFormat.getMaxId(), id ); idGenerator.freeId( id ); try
@Override public void freeId( long id ) { delegate.freeId( id ); }
@Override public void freeId( long id ) { delegate.freeId( id ); }
/** * Frees an id for this store's {@link IdGenerator}. * * @param id The id to free */ @Override public void freeId( long id ) { IdGenerator generator = this.idGenerator; if ( generator != null ) { generator.freeId( id ); } // else we're deleting records as part of applying transactions during recovery, and that's fine }