Example #1
0
        private ScratchBufferItem NextFile(long minSize, long?requestedSize)
        {
            var current  = _recycleArea.Last;
            var oldestTx = _env.PossibleOldestReadTransaction(null);

            while (current != null)
            {
                var recycled = current.Value;

                if (recycled.File.Size >= Math.Max(minSize, requestedSize ?? 0) &&
                    // even though this is in the recyle bin, there might still be some transactions looking at it
                    // so we have to make sure that this is really unused before actually reusing it
                    recycled.File.HasActivelyUsedBytes(oldestTx) == false)
                {
                    recycled.File.Reset();
                    recycled.RecycledAt = default(DateTime);
                    _recycleArea.Remove(current);
                    AddScratchBufferFile(recycled);

                    _scratchSpaceMonitor.Increase(recycled.File.NumberOfAllocatedPages * Constants.Storage.PageSize);

                    return(recycled);
                }

                current = current.Previous;
            }

            _currentScratchNumber++;
            AbstractPager scratchPager;

            if (requestedSize != null)
            {
                try
                {
                    scratchPager =
                        _options.CreateScratchPager(StorageEnvironmentOptions.ScratchBufferName(_currentScratchNumber),
                                                    requestedSize.Value);
                }
                catch (Exception)
                {
                    // this can fail because of disk space issue, let us just ignore it
                    // we'll allocate the minimum amount in a bit anyway
                    return(NextFile(minSize, null));
                }
            }
            else
            {
                scratchPager = _options.CreateScratchPager(StorageEnvironmentOptions.ScratchBufferName(_currentScratchNumber),
                                                           Math.Max(_options.InitialLogFileSize, minSize));
            }

            var scratchFile = new ScratchBufferFile(scratchPager, _currentScratchNumber);
            var item        = new ScratchBufferItem(scratchFile.Number, scratchFile);

            AddScratchBufferFile(item);

            _scratchSpaceMonitor.Increase(item.File.NumberOfAllocatedPages * Constants.Storage.PageSize);

            return(item);
        }
Example #2
0
        public virtual int CopyPage(I4KbBatchWrites destI4KbBatchWrites, int scratchNumber, long p, PagerState pagerState)
        {
            var item = GetScratchBufferFile(scratchNumber);

            ScratchBufferFile bufferFile = item.File;

            return(bufferFile.CopyPage(destI4KbBatchWrites, p, pagerState));
        }
Example #3
0
        public Page ReadPage(LowLevelTransaction tx, int scratchNumber, long p, PagerState pagerState = null)
        {
            var item = GetScratchBufferFile(scratchNumber);

            ScratchBufferFile bufferFile = item.File;

            return(bufferFile.ReadPage(tx, p, pagerState));
        }
Example #4
0
        public byte *AcquirePagePointerForNewPage(LowLevelTransaction tx, int scratchNumber, long p, int numberOfPages)
        {
            var item = GetScratchBufferFile(scratchNumber);

            ScratchBufferFile bufferFile = item.File;

            return(bufferFile.AcquirePagePointerForNewPage(tx, p, numberOfPages));
        }
Example #5
0
        public byte *AcquirePagePointerWithOverflowHandling(LowLevelTransaction tx, int scratchNumber, long p)
        {
            var item = GetScratchBufferFile(scratchNumber);

            ScratchBufferFile bufferFile = item.File;

            return(bufferFile.AcquirePagePointerWithOverflowHandling(tx, p));
        }
Example #6
0
        public void EnsureMapped(LowLevelTransaction tx, int scratchNumber, long positionInScratchBuffer, int numberOfPages)
        {
            var item = GetScratchBufferFile(scratchNumber);

            ScratchBufferFile bufferFile = item.File;

            bufferFile.EnsureMapped(tx, positionInScratchBuffer, numberOfPages);
        }
        public byte *AcquirePagePointer(LowLevelTransaction tx, int scratchNumber, long p)
        {
            var item = _scratchBuffers[scratchNumber];

            ScratchBufferFile bufferFile = item.File;

            return(bufferFile.AcquirePagePointer(tx, p));
        }
Example #8
0
        public byte *AcquirePagePointer(Transaction tx, int scratchNumber, long p)
        {
            ScratchBufferItem item = lastScratchBuffer;

            if (item.Number != scratchNumber)
            {
                item = _scratchBuffers[scratchNumber];
            }

            ScratchBufferFile bufferFile = item.File;

            return(bufferFile.AcquirePagePointer(tx, p));
        }
