From 3a6b9402d5f45b27b46861b21088e4eaedbacd2f Mon Sep 17 00:00:00 2001 From: Grisha Kotler Date: Wed, 5 Mar 2025 14:32:59 +0200 Subject: [PATCH] RavenDB-23704 - Move allocation pointer back when releasing memory at current allocation boundary --- src/Sparrow.Server/ByteString.cs | 6 +++- test/FastTests/Sparrow/ByteStringTests.cs | 34 ++++++++++++++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/Sparrow.Server/ByteString.cs b/src/Sparrow.Server/ByteString.cs index 0212954a7ac2..f7f274fb05fe 100644 --- a/src/Sparrow.Server/ByteString.cs +++ b/src/Sparrow.Server/ByteString.cs @@ -1336,7 +1336,11 @@ public void Release(ref ByteString value) int reusablePoolIndex = GetPoolIndexForReuse(value._pointer->Size); - if (value._pointer->Size <= ByteStringContext.MinBlockSizeInBytes) + if (value._pointer == _internalCurrent.Current - value._pointer->Size) + { + _internalCurrent.Current -= value._pointer->Size; + } + else if (value._pointer->Size <= ByteStringContext.MinBlockSizeInBytes) { FastStack pool = _internalReusableStringPool[reusablePoolIndex]; if (pool == null) diff --git a/test/FastTests/Sparrow/ByteStringTests.cs b/test/FastTests/Sparrow/ByteStringTests.cs index 2bde72effc69..0b3210d337de 100644 --- a/test/FastTests/Sparrow/ByteStringTests.cs +++ b/test/FastTests/Sparrow/ByteStringTests.cs @@ -1,4 +1,4 @@ -using System; +using Sparrow.Global; using Sparrow.Server; using Sparrow.Threading; using Tests.Infrastructure; @@ -195,6 +195,38 @@ public void CanResetAllocationBlockSize() } } + [RavenFact(RavenTestCategory.Memory)] + public void CanReuseMemory() + { + using (var context = new ByteStringContext(SharedMultipleUseFlag.None)) + { + while (context.AllocationBlockSize != 2 * Constants.Size.Megabyte) + { + context.Allocate(ByteStringContext.MinBlockSizeInBytes / 2, out _); + } + + Assert.Equal(2 * Constants.Size.Megabyte, context.AllocationBlockSize); + + const int toAllocate = ByteStringContext.MinBlockSizeInBytes * 5; + context.Allocate(toAllocate, out var first); + var ptrLocation = (long)first._pointer; + var allocatedBefore = context._totalAllocated; + context.Release(ref first); + + for (var i = 0; i < 512; i++) + { + var allocation = i % 2 == 0 ? toAllocate / 2 : toAllocate; + context.Allocate(allocation, out var byteString); + + Assert.Equal(ptrLocation, (long)byteString._pointer); + + context.Release(ref byteString); + } + + Assert.Equal(allocatedBefore, context._totalAllocated); + } + } + #if VALIDATE [Fact] public void ValidationKeyAfterAllocateAndReleaseReuseShouldBeDifferent()