public void Free(LowLevelTransaction tx, int scratchNumber, long page, long?txId) { var scratch = _scratchBuffers[scratchNumber]; scratch.File.Free(page, txId); if (scratch.File.AllocatedPagesCount != 0) { return; } List <ScratchBufferFile> recycledScratchesToDispose = null; while (_recycleArea.First != null) { var recycledScratch = _recycleArea.First.Value; if (IsLowMemory() == false && DateTime.UtcNow - recycledScratch.RecycledAt <= RecycledScratchFileTimeout) { break; } _recycleArea.RemoveFirst(); if (recycledScratch.File.HasActivelyUsedBytes(_env.PossibleOldestReadTransaction(tx))) { // even though this was in the recycle area, there might still be some transactions looking at it // so we cannot dispose it right now, the disposal will happen in RemoveInactiveScratches // when we are sure it's really no longer in use continue; } _scratchBuffers.TryRemove(recycledScratch.Number, out var _); if (recycledScratchesToDispose == null) { recycledScratchesToDispose = new List <ScratchBufferFile>(); } recycledScratchesToDispose.Add(recycledScratch.File); } if (recycledScratchesToDispose != null) { using (_env.IsInPreventNewTransactionsMode == false ? _env.PreventNewTransactions() : (IDisposable)null) { // we're about to dispose recycled scratch pagers, we need to update the cache so next transactions won't attempt to EnsurePagerStateReference() on them UpdateCacheForPagerStatesOfAllScratches(); } foreach (var recycledScratch in recycledScratchesToDispose) { recycledScratch.Dispose(); _scratchSpaceMonitor.Decrease(recycledScratch.NumberOfAllocatedPages * Constants.Storage.PageSize); } _forTestingPurposes?.ActionToCallDuringRemovalsOfRecycledScratchesRightAfterDisposingScratches?.Invoke(); } if (scratch == _current) { if (scratch.File.Size <= _options.MaxScratchBufferSize) { // we'll take the chance that no one is using us to reset the memory allocations // and avoid fragmentation, we can only do that if no transaction is looking at us if (scratch.File.HasActivelyUsedBytes(_env.PossibleOldestReadTransaction(tx)) == false) { scratch.File.Reset(); } return; } // this is the current one, but the size is too big, let us trim it var newCurrent = NextFile(_options.InitialLogFileSize, _options.MaxScratchBufferSize); newCurrent.File.PagerState.AddRef(); _current = newCurrent; } TryRecycleScratchFile(scratch); }