Skip to content

Commit

Permalink
RavenDB-16815 - persist document collection in attachment metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
garayx committed Aug 29, 2024
1 parent 4f3a258 commit d8ccb87
Show file tree
Hide file tree
Showing 17 changed files with 127 additions and 66 deletions.
1 change: 1 addition & 0 deletions src/Raven.Client/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ private Headers()
public const string AttachmentSize = "Attachment-Size";
public const string AttachmentRetireAt = "Attachment-Retire-At";
public const string AttachmentFlags = "Attachment-Flags";
// public const string AttachmentCollection = "Attachment-Collection";

internal const string DatabaseMissing = "Database-Missing";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ public class AttachmentName
public long Size;
public AttachmentFlags Flags;
public DateTime? RetireAt;
public string Collection;

internal virtual DynamicJsonValue ToJson()
{
var json = new DynamicJsonValue
Expand All @@ -46,7 +48,7 @@ internal virtual DynamicJsonValue ToJson()
};
json[nameof(Flags)] = Flags.ToString();
json[nameof(RetireAt)] = RetireAt;

json[nameof(Collection)] = Collection;
return json;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ internal static async Task<AttachmentResult> AttachmentResult(HttpResponseMessag
int flags = 0;
if (response.Headers.TryGetValues(Constants.Headers.AttachmentFlags, out IEnumerable<string> flagsVal))
int.TryParse(flagsVal.First(), out flags);

// var collection = response.Headers.TryGetValues(Constants.Headers.AttachmentCollection, out IEnumerable<string> collectionVal) ? collectionVal.First() : null;
var attachmentDetails = new AttachmentDetails
{
ContentType = contentType,
Expand All @@ -138,7 +138,8 @@ internal static async Task<AttachmentResult> AttachmentResult(HttpResponseMessag
ChangeVector = changeVector,
DocumentId = documentId,
RetireAt = attachmentRetireAt,
Flags = (AttachmentFlags)flags
Flags = (AttachmentFlags)flags,
// Collection = collection
};

var responseStream = await response.Content.ReadAsStreamWithZstdSupportAsync().ConfigureAwait(false);
Expand Down
1 change: 1 addition & 0 deletions src/Raven.Server/Documents/Attachment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ public sealed class Attachment
public long Size;
public AttachmentFlags Flags;
public DateTime? RetiredAt;
public LazyStringValue Collection;
}
}
59 changes: 38 additions & 21 deletions src/Raven.Server/Documents/AttachmentsStorage.cs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/Raven.Server/Documents/DocumentsStorage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1779,7 +1779,7 @@ public static Tombstone TableValueToTombstone(JsonOperationContext context, ref
}

if (flags.Contain(DocumentFlags.HasAttachments))
AttachmentsStorage.DeleteAttachmentsOfDocument(context, lowerId, changeVector, modifiedTicks, collectionName.Name, newFlags);
AttachmentsStorage.DeleteAttachmentsOfDocument(context, lowerId, changeVector, modifiedTicks, newFlags);

