예제 #1
0
        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);
        }