Example #9
0
        public Page ReadPage(Transaction tx, int scratchNumber, long p, PagerState pagerState = null)
        {
            ScratchBufferItem item = lastScratchBuffer;

            if (item.Number != scratchNumber)
            {
                item = _scratchBuffers[scratchNumber];
            }

            ScratchBufferFile bufferFile = item.File;

            return(bufferFile.ReadPage(tx, p, pagerState));
        }
		private ScratchBufferFile NextFile()
		{
			_currentScratchNumber++;
			var scratchPager = _options.CreateScratchPager(StorageEnvironmentOptions.ScratchBufferName(_currentScratchNumber));
			scratchPager.AllocateMorePages(null, _options.InitialFileSize.HasValue ? Math.Max(_options.InitialFileSize.Value, _options.InitialLogFileSize) : _options.InitialLogFileSize);

			var scratchFile = new ScratchBufferFile(scratchPager, _currentScratchNumber);
			_scratchBuffers.Add(_currentScratchNumber, scratchFile);

			_oldestTransactionWhenFlushWasForced = -1;

			return scratchFile;
		}
        private ScratchBufferItem NextFile()
        {
            _currentScratchNumber++;
            var scratchPager = _options.CreateScratchPager(StorageEnvironmentOptions.ScratchBufferName(_currentScratchNumber));

            scratchPager.EnsureContinuous(0, (int)(Math.Max(_options.InitialFileSize ?? 0, _options.InitialLogFileSize) / _options.PageSize));

            var scratchFile = new ScratchBufferFile(scratchPager, _currentScratchNumber);
            var item        = new ScratchBufferItem(scratchFile.Number, scratchFile);

            _scratchBuffers.TryAdd(item.Number, item);

            return(item);
        }
Example #12
0
        private ScratchBufferItem NextFile()
        {
            _currentScratchNumber++;

            var scratchPager = _options.CreateScratchPager(StorageEnvironmentOptions.ScratchBufferName(_currentScratchNumber));

            scratchPager.AllocateMorePages(null, Math.Max(_options.InitialFileSize ?? 0, _options.InitialLogFileSize));

            var scratchFile = new ScratchBufferFile(scratchPager, _currentScratchNumber);
            var item        = new ScratchBufferItem(scratchFile.Number, scratchFile);

            _scratchBuffers.TryAdd(item.Number, item);

            return(item);
        }
Example #13
0
        private ScratchBufferFile NextFile()
        {
            _currentScratchNumber++;
            var scratchPager = _options.CreateScratchPager(StorageEnvironmentOptions.ScratchBufferName(_currentScratchNumber));

            scratchPager.AllocateMorePages(null, Math.Max(_options.InitialFileSize ?? 0, _options.InitialLogFileSize));

            var scratchFile = new ScratchBufferFile(scratchPager, _currentScratchNumber);

            _scratchBuffers.Add(_currentScratchNumber, scratchFile);

            _oldestTransactionWhenFlushWasForced = -1;

            return(scratchFile);
        }
