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); }
public void WhenInitialFileSizeIsSetTheFileSizeForDataFileAndScratchFileShouldBeSetAccordinglyAndItWillBeRoundedToTheNearestGranularity() { var options = StorageEnvironmentOptions.ForPath(DataDir); options.InitialFileSize = GetExpectedInitialSize() * 2 + 1; using (new StorageEnvironment(options)) { var dataFile = Path.Combine(DataDir, Constants.DatabaseFilename); var scratchFile = Path.Combine(DataDir, StorageEnvironmentOptions.ScratchBufferName(0)); if (StorageEnvironmentOptions.RunningOnPosix) { // on Linux, we use 4K as the allocation granularity Assert.Equal(0, new FileInfo(dataFile).Length % 4096); Assert.Equal(0, new FileInfo(scratchFile).Length % 4096); } else { // on Windows, we use 64K as the allocation granularity Assert.Equal(0, new FileInfo(dataFile).Length % GetExpectedInitialSize()); Assert.Equal(0, new FileInfo(scratchFile).Length % GetExpectedInitialSize()); } } }
public ScratchBufferPoolInfo InfoForDebug(long oldestActiveTransaction) { var currentFile = _current.File; var scratchBufferPoolInfo = new ScratchBufferPoolInfo { OldestActiveTransaction = oldestActiveTransaction, NumberOfScratchFiles = _scratchBuffers.Count, CurrentFileNumber = currentFile.Number, CurrentFileSizeInMB = currentFile.Size / 1024L / 1024L, PerScratchFileSizeLimitInMB = _options.MaxScratchBufferSize / 1024L / 1024L, CurrentUtcTime = DateTime.UtcNow }; foreach (var scratchBufferItem in _scratchBuffers.Values.OrderBy(x => x.Number)) { var current = _current; var scratchFileUsage = new ScratchFileUsage { Name = StorageEnvironmentOptions.ScratchBufferName(scratchBufferItem.File.Number), SizeInKB = scratchBufferItem.File.Size / 1024, NumberOfAllocations = scratchBufferItem.File.NumberOfAllocations, AllocatedPagesCount = scratchBufferItem.File.AllocatedPagesCount, CanBeDeleted = scratchBufferItem != current && scratchBufferItem.File.HasActivelyUsedBytes(oldestActiveTransaction) == false, TxIdAfterWhichLatestFreePagesBecomeAvailable = scratchBufferItem.File.TxIdAfterWhichLatestFreePagesBecomeAvailable, IsInRecycleArea = _recycleArea.Contains(scratchBufferItem), NumberOfResets = scratchBufferItem.File.DebugInfo.NumberOfResets, LastResetTime = scratchBufferItem.File.DebugInfo.LastResetTime, LastFreeTime = scratchBufferItem.File.DebugInfo.LastFreeTime }; foreach (var freePage in scratchBufferItem.File.DebugInfo.GetMostAvailableFreePagesBySize()) { scratchFileUsage.MostAvailableFreePages.Add(new MostAvailableFreePagesBySize { Size = freePage.Key, ValidAfterTransactionId = freePage.Value }); } foreach (var allocatedPage in scratchBufferItem.File.DebugInfo.GetFirst10AllocatedPages()) { scratchFileUsage.First10AllocatedPages.Add(new AllocatedPageInScratchBuffer() { NumberOfPages = allocatedPage.NumberOfPages, PositionInScratchBuffer = allocatedPage.PositionInScratchBuffer, ScratchFileNumber = allocatedPage.ScratchFileNumber, ScratchPageNumber = allocatedPage.ScratchPageNumber, Size = allocatedPage.Size }); } scratchBufferPoolInfo.ScratchFilesUsage.Add(scratchFileUsage); } return(scratchBufferPoolInfo); }
public void DefaultScratchLocation() { var options = (StorageEnvironmentOptions.DirectoryStorageEnvironmentOptions)StorageEnvironmentOptions.ForPath(DataDir); using (var env = new StorageEnvironment(options)) { var scratchFile = Path.Combine(DataDir, StorageEnvironmentOptions.ScratchBufferName(0)); Assert.True(File.Exists(scratchFile)); } }
public void TempPathForVoronShouldWork2() { using (var storage = NewTransactionalStorage(requestedStorage: "voron", dataDir: path, tempDir: temp, runInMemory: false)) { var scratchFile = Path.Combine(path, StorageEnvironmentOptions.ScratchBufferName(0)); var scratchFileTemp = Path.Combine(temp, StorageEnvironmentOptions.ScratchBufferName(0)); Assert.False(File.Exists(scratchFile)); Assert.True(File.Exists(scratchFileTemp)); } }
public void ScratchLocationWithTemporaryPathSpecified() { var options = (StorageEnvironmentOptions.DirectoryStorageEnvironmentOptions)StorageEnvironmentOptions.ForPath(DataDir, DataDir + "Temp"); using (var env = new StorageEnvironment(options)) { var scratchFile = Path.Combine(DataDir, StorageEnvironmentOptions.ScratchBufferName(0)); var scratchFileTemp = Path.Combine(DataDir + "Temp", StorageEnvironmentOptions.ScratchBufferName(0)); Assert.False(File.Exists(scratchFile)); Assert.True(File.Exists(scratchFileTemp)); } }
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); }
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); }
public void WhenInitialFileSizeIsSetTheFileSizeForDataFileAndScratchFileShouldBeSetAccordingly() { var options = StorageEnvironmentOptions.ForPath(DataDir); options.InitialFileSize = GetExpectedInitialSize() * 2; using (new StorageEnvironment(options)) { var dataFile = Path.Combine(DataDir, Constants.DatabaseFilename); var scratchFile = Path.Combine(DataDir, StorageEnvironmentOptions.ScratchBufferName(0)); Assert.Equal(0, new FileInfo(dataFile).Length % GetExpectedInitialSize()); Assert.Equal(0, new FileInfo(scratchFile).Length % GetExpectedInitialSize()); } }
public void WhenInitialFileSizeIsSetTheFileSizeForDataFileAndScratchFileShouldBeSetAccordinglyAndItWillBeRoundedToTheNearestGranularity() { var options = StorageEnvironmentOptions.ForPath(path); options.InitialFileSize = GetExpectedInitialSize() * 2 + 1; using (new StorageEnvironment(options)) { var dataFile = Path.Combine(path, Constants.DatabaseFilename); var scratchFile = Path.Combine(path, StorageEnvironmentOptions.ScratchBufferName(0)); Assert.Equal(GetExpectedInitialSize() * 3, new FileInfo(dataFile).Length); Assert.Equal(GetExpectedInitialSize() * 3, new FileInfo(scratchFile).Length); } }
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); }
public void WhenInitialFileSizeIsNotSetTheFileSizeForDataFileAndScratchFileShouldBeSetToSystemAllocationGranularity() { var options = StorageEnvironmentOptions.ForPath(DataDir); options.InitialFileSize = null; using (new StorageEnvironment(options)) { var dataFile = Path.Combine(DataDir, Constants.DatabaseFilename); var scratchFile = Path.Combine(DataDir, StorageEnvironmentOptions.ScratchBufferName(0)); Assert.Equal(GetExpectedInitialSize(), new FileInfo(dataFile).Length); Assert.Equal(GetExpectedInitialSize(), new FileInfo(scratchFile).Length); } }
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 void WhenInitialFileSizeIsSetTheFileSizeForDataFileAndScratchFileShouldBeSetAccordinglyAndItWillBeRoundedToTheNearestGranularity() { Win32NativeMethods.SYSTEM_INFO systemInfo; Win32NativeMethods.GetSystemInfo(out systemInfo); var options = StorageEnvironmentOptions.ForPath(path); options.InitialFileSize = systemInfo.allocationGranularity * 2 + 1; using (new StorageEnvironment(options)) { var dataFile = Path.Combine(path, Constants.DatabaseFilename); var scratchFile = Path.Combine(path, StorageEnvironmentOptions.ScratchBufferName(0)); Assert.Equal(systemInfo.allocationGranularity * 3, new FileInfo(dataFile).Length); Assert.Equal(systemInfo.allocationGranularity * 3, new FileInfo(scratchFile).Length); } }
public ScratchBufferPoolInfo InfoForDebug(long oldestActiveTransaction) { var currentFile = _current.File; var scratchBufferPoolInfo = new ScratchBufferPoolInfo { OldestActiveTransaction = oldestActiveTransaction, NumberOfScratchFiles = _scratchBuffers.Count, CurrentFileNumber = currentFile.Number, CurrentFileSizeInMB = currentFile.Size / 1024L / 1024L, PerScratchFileSizeLimitInMB = _options.MaxScratchBufferSize / 1024L / 1024L }; foreach (var scratchBufferItem in _scratchBuffers.Values.OrderBy(x => x.Number)) { var current = _current; var scratchFileUsage = new ScratchFileUsage { Name = StorageEnvironmentOptions.ScratchBufferName(scratchBufferItem.File.Number), SizeInKB = scratchBufferItem.File.Size / 1024, NumberOfAllocations = scratchBufferItem.File.NumberOfAllocations, AllocatedPagesCount = scratchBufferItem.File.AllocatedPagesCount, CanBeDeleted = scratchBufferItem != current && scratchBufferItem.File.HasActivelyUsedBytes(oldestActiveTransaction) == false, TxIdAfterWhichLatestFreePagesBecomeAvailable = scratchBufferItem.File.TxIdAfterWhichLatestFreePagesBecomeAvailable }; foreach (var freePage in scratchBufferItem.File.GetMostAvailableFreePagesBySize()) { scratchFileUsage.MostAvailableFreePages.Add(new MostAvailableFreePagesBySize { Size = freePage.Key, ValidAfterTransactionId = freePage.Value }); } scratchBufferPoolInfo.ScratchFilesUsage.Add(scratchFileUsage); } return(scratchBufferPoolInfo); }
private void ThrowScratchBufferTooBig(LowLevelTransaction tx, int numberOfPages, long size, long oldestActiveTransaction, long sizeAfterAllocation, Stopwatch sp, ScratchBufferItem current) { var debugInfoBuilder = new StringBuilder(); var totalPages = tx.GetTransactionPages().Count; debugInfoBuilder.AppendFormat("Current transaction id: {0}\r\n", tx.Id); if ((totalPages + numberOfPages) * tx.Environment.Options.PageSize >= _sizeLimit / 2) { debugInfoBuilder.Append("- - - - - - - - - - - - -\r\n"); debugInfoBuilder.AppendFormat( "This transaction is VERY big, and requires {0:##,###;;0} kb out of {1:##,###;;0} kb allows!\r\n", ((totalPages + numberOfPages) * tx.Environment.Options.PageSize) / 1024, _sizeLimit / 1024 ); debugInfoBuilder.Append("- - - - - - - - - - - - -\r\n"); } debugInfoBuilder.AppendFormat("Requested number of pages: {0} (adjusted size: {1} == {2:#,#;;0} KB)\r\n", numberOfPages, size, size * tx.Environment.Options.PageSize / 1024); debugInfoBuilder.AppendFormat("Total number of pages in tx: {0} (adjusted size: {1} == {2:#,#;;0} KB)\r\n", totalPages, totalPages, totalPages * tx.Environment.Options.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", current.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.File.Size / 1024, scratchBufferFile.Value.File.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.File.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.File.Size / 1024L, current.File.SizeAfterAllocation(size) / 1024L, sizeAfterAllocation / 1024L, _sizeLimit / 1024L, sp.ElapsedMilliseconds, debugInfo ); throw new ScratchBufferSizeLimitException(message); }
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); }