public void Cleanup() { if (_recycleArea.Count == 0 && _scratchBuffers.Count == 1) { return; } long txIdAllowingToReleaseOldScratches = -1; // ReSharper disable once LoopCanBeConvertedToQuery foreach (var scratchBufferItem in _scratchBuffers) { if (scratchBufferItem.Value == _current) { continue; } txIdAllowingToReleaseOldScratches = Math.Max(txIdAllowingToReleaseOldScratches, scratchBufferItem.Value.File.TxIdAfterWhichLatestFreePagesBecomeAvailable); } ByteStringContext byteStringContext; try { byteStringContext = new ByteStringContext(SharedMultipleUseFlag.None); } catch (Exception e) when(e is OutOfMemoryException || e is EarlyOutOfMemoryException) { return; } try { while (_env.CurrentReadTransactionId <= txIdAllowingToReleaseOldScratches) { // we've just flushed and had no more writes after that, let us bump id of next read transactions to ensure // that nobody will attempt to read old scratches so we will be able to release more files try { using (var tx = _env.NewLowLevelTransaction(new TransactionPersistentContext(), TransactionFlags.ReadWrite, timeout: TimeSpan.FromMilliseconds(500), context: byteStringContext)) { tx.ModifyPage(0); tx.Commit(); } } catch (TimeoutException) { break; } catch (DiskFullException) { break; } } IDisposable exitPreventNewTransactions = null; try { // we need to ensure that no access to _recycleArea and _scratchBuffers will take place in the same time // and only methods that access this are used within write transaction using (_env.WriteTransaction()) { // additionally we must not allow to start any transaction (even read one) to start because it uses GetPagerStatesOfAllScratches() which // returns _pagerStatesAllScratchesCache that we're updating here if (_env.TryPreventNewTransactions(TimeSpan.Zero, out exitPreventNewTransactions)) { var removedInactive = RemoveInactiveScratches(_current, updateCacheBeforeDisposingScratch: false); // no need to update cache because we're going do to it here anyway var removedInactiveRecycled = RemoveInactiveRecycledScratches(); if (_logger.IsInfoEnabled) { _logger.Info( $"Cleanup of {nameof(ScratchBufferPool)} removed: {removedInactive} inactive scratches and {removedInactiveRecycled} inactive from the recycle area"); } _forTestingPurposes?.ActionToCallDuringCleanupRightAfterRemovingInactiveScratches?.Invoke(); UpdateCacheForPagerStatesOfAllScratches(); // it's going to be called by Rollback() of the write tx but let's call it explicitly so we can easily find this usage } } } catch (TimeoutException) { } catch (DiskFullException) { } finally { exitPreventNewTransactions?.Dispose(); } } finally { byteStringContext.Dispose(); } }