Example #14
0
        private ScratchBufferItem NextFile(long minSize, long?requestedSize, LowLevelTransaction tx)
        {
            if (_recycleArea.Count > 0)
            {
                var recycled = _recycleArea.Last.Value.Item2;
                _recycleArea.RemoveLast();

                if (recycled.File.Size <= Math.Max(minSize, requestedSize ?? 0))
                {
                    recycled.File.Reset(tx);
                    _scratchBuffers.TryAdd(recycled.Number, recycled);
                    return(recycled);
                }
            }

            _currentScratchNumber++;
            AbstractPager scratchPager;

            if (requestedSize != null)
            {
                try
                {
                    scratchPager =
                        _options.CreateScratchPager(StorageEnvironmentOptions.ScratchBufferName(_currentScratchNumber),
                                                    requestedSize.Value);
                }
                catch (Exception)
                {
                    // this can fail because of disk space issue, let us just ignore it
                    // we'll allocate the minimum amount in a bit anway
                    return(NextFile(minSize, null, tx));
                }
            }
            else
            {
                scratchPager = _options.CreateScratchPager(StorageEnvironmentOptions.ScratchBufferName(_currentScratchNumber),
                                                           Math.Max(_options.InitialLogFileSize, minSize));
            }

            var scratchFile = new ScratchBufferFile(scratchPager, _currentScratchNumber);
            var item        = new ScratchBufferItem(scratchFile.Number, scratchFile);

            _scratchBuffers.TryAdd(item.Number, item);

            return(item);
        }
		public PageFromScratchBuffer Allocate(Transaction tx, int numberOfPages)
		{
			if (tx == null)
				throw new ArgumentNullException("tx");
			var size = Utils.NearestPowerOfTwo(numberOfPages);

			PageFromScratchBuffer result;
			if (_current.TryGettingFromAllocatedBuffer(tx, numberOfPages, size, out result))
				return result;

			long sizeAfterAllocation = _current.SizeAfterAllocation(size);
			long oldestActiveTransaction = tx.Environment.OldestTransaction;

			if (_scratchBuffers.Count > 1)
			{
				var scratchesToDelete = new List<int>();

				// determine how many bytes of older scratches are still in use
				foreach (var olderScratch in _scratchBuffers.Values.Except(new []{_current}))
				{
					var bytesInUse = olderScratch.ActivelyUsedBytes(oldestActiveTransaction);

					if (bytesInUse > 0)
						sizeAfterAllocation += bytesInUse;
					else
						scratchesToDelete.Add(olderScratch.Number);
				}

				// delete inactive scratches
				foreach (var scratchNumber in scratchesToDelete)
				{
					var scratchBufferFile = _scratchBuffers[scratchNumber];
					_scratchBuffers.Remove(scratchNumber);
					scratchBufferFile.Dispose();
				}
			}

			if (sizeAfterAllocation > 0.8 * _sizeLimit && oldestActiveTransaction > _oldestTransactionWhenFlushWasForced)
			{
				_oldestTransactionWhenFlushWasForced = oldestActiveTransaction;

				// We are starting to force a flush to free scratch pages. We are doing it at this point (80% of the max scratch size)
				// to make sure that next transactions will be able to allocate pages that we are going to free in the current transaction.
				// Important notice: all pages freed by this run will get ValidAfterTransactionId == tx.Id (so only next ones can use it)

				tx.Environment.ForceLogFlushToDataFile(tx, allowToFlushOverwrittenPages: true);
			}

			if (sizeAfterAllocation > _sizeLimit)
			{
				var sp = Stopwatch.StartNew();

				// Our problem is that we don't have any available free pages, probably because
				// there are read transactions that are holding things open. We are going to see if
				// there are any free pages that _might_ be freed for us if we wait for a bit. The idea
				// is that we let the read transactions time to complete and do their work, at which point
				// we can continue running. It is possible that a long running read transaction
				// would in fact generate enough work for us to timeout, but hopefully we can avoid that.

				while (sp.ElapsedMilliseconds < tx.Environment.Options.ScratchBufferOverflowTimeout)
				{
					if (_current.TryGettingFromAllocatedBuffer(tx, numberOfPages, size, out result))
						return result;
					Thread.Sleep(32);
				}

				sp.Stop();

				if (_current.HasDiscontinuousSpaceFor(tx, size))
				{
					// there is enough space for the requested allocation but the problem is its fragmentation
					// so we will create a new scratch file and will allow to allocate new continuous range from there

					_current = NextFile();

					_current.PagerState.AddRef();
					tx.AddPagerState(_current.PagerState);

					return _current.Allocate(tx, numberOfPages, size);
				}

				var debugInfoBuilder = new StringBuilder();

				debugInfoBuilder.AppendFormat("Requested number of pages: {0} (NearestPowerOfTwo: {1})\r\n", numberOfPages, size);
				debugInfoBuilder.AppendFormat("Oldest active transaction: {0} (snapshot: {1})\r\n", tx.Environment.OldestTransaction, oldestActiveTransaction);
				debugInfoBuilder.AppendFormat("Oldest active transaction when flush was forced: {0}\r\n", _oldestTransactionWhenFlushWasForced);
				debugInfoBuilder.AppendFormat("Next write transaction id: {0}\r\n", tx.Environment.NextWriteTransactionId);

				debugInfoBuilder.AppendLine("Active transactions:");
				foreach (var activeTransaction in tx.Environment.ActiveTransactions)
				{
					debugInfoBuilder.AppendFormat("\tId: {0} - {1}\r\n", activeTransaction.Id, activeTransaction.Flags);
				}

				debugInfoBuilder.AppendLine("Scratch files usage:");
				foreach (var scratchBufferFile in _scratchBuffers)
				{
					debugInfoBuilder.AppendFormat("\t{0} - size: {1:#,#;;0} KB, in active use: {2:#,#;;0} KB\r\n", StorageEnvironmentOptions.ScratchBufferName(scratchBufferFile.Value.Number), scratchBufferFile.Value.Size / 1024, scratchBufferFile.Value.ActivelyUsedBytes(oldestActiveTransaction) / 1024);
				}

				debugInfoBuilder.AppendLine("Most available free pages:");
				foreach (var scratchBufferFile in _scratchBuffers)
				{
					debugInfoBuilder.AppendFormat("\t{0}\r\n", StorageEnvironmentOptions.ScratchBufferName(scratchBufferFile.Value.Number));

					foreach (var freePage in scratchBufferFile.Value.GetMostAvailableFreePagesBySize())
					{
						debugInfoBuilder.AppendFormat("\t\tSize:{0}, ValidAfterTransactionId: {1}\r\n", freePage.Key, freePage.Value);
					}
				}

				string debugInfo = debugInfoBuilder.ToString();

				string message = string.Format("Cannot allocate more space for the scratch buffer.\r\n" +
											   "Current size is:\t{0:#,#;;0} KB.\r\n" +
											   "Limit:\t\t\t{1:#,#;;0} KB.\r\n" +
											   "Requested Size:\t{2:#,#;;0} KB.\r\n" +
											   "Already flushed and waited for {3:#,#;;0} ms for read transactions to complete.\r\n" +
											   "Do you have a long running read transaction executing?\r\n" + 
											   "Debug info:\r\n{4}",
					_current.Size / 1024,
					_sizeLimit / 1024,
					(_current.Size + (size * AbstractPager.PageSize)) / 1024,
					sp.ElapsedMilliseconds,
					debugInfo);
				throw new ScratchBufferSizeLimitException(message);
			}

			// we don't have free pages to give out, need to allocate some
			result = _current.Allocate(tx, numberOfPages, size);

			return result;
		}
		public ScratchBufferPool(StorageEnvironment env)
		{
			_options = env.Options;
			_sizeLimit = env.Options.MaxScratchBufferSize;
			_current = NextFile();
		}
