@Override public void initialize( Supplier<KernelTransactionsSnapshot> transactionsSnapshotSupplier ) { bufferingIdGeneratorFactory.initialize( transactionsSnapshotSupplier ); } }
@Test void createCommunityBufferedContextByDefault() { IdContextFactory idContextFactory = IdContextFactoryBuilder.of( fs, jobScheduler ).build(); DatabaseIdContext idContext = idContextFactory.createIdContext( "database" ); IdGeneratorFactory idGeneratorFactory = idContext.getIdGeneratorFactory(); assertThat( idContext.getIdController(), instanceOf( BufferedIdController.class ) ); assertThat( idGeneratorFactory, instanceOf( BufferingIdGeneratorFactory.class ) ); ((BufferingIdGeneratorFactory)idGeneratorFactory).initialize( () -> mock( KernelTransactionsSnapshot.class ) ); idGeneratorFactory.open( testDirectory.file( "a"), IdType.NODE, () -> 0, 100 ).close(); idGeneratorFactory.open( testDirectory.file( "b"), IdType.PROPERTY, () -> 0, 100 ).close(); BufferingIdGeneratorFactory bufferedFactory = (BufferingIdGeneratorFactory) idGeneratorFactory; assertThat( bufferedFactory.get( IdType.NODE ), instanceOf( IdGeneratorImpl.class ) ); assertThat( bufferedFactory.get( IdType.PROPERTY ), not( instanceOf( IdGeneratorImpl.class ) ) ); }
@Test void createContextWithCustomIdGeneratorFactoryWhenProvided() { IdGeneratorFactory idGeneratorFactory = mock( IdGeneratorFactory.class ); IdContextFactory contextFactory = IdContextFactoryBuilder.of( fs, jobScheduler ).withIdGenerationFactoryProvider( any -> idGeneratorFactory ).build(); DatabaseIdContext idContext = contextFactory.createIdContext( "database" ); IdGeneratorFactory bufferedGeneratorFactory = idContext.getIdGeneratorFactory(); assertThat( idContext.getIdController(), instanceOf( BufferedIdController.class ) ); assertThat( bufferedGeneratorFactory, instanceOf( BufferingIdGeneratorFactory.class ) ); ((BufferingIdGeneratorFactory)bufferedGeneratorFactory).initialize( () -> mock( KernelTransactionsSnapshot.class ) ); File file = testDirectory.file( "a" ); IdType idType = IdType.NODE; LongSupplier highIdSupplier = () -> 0; int maxId = 100; idGeneratorFactory.open( file, idType, highIdSupplier, maxId ); verify( idGeneratorFactory ).open( file, idType, highIdSupplier, maxId ); }
@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 ); } }
@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 ); }
@Override public void initialize( Supplier<KernelTransactionsSnapshot> transactionsSnapshotSupplier ) { bufferingIdGeneratorFactory.initialize( transactionsSnapshotSupplier ); } }