if (flags.Contain(DocumentFlags.HasCounters))
CountersStorage.DeleteCountersForDocument(context, id, collectionName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ protected override async ValueTask GetAttachmentAsync(DocumentsOperationContext
HttpContext.Response.Headers[Constants.Headers.Etag] = $"\"{attachment.ChangeVector}\"";
HttpContext.Response.Headers[Constants.Headers.AttachmentRetireAt] = attachment.RetiredAt?.GetDefaultRavenFormat();
HttpContext.Response.Headers[Constants.Headers.AttachmentFlags] = ((int)attachment.Flags).ToString();

// HttpContext.Response.Headers[Constants.Headers.AttachmentCollection] = attachment.Collection.ToString();
DisposeReadTransactionIfNeeded(tx);

await WriteResponseStream(context, attachment, collection, token);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using NetTopologySuite.Utilities;
using Raven.Client.Documents.Attachments;
using Raven.Client.Documents.Operations.Attachments;
using Raven.Client.Documents.Replication.Messages;
Expand Down Expand Up @@ -392,6 +394,8 @@ protected override long ExecuteCmd(DocumentsOperationContext context)

toDispose.Add(DocumentIdWorker.GetLowerIdSliceAndStorageKey(context, attachment.Name, out _, out Slice attachmentName));
toDispose.Add(DocumentIdWorker.GetLowerIdSliceAndStorageKey(context, attachment.ContentType, out _, out Slice contentType));
toDispose.Add(DocumentIdWorker.GetLowerIdSliceAndStorageKey(context, attachment.Collection, out _, out Slice collectionSlice));


var local = context.GetChangeVector(result.ChangeVector);
var newChangeVector = ChangeVectorUtils.GetConflictStatus(incomingChangeVector, local) switch
Expand All @@ -406,7 +410,7 @@ protected override long ExecuteCmd(DocumentsOperationContext context)
if (newChangeVector != null)
{
database.DocumentsStorage.AttachmentsStorage.PutDirect(context, attachment.Key, attachmentName,
contentType, attachment.Base64Hash, attachment.RetiredAtUtc, attachment.Flags, attachment.AttachmentSize, isRevision, newChangeVector);
contentType, attachment.Base64Hash, attachment.RetiredAtUtc, collectionSlice, attachment.Flags, attachment.AttachmentSize,isRevision, newChangeVector);
}

break;
Expand All @@ -433,15 +437,10 @@ protected override long ExecuteCmd(DocumentsOperationContext context)

try
{
string collection;
using (var doc1 = context.DocumentDatabase.DocumentsStorage.Get(context, documentId, DocumentFields.Data, throwOnConflict: false))
{
doc1.TryGetCollection(out collection);
}


database.DocumentsStorage.AttachmentsStorage.DeleteAttachmentDirect(context, attachmentTombstone.Key, false, "$fromReplication", null,
newChangeVector,
attachmentTombstone.LastModifiedTicks, collection);
attachmentTombstone.LastModifiedTicks);
}
catch (Exception e)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Globalization;
using System.IO;
using Raven.Client.Documents.Attachments;
using Raven.Client.Extensions;
using Raven.Client.Util;
using Raven.Server.Documents.Replication.Stats;
using Raven.Server.ServerWide.Context;
Expand All @@ -23,6 +24,7 @@ public sealed class AttachmentReplicationItem : ReplicationBatchItem
public long AttachmentSize;
public AttachmentFlags Flags;
public DateTime? RetiredAtUtc;
public LazyStringValue Collection;

public override long Size => base.Size + // common

Expand All @@ -36,10 +38,14 @@ public sealed class AttachmentReplicationItem : ReplicationBatchItem
ContentType.Size +

sizeof(byte) + // size of Base64Hash
Base64Hash.Size
Base64Hash.Size

+ sizeof(long)
+ sizeof(int)
+ (RetiredAtUtc == null ? 0 : sizeof(long));
+ (RetiredAtUtc == null ? 0 : sizeof(long))
+ sizeof(int)
+ Collection.Size
;


public long StreamSize => sizeof(byte) + // type
Expand All @@ -57,6 +63,9 @@ public override DynamicJsonValue ToDebugJson()
djv[nameof(ContentType)] = ContentType.ToString(CultureInfo.InvariantCulture);
djv[nameof(Base64Hash)] = Base64Hash.ToString();
djv[nameof(Key)] = CompoundKeyHelper.ExtractDocumentId(Key);
djv[nameof(Flags)] = Flags.ToString();
djv[nameof(RetiredAtUtc)] = RetiredAtUtc;
djv[nameof(Collection)] = Collection.ToString();
return djv;
}

Expand All @@ -74,7 +83,8 @@ public static unsafe AttachmentReplicationItem From(DocumentsOperationContext co
TransactionMarker = attachment.TransactionMarker,
AttachmentSize = attachment.Size,
Flags = attachment.Flags,
RetiredAtUtc = attachment.RetiredAt
RetiredAtUtc = attachment.RetiredAt,
Collection = attachment.Collection
};