Example #17
0
 public ScratchBufferItem(int number, ScratchBufferFile file)
 {
     Number = number;
     File   = file;
     OldestTransactionWhenFlushWasForced = -1;
 }
 public ScratchFileDebugInfo(ScratchBufferFile parent)
 {
     _parent = parent;
 }
Example #19
0
        public PageFromScratchBuffer Allocate(Transaction tx, int numberOfPages)
        {
            if (tx == null)
                throw new ArgumentNullException("tx");
            var size = Utils.NearestPowerOfTwo(numberOfPages);

            PageFromScratchBuffer result;
            if (_current.TryGettingFromAllocatedBuffer(tx, numberOfPages, size, out result))
                return result;

            long sizeAfterAllocation;
            long oldestActiveTransaction = tx.Environment.OldestTransaction;

            if (_scratchBuffers.Count == 1)
            {
                sizeAfterAllocation = _current.SizeAfterAllocation(size);
            }
            else
            {
                sizeAfterAllocation = size * AbstractPager.PageSize;

                var scratchesToDelete = new List<int>();

                // determine how many bytes of older scratches are still in use
                foreach (var scratch in _scratchBuffers.Values)
                {
                    var bytesInUse = scratch.ActivelyUsedBytes(oldestActiveTransaction);

                    if (bytesInUse > 0)
                        sizeAfterAllocation += bytesInUse;
                    else
                    {
                        if(scratch != _current)
                            scratchesToDelete.Add(scratch.Number);
                    }
                }

                // delete inactive scratches
                foreach (var scratchNumber in scratchesToDelete)
                {
                    var scratchBufferFile = _scratchBuffers[scratchNumber];
                    _scratchBuffers.Remove(scratchNumber);
                    scratchBufferFile.Dispose();
                }
            }

            if (sizeAfterAllocation >= (_sizeLimit*3)/4 && oldestActiveTransaction > _oldestTransactionWhenFlushWasForced)
            {
                // we may get recursive flushing, so we want to avoid it
                if (tx.Environment.Journal.Applicator.IsCurrentThreadInFlushOperation == false)
                {
                    // We are starting to force a flush to free scratch pages. We are doing it at this point (80% of the max scratch size)
                    // to make sure that next transactions will be able to allocate pages that we are going to free in the current transaction.
                    // Important notice: all pages freed by this run will get ValidAfterTransactionId == tx.Id (so only next ones can use it)

                    bool flushLockTaken = false;
                    using (tx.Environment.Journal.Applicator.TryTakeFlushingLock(ref flushLockTaken))
                    {
                        if (flushLockTaken) // if we are already flushing, we don't need to force a flush
                        {
                            try
                            {
                                tx.Environment.ForceLogFlushToDataFile(tx, allowToFlushOverwrittenPages: true);
                                _oldestTransactionWhenFlushWasForced = oldestActiveTransaction;
                            }
                            catch (TimeoutException)
                            {
                                // we'll try next time
                            }
                            catch (InvalidJournalFlushRequest)
                            {
                                // journals flushing already in progress
                            }
                        }
                    }
                }
            }

            if (sizeAfterAllocation > _sizeLimit)
            {
                var sp = Stopwatch.StartNew();

                // Our problem is that we don't have any available free pages, probably because
                // there are read transactions that are holding things open. We are going to see if
                // there are any free pages that _might_ be freed for us if we wait for a bit. The idea
                // is that we let the read transactions time to complete and do their work, at which point
                // we can continue running. It is possible that a long running read transaction
                // would in fact generate enough work for us to timeout, but hopefully we can avoid that.

                while (sp.ElapsedMilliseconds < tx.Environment.Options.ScratchBufferOverflowTimeout)
                {
                    if (_current.TryGettingFromAllocatedBuffer(tx, numberOfPages, size, out result))
                        return result;
                    Thread.Sleep(32);
                }

                sp.Stop();

                bool createNextFile = false;

                if (_current.HasDiscontinuousSpaceFor(tx, size, _scratchBuffers.Count))
                {
                    // there is enough space for the requested allocation but the problem is its fragmentation
                    // so we will create a new scratch file and will allow to allocate new continuous range from there

                    createNextFile = true;
                }
                else if (_scratchBuffers.Count == 1 && _current.Size < _sizeLimit &&
                        (_current.ActivelyUsedBytes(oldestActiveTransaction) + size * AbstractPager.PageSize) < _sizeLimit)
                {
                    // there is only one scratch file that hasn't reach the size limit yet and
                    // the number of bytes being in active use allows to allocate the requested size
                    // let it create a new file

                    createNextFile = true;
                }

                if (createNextFile)
                {
                    _current = NextFile();

                    tx.EnsurePagerStateReference(_current.PagerState);

                    return _current.Allocate(tx, numberOfPages, size);
                }

                var debugInfoBuilder = new StringBuilder();

                debugInfoBuilder.AppendFormat("Current transaction id: {0}\r\n", tx.Id);
                debugInfoBuilder.AppendFormat("Requested number of pages: {0} (adjusted size: {1} == {2:#,#;;0} KB)\r\n", numberOfPages, size, size * AbstractPager.PageSize / 1024);
                debugInfoBuilder.AppendFormat("Oldest active transaction: {0} (snapshot: {1})\r\n", tx.Environment.OldestTransaction, oldestActiveTransaction);
                debugInfoBuilder.AppendFormat("Oldest active transaction when flush was forced: {0}\r\n", _oldestTransactionWhenFlushWasForced);
                debugInfoBuilder.AppendFormat("Next write transaction id: {0}\r\n", tx.Environment.NextWriteTransactionId + 1);

                debugInfoBuilder.AppendLine("Active transactions:");
                foreach (var activeTransaction in tx.Environment.ActiveTransactions)
                {
                    debugInfoBuilder.AppendFormat("\tId: {0} - {1}\r\n", activeTransaction.Id, activeTransaction.Flags);
                }

                debugInfoBuilder.AppendLine("Scratch files usage:");
                foreach (var scratchBufferFile in _scratchBuffers.OrderBy(x => x.Key))
                {
                    debugInfoBuilder.AppendFormat("\t{0} - size: {1:#,#;;0} KB, in active use: {2:#,#;;0} KB\r\n", StorageEnvironmentOptions.ScratchBufferName(scratchBufferFile.Value.Number), scratchBufferFile.Value.Size / 1024, scratchBufferFile.Value.ActivelyUsedBytes(oldestActiveTransaction) / 1024);
                }

                debugInfoBuilder.AppendLine("Most available free pages:");
                foreach (var scratchBufferFile in _scratchBuffers.OrderBy(x => x.Key))
                {
                    debugInfoBuilder.AppendFormat("\t{0}\r\n", StorageEnvironmentOptions.ScratchBufferName(scratchBufferFile.Value.Number));

                    foreach (var freePage in scratchBufferFile.Value.GetMostAvailableFreePagesBySize())
                    {
                        debugInfoBuilder.AppendFormat("\t\tSize:{0}, ValidAfterTransactionId: {1}\r\n", freePage.Key, freePage.Value);
                    }
                }

                debugInfoBuilder.AppendFormat("Compression buffer size: {0:#,#;;0} KB\r\n", tx.Environment.Journal.CompressionBufferSize / 1024);

                string debugInfo = debugInfoBuilder.ToString();

                string message = string.Format("Cannot allocate more space for the scratch buffer.\r\n" +
                                               "Current file size is:\t{0:#,#;;0} KB.\r\n" +
                                               "Requested size for current file:\t{1:#,#;;0} KB.\r\n" +
                                               "Requested total size for all files:\t{2:#,#;;0} KB.\r\n" +
                                               "Limit:\t\t\t{3:#,#;;0} KB.\r\n" +
                                               "Already flushed and waited for {4:#,#;;0} ms for read transactions to complete.\r\n" +
                                               "Do you have a long running read transaction executing?\r\n" +
                                               "Debug info:\r\n{5}",
                    _current.Size / 1024L,
                    _current.SizeAfterAllocation(size) / 1024L,
                    sizeAfterAllocation / 1024L,
                    _sizeLimit / 1024L,
                    sp.ElapsedMilliseconds,
                    debugInfo
                    );

                throw new ScratchBufferSizeLimitException(message);
            }

            // we don't have free pages to give out, need to allocate some
            result = _current.Allocate(tx, numberOfPages, size);
            _options.OnScratchBufferSizeChanged(sizeAfterAllocation);

            return result;
        }
