2121import static org .apache .bookkeeper .mledger .ManagedLedgerException .getManagedLedgerException ;
2222import static org .apache .bookkeeper .mledger .impl .ManagedLedgerImpl .NULL_OFFLOAD_PROMISE ;
2323import static org .apache .pulsar .common .util .Runnables .catchingAndLoggingThrowables ;
24+ import com .google .common .annotations .VisibleForTesting ;
2425import com .google .common .base .Predicates ;
2526import com .google .common .collect .BoundType ;
2627import com .google .common .collect .Maps ;
@@ -118,6 +119,7 @@ public class ManagedLedgerFactoryImpl implements ManagedLedgerFactory {
118119 private final ManagedLedgerFactoryConfig config ;
119120 @ Getter
120121 protected final OrderedScheduler scheduledExecutor ;
122+ @ Getter
121123 private final ScheduledExecutorService cacheEvictionExecutor ;
122124
123125 @ Getter
@@ -147,6 +149,7 @@ public class ManagedLedgerFactoryImpl implements ManagedLedgerFactory {
147149 */
148150 @ Getter
149151 private boolean metadataServiceAvailable ;
152+ private final ManagedLedgerConfig defaultManagedLedgerConfig ;
150153
151154 private static class PendingInitializeManagedLedger {
152155
@@ -170,7 +173,8 @@ public ManagedLedgerFactoryImpl(MetadataStoreExtended metadataStore, ClientConfi
170173 ManagedLedgerFactoryConfig config )
171174 throws Exception {
172175 this (metadataStore , new DefaultBkFactory (bkClientConfiguration ),
173- true /* isBookkeeperManaged */ , config , NullStatsLogger .INSTANCE , OpenTelemetry .noop ());
176+ true /* isBookkeeperManaged */ , config , NullStatsLogger .INSTANCE , OpenTelemetry .noop (),
177+ new ManagedLedgerConfig ());
174178 }
175179
176180 public ManagedLedgerFactoryImpl (MetadataStoreExtended metadataStore , BookKeeper bookKeeper )
@@ -181,15 +185,23 @@ public ManagedLedgerFactoryImpl(MetadataStoreExtended metadataStore, BookKeeper
181185 public ManagedLedgerFactoryImpl (MetadataStoreExtended metadataStore , BookKeeper bookKeeper ,
182186 ManagedLedgerFactoryConfig config )
183187 throws Exception {
184- this (metadataStore , (policyConfig ) -> CompletableFuture .completedFuture (bookKeeper ), config );
188+ this (metadataStore , bookKeeper , config , new ManagedLedgerConfig ());
189+ }
190+
191+ public ManagedLedgerFactoryImpl (MetadataStoreExtended metadataStore , BookKeeper bookKeeper ,
192+ ManagedLedgerFactoryConfig config , ManagedLedgerConfig defaultManagedLedgerConfig )
193+ throws Exception {
194+ this (metadataStore , (policyConfig ) -> CompletableFuture .completedFuture (bookKeeper ),
195+ false /* isBookkeeperManaged */ , config , NullStatsLogger .INSTANCE , OpenTelemetry .noop (),
196+ defaultManagedLedgerConfig );
185197 }
186198
187199 public ManagedLedgerFactoryImpl (MetadataStoreExtended metadataStore ,
188200 BookkeeperFactoryForCustomEnsemblePlacementPolicy bookKeeperGroupFactory ,
189201 ManagedLedgerFactoryConfig config )
190202 throws Exception {
191203 this (metadataStore , bookKeeperGroupFactory , false /* isBookkeeperManaged */ ,
192- config , NullStatsLogger .INSTANCE , OpenTelemetry .noop ());
204+ config , NullStatsLogger .INSTANCE , OpenTelemetry .noop (), new ManagedLedgerConfig () );
193205 }
194206
195207 public ManagedLedgerFactoryImpl (MetadataStoreExtended metadataStore ,
@@ -198,15 +210,17 @@ public ManagedLedgerFactoryImpl(MetadataStoreExtended metadataStore,
198210 OpenTelemetry openTelemetry )
199211 throws Exception {
200212 this (metadataStore , bookKeeperGroupFactory , false /* isBookkeeperManaged */ ,
201- config , statsLogger , openTelemetry );
213+ config , statsLogger , openTelemetry , new ManagedLedgerConfig () );
202214 }
203215
204216 private ManagedLedgerFactoryImpl (MetadataStoreExtended metadataStore ,
205217 BookkeeperFactoryForCustomEnsemblePlacementPolicy bookKeeperGroupFactory ,
206218 boolean isBookkeeperManaged ,
207219 ManagedLedgerFactoryConfig config ,
208220 StatsLogger statsLogger ,
209- OpenTelemetry openTelemetry ) throws Exception {
221+ OpenTelemetry openTelemetry ,
222+ ManagedLedgerConfig defaultManagedLedgerConfig ) throws Exception {
223+ this .defaultManagedLedgerConfig = defaultManagedLedgerConfig ;
210224 MetadataCompressionConfig compressionConfigForManagedLedgerInfo =
211225 config .getCompressionConfigForManagedLedgerInfo ();
212226 MetadataCompressionConfig compressionConfigForManagedCursorInfo =
@@ -303,17 +317,60 @@ private synchronized void refreshStats() {
303317 lastStatTimestamp = now ;
304318 }
305319
306- private synchronized void doCacheEviction () {
320+ @ VisibleForTesting
321+ public synchronized void doCacheEviction () {
307322 long maxTimestamp = System .nanoTime () - cacheEvictionTimeThresholdNanos ;
323+ entryCacheManager .doCacheEviction (maxTimestamp );
324+ }
308325
309- ledgers .values ().forEach (mlfuture -> {
310- if (mlfuture .isDone () && !mlfuture .isCompletedExceptionally ()) {
311- ManagedLedgerImpl ml = mlfuture .getNow (null );
312- if (ml != null ) {
313- ml .doCacheEviction (maxTimestamp );
314- }
326+ /**
327+ * Waits for all pending cache evictions based on total cache size or entry TTL to complete.
328+ * This is for testing purposes only, so that we can ensure all cache evictions are done before proceeding with
329+ * further operations. Please notice that Managed Ledger level cache eviction is not handled here. There's
330+ * a similar method {@link ManagedLedgerImpl#waitForPendingCacheEvictions()} that waits for pending cache evictions
331+ * at the Managed Ledger level.
332+ */
333+ @ VisibleForTesting
334+ public void waitForPendingCacheEvictions () {
335+ try {
336+ cacheEvictionExecutor .submit (() -> {
337+ // no-op
338+ }).get ();
339+ } catch (InterruptedException e ) {
340+ Thread .currentThread ().interrupt ();
341+ } catch (ExecutionException e ) {
342+ throw new RuntimeException (e );
343+ }
344+ }
345+
346+ /**
347+ * Blocks the pending cache evictions until the returned runnable is called.
348+ * This is for testing purposes only, so that asynchronous cache evictions can be blocked for consistent
349+ * test results.
350+ *
351+ * @return
352+ * @throws InterruptedException
353+ */
354+ @ VisibleForTesting
355+ public Runnable blockPendingCacheEvictions () throws InterruptedException {
356+ CountDownLatch blockedLatch = new CountDownLatch (1 );
357+ CountDownLatch releaseLatch = new CountDownLatch (1 );
358+ cacheEvictionExecutor .execute (() -> {
359+ blockedLatch .countDown ();
360+ try {
361+ releaseLatch .await ();
362+ } catch (InterruptedException e ) {
363+ Thread .currentThread ().interrupt ();
315364 }
316365 });
366+ blockedLatch .await ();
367+ return () -> {
368+ if (releaseLatch .getCount () == 0 ) {
369+ throw new IllegalStateException ("Releasing should only be called once" );
370+ }
371+ releaseLatch .countDown ();
372+ waitForPendingCacheEvictions ();
373+ };
317374 }
318375
319376 /**
@@ -329,7 +386,7 @@ public Map<String, ManagedLedger> getManagedLedgers() {
329386
330387 @ Override
331388 public ManagedLedger open (String name ) throws InterruptedException , ManagedLedgerException {
332- return open (name , new ManagedLedgerConfig () );
389+ return open (name , defaultManagedLedgerConfig );
333390 }
334391
335392 @ Override
@@ -365,7 +422,7 @@ public void openLedgerFailed(ManagedLedgerException exception, Object ctx) {
365422
366423 @ Override
367424 public void asyncOpen (String name , OpenLedgerCallback callback , Object ctx ) {
368- asyncOpen (name , new ManagedLedgerConfig () , callback , null , ctx );
425+ asyncOpen (name , defaultManagedLedgerConfig , callback , null , ctx );
369426 }
370427
371428 @ Override
0 commit comments