// although the key is LSV but is treated as slice and doesn't respect escaping
Expand Down Expand Up @@ -128,6 +138,14 @@ public override unsafe void Write(Slice changeVector, Stream stream, byte[] temp
*(AttachmentFlags*)(pTemp + tempBufferPos) = Flags;
tempBufferPos += sizeof(AttachmentFlags);


*(int*)(pTemp + tempBufferPos) = Collection.Size;
tempBufferPos += sizeof(int);
Memory.Copy(pTemp + tempBufferPos, Collection.Buffer, Collection.Size);
tempBufferPos += Collection.Size;



stream.Write(tempBuffer, 0, tempBufferPos);
stats.RecordAttachmentOutput(Size);
}
Expand All @@ -152,6 +170,8 @@ public override unsafe void Read(JsonOperationContext context, ByteStringContext
RetiredAtUtc = new DateTime(ticks, DateTimeKind.Utc);

Flags = *(AttachmentFlags*)Reader.ReadExactly(sizeof(AttachmentFlags)) | AttachmentFlags.None;
SetLazyStringValueFromString(context, out Collection);


stats.RecordAttachmentRead(Size);
}
Expand Down Expand Up @@ -182,7 +202,7 @@ protected override ReplicationBatchItem CloneInternal(JsonOperationContext conte
item.AttachmentSize = AttachmentSize;
item.RetiredAtUtc = RetiredAtUtc;
item.Flags = Flags;

item.Collection = Collection.Clone(context);
item.ToDispose(new DisposableAction(() =>
{
item.Base64Hash.Release(allocator);
Expand Down Expand Up @@ -252,6 +272,7 @@ protected override void InnerDispose()
Name?.Dispose();
ContentType?.Dispose();
Stream?.Dispose();
Collection?.Dispose();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,8 @@ private void ResolveAttachmentsConflicts(DocumentsOperationContext context, Docu
x.ContentType == attachment.ContentType &&
x.Flags == attachment.Flags &&
x.Size == attachment.Size &&
x.RetireAt == attachment.RetireAt))
x.RetireAt == attachment.RetireAt &&
x.Collection == attachment.Collection))
{
found = true;
// we have to generate a _new_ change vector for the attachment, since it is resolved, to ensure
Expand Down
2 changes: 1 addition & 1 deletion src/Raven.Server/Documents/Revisions/RevisionsStorage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -923,7 +923,7 @@ internal void DeleteRevisionFromTable(DocumentsOperationContext context, Table t

if (revision.Flags.Contain(DocumentFlags.HasAttachments))
{
_documentsStorage.AttachmentsStorage.DeleteRevisionAttachments(context, revision, changeVector, lastModifiedTicks, collectionName.Name, flags);
_documentsStorage.AttachmentsStorage.DeleteRevisionAttachments(context, revision, changeVector, lastModifiedTicks, flags);
}

Table writeTable = null;
Expand Down
3 changes: 2 additions & 1 deletion src/Raven.Server/Documents/Schemas/Attachments.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ e.g. fitz(record-separator)profile.png and fitz0(record-separator)profile.png, w
ChangeVector = 6,
Size = 7,
Flags = 8,
RetireAt = 9
RetireAt = 9,
Collection = 10
}

static Attachments()
Expand Down
13 changes: 5 additions & 8 deletions src/Raven.Server/Smuggler/Documents/DatabaseDestination.cs
Original file line number Diff line number Diff line change
Expand Up @@ -645,12 +645,7 @@ protected override long ExecuteCmd(DocumentsOperationContext context)
throw new InvalidOperationException("Cannot find a document ID inside the attachment key");
var attachmentId = key.Content.Substring(idEnd);
idsOfDocumentsToUpdateAfterAttachmentDeletion.Add(attachmentId);
string collection;
using (var doc1 = context.DocumentDatabase.DocumentsStorage.Get(context, attachmentId, DocumentFields.Default, throwOnConflict: false))
{
doc1.TryGetCollection(out collection);
}
_database.DocumentsStorage.AttachmentsStorage.DeleteAttachmentDirect(context, key, false, "$fromReplication", null, tombstone.ChangeVector, tombstone.LastModified.Ticks, collection);
_database.DocumentsStorage.AttachmentsStorage.DeleteAttachmentDirect(context, key, false, "$fromReplication", null, tombstone.ChangeVector, tombstone.LastModified.Ticks);
break;

case Tombstone.TombstoneType.Revision:
Expand Down Expand Up @@ -845,7 +840,8 @@ private unsafe void PutAttachments(DocumentsOperationContext context, Document d
attachment.TryGet(nameof(AttachmentName.Hash), out LazyStringValue hash) == false ||
attachment.TryGet(nameof(AttachmentName.Flags), out AttachmentFlags flags) == false ||
attachment.TryGet(nameof(AttachmentName.Size), out long size) == false ||
attachment.TryGet(nameof(AttachmentName.RetireAt), out DateTime? retireAt) == false)
attachment.TryGet(nameof(AttachmentName.RetireAt), out DateTime? retireAt) == false ||
attachment.TryGet(nameof(AttachmentName.Collection), out LazyStringValue collection) == false)

throw new ArgumentException($"The attachment info is missing a mandatory value: {attachment}");

Expand All @@ -862,11 +858,12 @@ private unsafe void PutAttachments(DocumentsOperationContext context, Document d
using (DocumentIdWorker.GetLowerIdSliceAndStorageKey(_context, name, out Slice lowerName, out Slice nameSlice))
using (DocumentIdWorker.GetLowerIdSliceAndStorageKey(_context, contentType, out Slice lowerContentType, out Slice contentTypeSlice))
using (Slice.External(_context.Allocator, hash, out Slice base64Hash))
using (DocumentIdWorker.GetLowerIdSliceAndStorageKey(_context, collection, out Slice _, out Slice collectionSlice))
using (Slice.From(_context.Allocator, document.ChangeVector, out Slice cv))
using (attachmentsStorage.GetAttachmentKey(_context, lowerDocumentId.Content.Ptr, lowerDocumentId.Size, lowerName.Content.Ptr, lowerName.Size,
base64Hash, lowerContentType.Content.Ptr, lowerContentType.Size, type, cv, out Slice keySlice))
{
attachmentsStorage.PutDirect(context, keySlice, nameSlice, contentTypeSlice, base64Hash, retireAt, flags, size, isRevision: true);
attachmentsStorage.PutDirect(context, keySlice, nameSlice, contentTypeSlice, base64Hash, retireAt, collectionSlice, flags, size, isRevision: true);
}
}
}
Expand Down
26 changes: 23 additions & 3 deletions src/Raven.Server/Smuggler/Documents/StreamSource.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
Expand Down Expand Up @@ -1551,7 +1552,8 @@ public static BlittableJsonReaderObject WriteDummyDocumentForAttachment(JsonOper
["ContentType"] = string.Empty,
["Size"] = details.Size,
[nameof(AttachmentName.Flags)] = AttachmentFlags.None,
[nameof(AttachmentName.RetireAt)] = null
[nameof(AttachmentName.RetireAt)] = null,
[nameof(AttachmentName.Collection)] = "@files"
};
var attachments = new DynamicJsonArray();
attachments.Add(attachment);
Expand Down Expand Up @@ -1617,10 +1619,10 @@ private async IAsyncEnumerable<DocumentItem> ReadDocumentsAsync(List<string> col

var data = builder.CreateReader();
builder.Reset();

string collectionName = null;
if (data.TryGet(Constants.Documents.Metadata.Key, out BlittableJsonReaderObject metadata))
{
if (metadata.TryGet(Constants.Documents.Metadata.Collection, out string collectionName))
if (metadata.TryGet(Constants.Documents.Metadata.Collection, out collectionName))
{
if (collectionsHashSet.Count > 0 && collectionsHashSet.Contains(collectionName) == false)
{
Expand Down Expand Up @@ -1741,6 +1743,24 @@ private async IAsyncEnumerable<DocumentItem> ReadDocumentsAsync(List<string> col
}
}

if (attachmentInMetadata.TryGet(nameof(AttachmentName.Collection), out string _) == false)
{
if (attachmentInMetadata.Modifications == null)
{

Debug.Assert(collectionName != null);
attachmentInMetadata.Modifications = new DynamicJsonValue(attachmentInMetadata)
{
[nameof(AttachmentName.Collection)] = collectionName
};
}
else
{
attachmentInMetadata.Modifications[nameof(AttachmentName.Collection)] = collectionName;
}
}


if (attachmentInMetadata.Modifications != null)
{
didWork = true;
Expand Down
2 changes: 1 addition & 1 deletion test/FastTests/Client/RavenCommandTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public void WhenCommandCanBeCheckedForFastestNode_ItCanRunInParallel()
"PutDatabaseClientConfigurationCommand", "PutDatabaseSettingsCommand", "PutDatabaseStudioConfigurationCommand", "GetTcpInfoForReplicationCommand",
"AddQueueSinkCommand", "UpdateQueueSinkCommand", "ConfigureDataArchivalCommand",
"AdoptOrphanedRevisionsCommand", "ConfigureAttachmentsRetireCommand",
"DeleteRetiredAttachmentCommand", "GetRetiredAttachmentCommand", "GetRetiredAttachmentsCommand", "DeleteRetiredAttachmentsCommand"
"DeleteRetiredAttachmentCommand", "GetRetiredAttachmentCommand", "GetRetiredAttachmentsCommand", "DeleteRetiredAttachmentsCommand", "GetRetireAttachmentsConfigurationCommand"
}.OrderBy(t => t);

var commandBaseType = typeof(RavenCommand<>);
Expand Down
Loading

0 comments on commit d8ccb87

Please sign in to comment.