Example #20
0
        public PageFromScratchBuffer Allocate(Transaction tx, int numberOfPages)
        {
            if (tx == null)
            {
                throw new ArgumentNullException("tx");
            }
            var size = Utils.NearestPowerOfTwo(numberOfPages);

            PageFromScratchBuffer result;

            if (_current.TryGettingFromAllocatedBuffer(tx, numberOfPages, size, out result))
            {
                return(result);
            }

            long sizeAfterAllocation     = _current.SizeAfterAllocation(size);
            long oldestActiveTransaction = tx.Environment.OldestTransaction;

            if (_scratchBuffers.Count > 1)
            {
                var scratchesToDelete = new List <int>();

                // determine how many bytes of older scratches are still in use
                foreach (var olderScratch in _scratchBuffers.Values.Except(new [] { _current }))
                {
                    var bytesInUse = olderScratch.ActivelyUsedBytes(oldestActiveTransaction);

                    if (bytesInUse > 0)
                    {
                        sizeAfterAllocation += bytesInUse;
                    }
                    else
                    {
                        scratchesToDelete.Add(olderScratch.Number);
                    }
                }

                // delete inactive scratches
                foreach (var scratchNumber in scratchesToDelete)
                {
                    var scratchBufferFile = _scratchBuffers[scratchNumber];
                    _scratchBuffers.Remove(scratchNumber);
                    scratchBufferFile.Dispose();
                }
            }

            if (sizeAfterAllocation > 0.8 * _sizeLimit && oldestActiveTransaction > _oldestTransactionWhenFlushWasForced)
            {
                _oldestTransactionWhenFlushWasForced = oldestActiveTransaction;

                // We are starting to force a flush to free scratch pages. We are doing it at this point (80% of the max scratch size)
                // to make sure that next transactions will be able to allocate pages that we are going to free in the current transaction.
                // Important notice: all pages freed by this run will get ValidAfterTransactionId == tx.Id (so only next ones can use it)

                tx.Environment.ForceLogFlushToDataFile(tx, allowToFlushOverwrittenPages: true);
            }

            if (sizeAfterAllocation > _sizeLimit)
            {
                var sp = Stopwatch.StartNew();

                // Our problem is that we don't have any available free pages, probably because
                // there are read transactions that are holding things open. We are going to see if
                // there are any free pages that _might_ be freed for us if we wait for a bit. The idea
                // is that we let the read transactions time to complete and do their work, at which point
                // we can continue running. It is possible that a long running read transaction
                // would in fact generate enough work for us to timeout, but hopefully we can avoid that.

                while (sp.ElapsedMilliseconds < tx.Environment.Options.ScratchBufferOverflowTimeout)
                {
                    if (_current.TryGettingFromAllocatedBuffer(tx, numberOfPages, size, out result))
                    {
                        return(result);
                    }
                    Thread.Sleep(32);
                }

                sp.Stop();

                if (_current.HasDiscontinuousSpaceFor(tx, size))
                {
                    // there is enough space for the requested allocation but the problem is its fragmentation
                    // so we will create a new scratch file and will allow to allocate new continuous range from there

                    _current = NextFile();

                    _current.PagerState.AddRef();
                    tx.AddPagerState(_current.PagerState);

                    return(_current.Allocate(tx, numberOfPages, size));
                }

                string message = string.Format("Cannot allocate more space for the scratch buffer.\r\n" +
                                               "Current size is:\t{0:#,#;;0} kb.\r\n" +
                                               "Limit:\t\t\t{1:#,#;;0} kb.\r\n" +
                                               "Requested Size:\t{2:#,#;;0} kb.\r\n" +
                                               "Already flushed and waited for {3:#,#;;0} ms for read transactions to complete.\r\n" +
                                               "Do you have a long running read transaction executing?",
                                               _current.Size / 1024,
                                               _sizeLimit / 1024,
                                               (_current.Size + (size * AbstractPager.PageSize)) / 1024,
                                               sp.ElapsedMilliseconds);
                throw new ScratchBufferSizeLimitException(message);
            }

            // we don't have free pages to give out, need to allocate some
            result = _current.Allocate(tx, numberOfPages, size);

            return(result);
        }
