diff --git a/src/Libraries/Microsoft.Extensions.Caching.Hybrid/Internal/HybridCachePayload.cs b/src/Libraries/Microsoft.Extensions.Caching.Hybrid/Internal/HybridCachePayload.cs index 079bd295a02..9199a5cfd15 100644 --- a/src/Libraries/Microsoft.Extensions.Caching.Hybrid/Internal/HybridCachePayload.cs +++ b/src/Libraries/Microsoft.Extensions.Caching.Hybrid/Internal/HybridCachePayload.cs @@ -308,9 +308,7 @@ public static HybridCachePayloadParseResult TryParse(ArraySegment source, pendingTags = new(pendingTagBuffer[0]); break; default: - string[] final = new string[pendingTagsCount]; - pendingTagBuffer.CopyTo(final, 0); - pendingTags = new(final); + pendingTags = new(pendingTagBuffer.AsSpan(0, pendingTagsCount).ToArray()); break; } diff --git a/test/Libraries/Microsoft.Extensions.Caching.Hybrid.Tests/PayloadTests.cs b/test/Libraries/Microsoft.Extensions.Caching.Hybrid.Tests/PayloadTests.cs index a4a9c470551..6ee4a8a5558 100644 --- a/test/Libraries/Microsoft.Extensions.Caching.Hybrid.Tests/PayloadTests.cs +++ b/test/Libraries/Microsoft.Extensions.Caching.Hybrid.Tests/PayloadTests.cs @@ -322,4 +322,45 @@ public async Task MalformedTagDetected() collector.WriteTo(log); collector.AssertErrors([Log.IdTagInvalidUnicode]); } + + [Theory] + [InlineData("tag1,tag2", 2)] + [InlineData("tag1,tag2,tag3", 3)] + public void RoundTrip_WithPendingTags_WhenKnownTagsMismatch(string delimitedTags, int tagCount) + { + var clock = new FakeTime(); + using var provider = GetDefaultCache(out var cache, config => + { + config.AddSingleton(clock); + }); + + byte[] bytes = new byte[1024]; + new Random().NextBytes(bytes); + + string key = "my key"; + string[] tagsArray = delimitedTags.Split(','); + var writeTags = TagSet.Create(tagsArray); + Assert.Equal(tagCount, writeTags.Count); + + var maxLen = HybridCachePayload.GetMaxBytes(key, writeTags, bytes.Length); + var oversized = ArrayPool.Shared.Rent(maxLen); + + int actualLength = HybridCachePayload.Write(oversized, key, cache.CurrentTimestamp(), TimeSpan.FromMinutes(1), 0, writeTags, new(bytes)); + log.WriteLine($"bytes written: {actualLength}"); + + clock.Add(TimeSpan.FromSeconds(10)); + + // Inject non-completed tasks for each tag so IsTagExpired returns isPending=true + foreach (string tag in tagsArray) + { + cache.DebugInvalidateTag(tag, new TaskCompletionSource().Task); + } + + // Parse with empty knownTags to force all tags into pendingTags via the rented buffer path + var result = HybridCachePayload.TryParse(new(oversized, 0, actualLength), key, TagSet.Empty, cache, + out var payload, out var remaining, out var flags, out var entropy, out var pendingTags, out _); + Assert.Equal(HybridCachePayload.HybridCachePayloadParseResult.Success, result); + Assert.True(payload.SequenceEqual(bytes)); + Assert.Equal(tagCount, pendingTags.Count); + } }