@Override public void initialize( Supplier<KernelTransactionsSnapshot> transactionsSnapshotSupplier ) { bufferingIdGeneratorFactory.initialize( transactionsSnapshotSupplier ); } }
@Override public void maintenance() { bufferingIdGeneratorFactory.maintenance(); }
@Override public IdGenerator open( File filename, IdType idType, LongSupplier highId, long maxId ) { IdTypeConfiguration typeConfiguration = getIdTypeConfiguration( idType ); return open( filename, typeConfiguration.getGrabSize(), idType, highId, maxId ); }
@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 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 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 ) ) ); }
private DatabaseIdContext createBufferingIdContext( Function<String,? extends IdGeneratorFactory> idGeneratorFactoryProvider, JobScheduler jobScheduler, String databaseName ) { IdGeneratorFactory idGeneratorFactory = idGeneratorFactoryProvider.apply( databaseName ); BufferingIdGeneratorFactory bufferingIdGeneratorFactory = new BufferingIdGeneratorFactory( idGeneratorFactory, eligibleForIdReuse, idTypeConfigurationProvider ); BufferedIdController bufferingController = createBufferedIdController( bufferingIdGeneratorFactory, jobScheduler ); return createIdContext( bufferingIdGeneratorFactory, bufferingController ); }
@Override public void clear() { bufferingIdGeneratorFactory.clear(); }
@Override public IdGenerator open( File filename, int grabSize, IdType idType, LongSupplier highId, long maxId ) { assert boundaries != null : "Factory needs to be initialized before usage"; IdGenerator generator = delegate.open( filename, grabSize, idType, highId, maxId ); IdTypeConfiguration typeConfiguration = getIdTypeConfiguration(idType); if ( typeConfiguration.allowAggressiveReuse() ) { BufferingIdGenerator bufferingGenerator = new BufferingIdGenerator( generator ); bufferingGenerator.initialize( boundaries, safeThreshold ); overriddenIdGenerators[idType.ordinal()] = bufferingGenerator; generator = bufferingGenerator; } return generator; }
@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 ); }
mock( Procedures.class ), IOLimiter.UNLIMITED, databaseAvailabilityGuard, clock, new CanWrite(), new StoreCopyCheckPointMutex(), RecoveryCleanupWorkCollector.immediate(), new BufferedIdController( new BufferingIdGeneratorFactory( idGeneratorFactory, IdReuseEligibility.ALWAYS, idConfigurationProvider ), jobScheduler ), DatabaseInfo.COMMUNITY, new TransactionVersionContextSupplier(), ON_HEAP, Collections.emptyList(), file -> EMPTY_WATCHER, new GraphDatabaseFacade(), Iterables.empty() ) );
@Override public void clear() { bufferingIdGeneratorFactory.clear(); }
@Override public IdGenerator open( File filename, int grabSize, IdType idType, LongSupplier highId, long maxId ) { assert boundaries != null : "Factory needs to be initialized before usage"; IdGenerator generator = delegate.open( filename, grabSize, idType, highId, maxId ); IdTypeConfiguration typeConfiguration = getIdTypeConfiguration(idType); if ( typeConfiguration.allowAggressiveReuse() ) { BufferingIdGenerator bufferingGenerator = new BufferingIdGenerator( generator ); bufferingGenerator.initialize( boundaries, safeThreshold ); overriddenIdGenerators[idType.ordinal()] = bufferingGenerator; generator = bufferingGenerator; } return generator; }
@Override public IdGenerator open( File filename, IdType idType, LongSupplier highId, long maxId ) { IdTypeConfiguration typeConfiguration = getIdTypeConfiguration( idType ); return open( filename, typeConfiguration.getGrabSize(), idType, highId, maxId ); }
@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 ); }
@Override public void maintenance() { bufferingIdGeneratorFactory.maintenance(); }
private RecordStorageEngine get( FileSystemAbstraction fs, PageCache pageCache, IndexProvider indexProvider, DatabaseHealth databaseHealth, DatabaseLayout databaseLayout, Function<BatchTransactionApplierFacade, BatchTransactionApplierFacade> transactionApplierTransformer, Monitors monitors, LockService lockService ) { IdGeneratorFactory idGeneratorFactory = new EphemeralIdGenerator.Factory(); ExplicitIndexProvider explicitIndexProviderLookup = mock( ExplicitIndexProvider.class ); when( explicitIndexProviderLookup.allIndexProviders() ).thenReturn( Iterables.empty() ); IndexConfigStore indexConfigStore = new IndexConfigStore( databaseLayout, fs ); JobScheduler scheduler = life.add( createScheduler() ); Config config = Config.defaults( GraphDatabaseSettings.default_schema_provider, indexProvider.getProviderDescriptor().name() ); Dependencies dependencies = new Dependencies(); dependencies.satisfyDependency( indexProvider ); BufferingIdGeneratorFactory bufferingIdGeneratorFactory = new BufferingIdGeneratorFactory( idGeneratorFactory, IdReuseEligibility.ALWAYS, new CommunityIdTypeConfigurationProvider() ); DefaultIndexProviderMap indexProviderMap = new DefaultIndexProviderMap( dependencies, config ); NullLogProvider nullLogProvider = NullLogProvider.getInstance(); life.add( indexProviderMap ); return life.add( new ExtendedRecordStorageEngine( databaseLayout, config, pageCache, fs, nullLogProvider, nullLogProvider, mockedTokenHolders(), mock( SchemaState.class ), new StandardConstraintSemantics(), scheduler, mock( TokenNameLookup.class ), lockService, indexProviderMap, IndexingService.NO_MONITOR, databaseHealth, explicitIndexProviderLookup, indexConfigStore, new SynchronizedArrayIdOrderingQueue(), idGeneratorFactory, new BufferedIdController( bufferingIdGeneratorFactory, scheduler ), transactionApplierTransformer, monitors, RecoveryCleanupWorkCollector.immediate(), OperationalMode.single ) ); }
@Override public void initialize( Supplier<KernelTransactionsSnapshot> transactionsSnapshotSupplier ) { bufferingIdGeneratorFactory.initialize( transactionsSnapshotSupplier ); } }
private DatabaseIdContext createBufferingIdContext( Function<String,? extends IdGeneratorFactory> idGeneratorFactoryProvider, JobScheduler jobScheduler, String databaseName ) { IdGeneratorFactory idGeneratorFactory = idGeneratorFactoryProvider.apply( databaseName ); BufferingIdGeneratorFactory bufferingIdGeneratorFactory = new BufferingIdGeneratorFactory( idGeneratorFactory, eligibleForIdReuse, idTypeConfigurationProvider ); BufferedIdController bufferingController = createBufferedIdController( bufferingIdGeneratorFactory, jobScheduler ); return createIdContext( bufferingIdGeneratorFactory, bufferingController ); }