From 9804d4a3673a3b2658a5fd65bdf4f01b2ec8dbc0 Mon Sep 17 00:00:00 2001 From: aviv Date: Thu, 14 Dec 2023 15:01:07 +0200 Subject: [PATCH] RavenDB-14041 : AttachmentStorage.Put - do not throw a concurrency exception on partial update with a valid expected change vector --- .../Documents/AttachmentsStorage.cs | 4 +- test/SlowTests/Issues/RavenDB_14041.cs | 51 +++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 test/SlowTests/Issues/RavenDB_14041.cs diff --git a/src/Raven.Server/Documents/AttachmentsStorage.cs b/src/Raven.Server/Documents/AttachmentsStorage.cs index 81be16953a7a..309fd0fb0658 100644 --- a/src/Raven.Server/Documents/AttachmentsStorage.cs +++ b/src/Raven.Server/Documents/AttachmentsStorage.cs @@ -236,12 +236,14 @@ void SetTableValue(TableValueBuilder tvb, Slice cv) else { var putStream = true; + var attachmentExists = false; // We already asserted that the document is not in conflict, so we might have just one partial key, not more. using (GetAttachmentPartialKey(context, keySlice, base64Hash.Size, lowerContentType.Size, out Slice partialKeySlice)) { if (table.SeekOnePrimaryKeyPrefix(partialKeySlice, out TableValueReader partialTvr)) { + attachmentExists = true; if (expectedChangeVector != null) { var oldChangeVector = TableValueToChangeVector(context, (int)AttachmentsTable.ChangeVector, ref partialTvr); @@ -282,7 +284,7 @@ void SetTableValue(TableValueBuilder tvb, Slice cv) } } - if (string.IsNullOrEmpty(expectedChangeVector) == false) + if (attachmentExists == false && string.IsNullOrEmpty(expectedChangeVector) == false) { ThrowConcurrentExceptionOnMissingAttachment(documentId, name, expectedChangeVector); } diff --git a/test/SlowTests/Issues/RavenDB_14041.cs b/test/SlowTests/Issues/RavenDB_14041.cs new file mode 100644 index 000000000000..098b37c19a04 --- /dev/null +++ b/test/SlowTests/Issues/RavenDB_14041.cs @@ -0,0 +1,51 @@ +using System.IO; +using Raven.Client.Documents.Commands.Batches; +using SlowTests.Core.Utils.Entities; +using Tests.Infrastructure; +using Xunit; +using Xunit.Abstractions; + +namespace SlowTests.Issues +{ + public class RavenDB_14041 : ClusterTestBase + { + public RavenDB_14041(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void CanUpdateAttachmentUsingPutAttachmentCommandDataWithExpectedChangeVector() + { + using (var store = GetDocumentStore()) + { + const string documentId = "cats/1-A"; + const string name = "bruhhh"; + const string contentType = "stuff"; + + using (var session = store.OpenSession()) + { + session.Store(new User(), documentId); + + using var stream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5 }); + session.Advanced.Attachments.Store(documentId, name, stream, contentType); + + session.SaveChanges(); + } + + // update attachment + using (var session = store.OpenSession()) + { + var attachment = session.Advanced.Attachments.Get(documentId, name); + using var stream = new MemoryStream(new byte[] { 6, 7, 8, 9, 10 }); + + // should not throw a concurrency exception + var cmd = new PutAttachmentCommandData(documentId, name, stream, contentType, changeVector: attachment.Details.ChangeVector); + session.Advanced.Defer(cmd); + session.SaveChanges(); + } + + } + } + } + +}