Example #21
0
 public ScratchBufferCacheItem(int number, ScratchBufferFile file)
 {
     this.Number = number;
     this.File   = file;
 }
Example #22
0
 public ScratchBufferPool(StorageEnvironment env)
 {
     _options   = env.Options;
     _sizeLimit = env.Options.MaxScratchBufferSize;
     _current   = NextFile();
 }
Example #23
0
 public ScratchBufferItem(int number, ScratchBufferFile file)
 {
     Number = number;
     File   = file;
 }
 public ScratchBufferCacheItem(int number, ScratchBufferFile file)
 {
     this.Number = number;
     this.File = file;
 }
Example #25
0
        public PageFromScratchBuffer Allocate(Transaction tx, int numberOfPages)
        {
            if (tx == null)
            {
                throw new ArgumentNullException("tx");
            }
            var size = Utils.NearestPowerOfTwo(numberOfPages);

            PageFromScratchBuffer result;

            if (_current.TryGettingFromAllocatedBuffer(tx, numberOfPages, size, out result))
            {
                return(result);
            }

            long sizeAfterAllocation;
            long oldestActiveTransaction = tx.Environment.OldestTransaction;

            if (_scratchBuffers.Count == 1)
            {
                sizeAfterAllocation = _current.SizeAfterAllocation(size);
            }
            else
            {
                sizeAfterAllocation = size * AbstractPager.PageSize;

                var scratchesToDelete = new List <int>();

                // determine how many bytes of older scratches are still in use
                foreach (var scratch in _scratchBuffers.Values)
                {
                    var bytesInUse = scratch.ActivelyUsedBytes(oldestActiveTransaction);

                    if (bytesInUse > 0)
                    {
                        sizeAfterAllocation += bytesInUse;
                    }
                    else
                    {
                        if (scratch != _current)
                        {
                            scratchesToDelete.Add(scratch.Number);
                        }
                    }
                }

                // delete inactive scratches
                foreach (var scratchNumber in scratchesToDelete)
                {
                    var scratchBufferFile = _scratchBuffers[scratchNumber];
                    _scratchBuffers.Remove(scratchNumber);
                    scratchBufferFile.Dispose();
                }
            }

            if (sizeAfterAllocation >= (_sizeLimit * 3) / 4 && oldestActiveTransaction > _oldestTransactionWhenFlushWasForced)
            {
                // we may get recursive flushing, so we want to avoid it
                if (tx.Environment.Journal.Applicator.IsCurrentThreadInFlushOperation == false)
                {
                    // We are starting to force a flush to free scratch pages. We are doing it at this point (80% of the max scratch size)
                    // to make sure that next transactions will be able to allocate pages that we are going to free in the current transaction.
                    // Important notice: all pages freed by this run will get ValidAfterTransactionId == tx.Id (so only next ones can use it)

                    bool flushLockTaken = false;
                    using (tx.Environment.Journal.Applicator.TryTakeFlushingLock(ref flushLockTaken))
                    {
                        if (flushLockTaken) // if we are already flushing, we don't need to force a flush
                        {
                            try
                            {
                                tx.Environment.ForceLogFlushToDataFile(tx, allowToFlushOverwrittenPages: true);
                                _oldestTransactionWhenFlushWasForced = oldestActiveTransaction;
                            }
                            catch (TimeoutException)
                            {
                                // we'll try next time
                            }
                            catch (InvalidJournalFlushRequest)
                            {
                                // journals flushing already in progress
                            }
                        }
                    }
                }
            }

            if (sizeAfterAllocation > _sizeLimit)
            {
                var sp = Stopwatch.StartNew();

                // Our problem is that we don't have any available free pages, probably because
                // there are read transactions that are holding things open. We are going to see if
                // there are any free pages that _might_ be freed for us if we wait for a bit. The idea
                // is that we let the read transactions time to complete and do their work, at which point
                // we can continue running. It is possible that a long running read transaction
                // would in fact generate enough work for us to timeout, but hopefully we can avoid that.

                while (sp.ElapsedMilliseconds < tx.Environment.Options.ScratchBufferOverflowTimeout)
                {
                    if (_current.TryGettingFromAllocatedBuffer(tx, numberOfPages, size, out result))
                    {
                        return(result);
                    }
                    Thread.Sleep(32);
                }

                sp.Stop();

                bool createNextFile = false;

                if (_current.HasDiscontinuousSpaceFor(tx, size, _scratchBuffers.Count))
                {
                    // there is enough space for the requested allocation but the problem is its fragmentation
                    // so we will create a new scratch file and will allow to allocate new continuous range from there

                    createNextFile = true;
                }
                else if (_scratchBuffers.Count == 1 && _current.Size < _sizeLimit &&
                         (_current.ActivelyUsedBytes(oldestActiveTransaction) + size * AbstractPager.PageSize) < _sizeLimit)
                {
                    // there is only one scratch file that hasn't reach the size limit yet and
                    // the number of bytes being in active use allows to allocate the requested size
                    // let it create a new file

                    createNextFile = true;
                }

                if (createNextFile)
                {
                    _current = NextFile();

                    tx.EnsurePagerStateReference(_current.PagerState);

                    return(_current.Allocate(tx, numberOfPages, size));
                }

                var debugInfoBuilder = new StringBuilder();

                debugInfoBuilder.AppendFormat("Current transaction id: {0}\r\n", tx.Id);
                debugInfoBuilder.AppendFormat("Requested number of pages: {0} (adjusted size: {1} == {2:#,#;;0} KB)\r\n", numberOfPages, size, size * AbstractPager.PageSize / 1024);
                debugInfoBuilder.AppendFormat("Oldest active transaction: {0} (snapshot: {1})\r\n", tx.Environment.OldestTransaction, oldestActiveTransaction);
                debugInfoBuilder.AppendFormat("Oldest active transaction when flush was forced: {0}\r\n", _oldestTransactionWhenFlushWasForced);
                debugInfoBuilder.AppendFormat("Next write transaction id: {0}\r\n", tx.Environment.NextWriteTransactionId + 1);

                debugInfoBuilder.AppendLine("Active transactions:");
                foreach (var activeTransaction in tx.Environment.ActiveTransactions)
                {
                    debugInfoBuilder.AppendFormat("\tId: {0} - {1}\r\n", activeTransaction.Id, activeTransaction.Flags);
                }

                debugInfoBuilder.AppendLine("Scratch files usage:");
                foreach (var scratchBufferFile in _scratchBuffers.OrderBy(x => x.Key))
                {
                    debugInfoBuilder.AppendFormat("\t{0} - size: {1:#,#;;0} KB, in active use: {2:#,#;;0} KB\r\n", StorageEnvironmentOptions.ScratchBufferName(scratchBufferFile.Value.Number), scratchBufferFile.Value.Size / 1024, scratchBufferFile.Value.ActivelyUsedBytes(oldestActiveTransaction) / 1024);
                }

                debugInfoBuilder.AppendLine("Most available free pages:");
                foreach (var scratchBufferFile in _scratchBuffers.OrderBy(x => x.Key))
                {
                    debugInfoBuilder.AppendFormat("\t{0}\r\n", StorageEnvironmentOptions.ScratchBufferName(scratchBufferFile.Value.Number));

                    foreach (var freePage in scratchBufferFile.Value.GetMostAvailableFreePagesBySize())
                    {
                        debugInfoBuilder.AppendFormat("\t\tSize:{0}, ValidAfterTransactionId: {1}\r\n", freePage.Key, freePage.Value);
                    }
                }

                debugInfoBuilder.AppendFormat("Compression buffer size: {0:#,#;;0} KB\r\n", tx.Environment.Journal.CompressionBufferSize / 1024);

                string debugInfo = debugInfoBuilder.ToString();

                string message = string.Format("Cannot allocate more space for the scratch buffer.\r\n" +
                                               "Current file size is:\t{0:#,#;;0} KB.\r\n" +
                                               "Requested size for current file:\t{1:#,#;;0} KB.\r\n" +
                                               "Requested total size for all files:\t{2:#,#;;0} KB.\r\n" +
                                               "Limit:\t\t\t{3:#,#;;0} KB.\r\n" +
                                               "Already flushed and waited for {4:#,#;;0} ms for read transactions to complete.\r\n" +
                                               "Do you have a long running read transaction executing?\r\n" +
                                               "Debug info:\r\n{5}",
                                               _current.Size / 1024L,
                                               _current.SizeAfterAllocation(size) / 1024L,
                                               sizeAfterAllocation / 1024L,
                                               _sizeLimit / 1024L,
                                               sp.ElapsedMilliseconds,
                                               debugInfo
                                               );

                throw new ScratchBufferSizeLimitException(message);
            }

            // we don't have free pages to give out, need to allocate some
            result = _current.Allocate(tx, numberOfPages, size);
            _options.OnScratchBufferSizeChanged(sizeAfterAllocation);

            return(result);
        }