private void DeleteOldRevisions(DocumentsOperationContext context, Table table, Slice lowerId, CollectionName collectionName, RevisionsCollectionConfiguration configuration, NonPersistentDocumentFlags nonPersistentFlags, string changeVector) { using (GetKeyPrefix(context, lowerId, out Slice prefixSlice)) { // We delete the old revisions after we put the current one, // because in case that MinimumRevisionsToKeep is 3 or lower we may get a revision document from replication // which is old. But because we put it first, we make sure to clean this document, because of the order to the revisions. var revisionsCount = IncrementCountOfRevisions(context, prefixSlice, 1); DeleteOldRevisions(context, table, prefixSlice, collectionName, configuration, revisionsCount, nonPersistentFlags, changeVector); } }
public void AddConflict( DocumentsOperationContext context, string id, long lastModifiedTicks, BlittableJsonReaderObject incomingDoc, string incomingChangeVector, string incomingTombstoneCollection, DocumentFlags flags, NonPersistentDocumentFlags nonPersistentFlags = NonPersistentDocumentFlags.None) { if (_logger.IsInfoEnabled) { _logger.Info($"Adding conflict to {id} (Incoming change vector {incomingChangeVector})"); } var tx = context.Transaction.InnerTransaction; var conflictsTable = tx.OpenTable(ConflictsSchema, ConflictsSlice); var fromSmuggler = (nonPersistentFlags & NonPersistentDocumentFlags.FromSmuggler) == NonPersistentDocumentFlags.FromSmuggler; using (DocumentIdWorker.GetLowerIdSliceAndStorageKey(context, id, out Slice lowerId, out Slice idPtr)) { CollectionName collectionName; // ReSharper disable once ArgumentsStyleLiteral var existing = _documentsStorage.GetDocumentOrTombstone(context, id, throwOnConflict: false); if (existing.Document != null) { var existingDoc = existing.Document; if (fromSmuggler == false) { using (Slice.From(context.Allocator, existingDoc.ChangeVector, out Slice cv)) using (DocumentIdWorker.GetStringPreserveCase(context, CollectionName.GetLazyCollectionNameFrom(context, existingDoc.Data), out Slice collectionSlice)) using (conflictsTable.Allocate(out TableValueBuilder tvb)) { tvb.Add(lowerId); tvb.Add(SpecialChars.RecordSeparator); tvb.Add(cv); tvb.Add(idPtr); tvb.Add(existingDoc.Data.BasePointer, existingDoc.Data.Size); tvb.Add(Bits.SwapBytes(_documentsStorage.GenerateNextEtag())); tvb.Add(collectionSlice); tvb.Add(existingDoc.LastModified.Ticks); tvb.Add((int)existingDoc.Flags); if (conflictsTable.Set(tvb)) { Interlocked.Increment(ref ConflictsCount); } } } // we delete the data directly, without generating a tombstone, because we have a // conflict instead _documentsStorage.EnsureLastEtagIsPersisted(context, existingDoc.Etag); collectionName = _documentsStorage.ExtractCollectionName(context, existingDoc.Data); //make sure that the relevant collection tree exists var table = tx.OpenTable(DocsSchema, collectionName.GetTableName(CollectionTableType.Documents)); table.Delete(existingDoc.StorageId); } else if (existing.Tombstone != null) { var existingTombstone = existing.Tombstone; if (fromSmuggler == false) { using (Slice.From(context.Allocator, existingTombstone.ChangeVector, out var cv)) using (DocumentIdWorker.GetStringPreserveCase(context, existingTombstone.Collection, out Slice collectionSlice)) using (conflictsTable.Allocate(out TableValueBuilder tvb)) { tvb.Add(lowerId); tvb.Add(SpecialChars.RecordSeparator); tvb.Add(cv); tvb.Add(idPtr); tvb.Add(null, 0); tvb.Add(Bits.SwapBytes(_documentsStorage.GenerateNextEtag())); tvb.Add(collectionSlice); tvb.Add(existingTombstone.LastModified.Ticks); tvb.Add((int)existingTombstone.Flags); if (conflictsTable.Set(tvb)) { Interlocked.Increment(ref ConflictsCount); } } } // we delete the data directly, without generating a tombstone, because we have a // conflict instead _documentsStorage.EnsureLastEtagIsPersisted(context, existingTombstone.Etag); collectionName = _documentsStorage.GetCollection(existingTombstone.Collection, throwIfDoesNotExist: true); var table = tx.OpenTable(TombstonesSchema, collectionName.GetTableName(CollectionTableType.Tombstones)); table.Delete(existingTombstone.StorageId); } else // has existing conflicts { collectionName = _documentsStorage.ExtractCollectionName(context, incomingDoc); using (GetConflictsIdPrefix(context, lowerId, out Slice prefixSlice)) { var conflicts = GetConflictsFor(context, prefixSlice); foreach (var conflict in conflicts) { var conflictStatus = ChangeVectorUtils.GetConflictStatus(incomingChangeVector, conflict.ChangeVector); switch (conflictStatus) { case ConflictStatus.Update: DeleteConflictsFor(context, conflict.ChangeVector); // delete this, it has been subsumed break; case ConflictStatus.Conflict: if (fromSmuggler && DocumentCompare.IsEqualTo(conflict.Doc, incomingDoc, false) == DocumentCompareResult.Equal) { return; // we already have a conflict with equal content, no need to create another one } break; // we'll add this conflict if no one else also includes it case ConflictStatus.AlreadyMerged: return; // we already have a conflict that includes this version default: throw new ArgumentOutOfRangeException("Invalid conflict status " + conflictStatus); } } } } var etag = _documentsStorage.GenerateNextEtag(); if (context.LastDatabaseChangeVector == null) { context.LastDatabaseChangeVector = GetDatabaseChangeVector(context); } ChangeVectorUtils.TryUpdateChangeVector(_documentDatabase.ServerStore.NodeTag, _documentDatabase.DbId, etag, ref context.LastDatabaseChangeVector); byte * doc = null; var docSize = 0; string collection; if (incomingDoc != null) // can be null if it is a tombstone { doc = incomingDoc.BasePointer; docSize = incomingDoc.Size; collection = CollectionName.GetLazyCollectionNameFrom(context, incomingDoc); } else { collection = incomingTombstoneCollection; } using (Slice.From(context.Allocator, incomingChangeVector, out var cv)) using (DocumentIdWorker.GetStringPreserveCase(context, collection, out Slice collectionSlice)) using (conflictsTable.Allocate(out TableValueBuilder tvb)) { tvb.Add(lowerId); tvb.Add(SpecialChars.RecordSeparator); tvb.Add(cv); tvb.Add(idPtr); tvb.Add(doc, docSize); tvb.Add(Bits.SwapBytes(etag)); tvb.Add(collectionSlice); tvb.Add(lastModifiedTicks); tvb.Add((int)flags); if (conflictsTable.Set(tvb)) { Interlocked.Increment(ref ConflictsCount); } } context.Transaction.AddAfterCommitNotification(new DocumentChange { ChangeVector = incomingChangeVector, CollectionName = collectionName.Name, Id = id, Type = DocumentChangeTypes.Conflict, }); } }
public void Put(DocumentsOperationContext context, string id, BlittableJsonReaderObject document, DocumentFlags flags, NonPersistentDocumentFlags nonPersistentFlags, string changeVector, long lastModifiedTicks, RevisionsCollectionConfiguration configuration = null, CollectionName collectionName = null) { Debug.Assert(changeVector != null, "Change vector must be set"); Debug.Assert(lastModifiedTicks != DateTime.MinValue.Ticks, "last modified ticks must be set"); BlittableJsonReaderObject.AssertNoModifications(document, id, assertChildren: true); if (collectionName == null) { collectionName = _database.DocumentsStorage.ExtractCollectionName(context, document); } using (DocumentIdWorker.GetLowerIdSliceAndStorageKey(context, id, out Slice lowerId, out Slice idPtr)) { var fromSmuggler = (nonPersistentFlags & NonPersistentDocumentFlags.FromSmuggler) == NonPersistentDocumentFlags.FromSmuggler; var fromReplication = (nonPersistentFlags & NonPersistentDocumentFlags.FromReplication) == NonPersistentDocumentFlags.FromReplication; var table = EnsureRevisionTableCreated(context.Transaction.InnerTransaction, collectionName); // We want the revision's attachments to have a lower etag than the revision itself if ((flags & DocumentFlags.HasAttachments) == DocumentFlags.HasAttachments && fromSmuggler == false) { using (Slice.From(context.Allocator, changeVector, out Slice changeVectorSlice)) { if (table.VerifyKeyExists(changeVectorSlice) == false) { _documentsStorage.AttachmentsStorage.RevisionAttachments(context, lowerId, changeVectorSlice); } } } if (fromReplication) { void PutFromRevisionIfChangeVectorIsGreater() { bool hasDoc; TableValueReader tvr; try { hasDoc = _documentsStorage.GetTableValueReaderForDocument(context, lowerId, throwOnConflict: true, tvr: out tvr); } catch (DocumentConflictException) { // Do not modify the document. return; } if (hasDoc == false) { PutFromRevision(); return; } var docChangeVector = TableValueToChangeVector(context, (int)DocumentsTable.ChangeVector, ref tvr); if (ChangeVectorUtils.GetConflictStatus(changeVector, docChangeVector) == ConflictStatus.Update) { PutFromRevision(); } void PutFromRevision() { _documentsStorage.Put(context, id, null, document, lastModifiedTicks, changeVector, flags & ~DocumentFlags.Revision, nonPersistentFlags | NonPersistentDocumentFlags.FromRevision); } } PutFromRevisionIfChangeVectorIsGreater(); } flags |= DocumentFlags.Revision; var newEtag = _database.DocumentsStorage.GenerateNextEtag(); var newEtagSwapBytes = Bits.SwapBytes(newEtag); using (table.Allocate(out TableValueBuilder tvb)) using (Slice.From(context.Allocator, changeVector, out var cv)) { tvb.Add(cv.Content.Ptr, cv.Size); tvb.Add(lowerId); tvb.Add(SpecialChars.RecordSeparator); tvb.Add(newEtagSwapBytes); tvb.Add(idPtr); tvb.Add(document.BasePointer, document.Size); tvb.Add((int)flags); tvb.Add(NotDeletedRevisionMarker); tvb.Add(lastModifiedTicks); tvb.Add(context.GetTransactionMarker()); if (flags.Contain(DocumentFlags.Resolved)) { tvb.Add((int)DocumentFlags.Resolved); } else { tvb.Add(0); } tvb.Add(Bits.SwapBytes(lastModifiedTicks)); var isNew = table.Set(tvb); if (isNew == false) { // It might be just an update from replication as we call this twice, both for the doc delete and for deleteRevision. return; } } if (configuration == null) { configuration = GetRevisionsConfiguration(collectionName.Name); } DeleteOldRevisions(context, table, lowerId, collectionName, configuration, nonPersistentFlags, changeVector); } }
private unsafe bool AboutToReadPropertyNameInMetadataUnlikely(IJsonParser reader, JsonParserState state, out bool aboutToReadPropertyName) { aboutToReadPropertyName = true; switch (state.StringSize) { default: // accept this property { return(true); } case -2: // IgnoreArrayProperty { if (state.CurrentTokenType != JsonParserToken.StartArray) { ThrowInvalidArrayType(state, reader); } while (state.CurrentTokenType != JsonParserToken.EndArray) { if (reader.Read() == false) { _state = State.IgnoreArray; aboutToReadPropertyName = false; return(true); } } break; } case -1: // IgnoreProperty { if (reader.Read() == false) { _state = State.IgnoreProperty; aboutToReadPropertyName = false; return(true); } if (state.CurrentTokenType == JsonParserToken.StartArray || state.CurrentTokenType == JsonParserToken.StartObject) { ThrowInvalidMetadataProperty(state, reader); } break; } case 3: // @id if (state.StringBuffer[0] != (byte)'@' || *(short *)(state.StringBuffer + 1) != 25705) { aboutToReadPropertyName = true; return(true); } if (reader.Read() == false) { _state = State.ReadingId; aboutToReadPropertyName = false; return(true); } if (state.CurrentTokenType != JsonParserToken.String) { ThrowExpectedFieldTypeOfString(Constants.Documents.Metadata.Id, state, reader); } Id = CreateLazyStringValueFromParserState(state); break; case 5: // @etag if (state.StringBuffer[0] != (byte)'@' || *(int *)(state.StringBuffer + 1) != 1734440037) { aboutToReadPropertyName = true; return(true); } if (ReadFirstEtagOfLegacyRevision && (NonPersistentFlags & NonPersistentDocumentFlags.LegacyRevision) == NonPersistentDocumentFlags.LegacyRevision) { if (FirstEtagOfLegacyRevision == null) { if (reader.Read() == false) { _state = State.ReadingFirstEtagOfLegacyRevision; aboutToReadPropertyName = false; return(true); } if (state.CurrentTokenType != JsonParserToken.String) { ThrowExpectedFieldTypeOfString("@etag", state, reader); } FirstEtagOfLegacyRevision = LegacyEtag = CreateLazyStringValueFromParserState(state); ChangeVector = ChangeVectorUtils.NewChangeVector("RV", ++LegacyRevisionsCount, new Guid(FirstEtagOfLegacyRevision).ToBase64Unpadded()); break; } ChangeVector = ChangeVectorUtils.NewChangeVector("RV", ++LegacyRevisionsCount, new Guid(FirstEtagOfLegacyRevision).ToBase64Unpadded()); } if (ReadLegacyEtag) { if (reader.Read() == false) { _state = State.ReadingEtag; aboutToReadPropertyName = false; return(true); } if (state.CurrentTokenType != JsonParserToken.String) { ThrowExpectedFieldTypeOfString("@etag", state, reader); } LegacyEtag = CreateLazyStringValueFromParserState(state); break; } goto case -1; case 6: // @flags if (state.StringBuffer[0] != (byte)'@' || *(int *)(state.StringBuffer + 1) != 1734437990 || state.StringBuffer[1 + sizeof(int)] != (byte)'s') { aboutToReadPropertyName = true; return(true); } if (reader.Read() == false) { _state = State.ReadingFlags; aboutToReadPropertyName = false; return(true); } if (state.CurrentTokenType != JsonParserToken.String) { ThrowExpectedFieldTypeOfString(Constants.Documents.Metadata.Flags, state, reader); } Flags = ReadFlags(state); break; case 9: // @counters // always remove the @counters metadata // not doing so might cause us to have counter on the document but not in the storage. // the counters will be updated when we import the counters themselves if (state.StringBuffer[0] != (byte)'@' || *(long *)(state.StringBuffer + 1) != 8318823012450529123) { aboutToReadPropertyName = true; return(true); } if (reader.Read() == false) { _verifyStartArray = true; _state = State.IgnoreArray; aboutToReadPropertyName = false; return(true); } goto case -2; case 12: // @index-score OR @attachments if (state.StringBuffer[0] == (byte)'@') { // @index-score if (*(long *)(state.StringBuffer + 1) == 7166121427196997225 && *(short *)(state.StringBuffer + 1 + sizeof(long)) == 29295 && state.StringBuffer[1 + sizeof(long) + sizeof(short)] == (byte)'e') { goto case -1; } // @attachments if (OperateOnTypes.HasFlag(DatabaseItemType.Attachments) == false && *(long *)(state.StringBuffer + 1) == 7308612546338255969 && *(short *)(state.StringBuffer + 1 + sizeof(long)) == 29806 && state.StringBuffer[1 + sizeof(long) + sizeof(short)] == (byte)'s') { if (reader.Read() == false) { _verifyStartArray = true; _state = State.IgnoreArray; aboutToReadPropertyName = false; return(true); } goto case -2; } } aboutToReadPropertyName = true; return(true); case 13: //Last-Modified if (*(long *)state.StringBuffer != 7237087983830262092 || *(int *)(state.StringBuffer + sizeof(long)) != 1701406313 || state.StringBuffer[12] != (byte)'d') { aboutToReadPropertyName = true; return(true); } goto case -1; case 14: if (state.StringBuffer[0] == (byte)'@') { // @change-vector if (*(long *)(state.StringBuffer + 1) == 8515573965335390307 && *(int *)(state.StringBuffer + 1 + sizeof(long)) == 1869898597 && state.StringBuffer[1 + sizeof(long) + sizeof(int)] == (byte)'r') { if (reader.Read() == false) { _state = State.ReadingChangeVector; aboutToReadPropertyName = false; return(true); } if (state.CurrentTokenType != JsonParserToken.String) { ThrowExpectedFieldTypeOfString(Constants.Documents.Metadata.ChangeVector, state, reader); } ChangeVector = CreateLazyStringValueFromParserState(state); break; } // @last-modified if (*(long *)(state.StringBuffer + 1) == 7237123168202350956 && *(int *)(state.StringBuffer + 1 + sizeof(long)) == 1701406313 && state.StringBuffer[1 + sizeof(long) + sizeof(int)] == (byte)'d') { if (reader.Read() == false) { _state = State.ReadingLastModified; aboutToReadPropertyName = false; return(true); } if (state.CurrentTokenType != JsonParserToken.String) { ThrowExpectedFieldTypeOfString(Constants.Documents.Metadata.LastModified, state, reader); } LastModified = ReadDateTime(state, reader, State.ReadingLastModified); break; } } aboutToReadPropertyName = true; return(true); case 15: //Raven-Read-Only if (*(long *)state.StringBuffer != 7300947898092904786 || *(int *)(state.StringBuffer + sizeof(long)) != 1328374881 || *(short *)(state.StringBuffer + sizeof(long) + sizeof(int)) != 27758 || state.StringBuffer[14] != (byte)'y') { aboutToReadPropertyName = true; return(true); } goto case -1; case 17: //Raven-Entity-Name --> @collection if (*(long *)state.StringBuffer != 7945807069737017682 || *(long *)(state.StringBuffer + sizeof(long)) != 7881666780093245812 || state.StringBuffer[16] != (byte)'e') { aboutToReadPropertyName = true; return(true); } var collection = _metadataCollections; state.StringBuffer = collection.AllocatedMemoryData.Address; state.StringSize = collection.Size; aboutToReadPropertyName = true; return(true); case 19: //Raven-Last-Modified if (*(long *)state.StringBuffer != 7011028672080929106 || *(long *)(state.StringBuffer + sizeof(long)) != 7379539893622240371 || *(short *)(state.StringBuffer + sizeof(long) + sizeof(long)) != 25961 || state.StringBuffer[18] != (byte)'d') { aboutToReadPropertyName = true; return(true); } if (reader.Read() == false) { _state = State.ReadingLegacyLastModified; aboutToReadPropertyName = false; return(true); } if (state.CurrentTokenType != JsonParserToken.String) { ThrowExpectedFieldTypeOfString(LegacyLastModified, state, reader); } LastModified = ReadDateTime(state, reader, State.ReadingLegacyLastModified); break; case 21: //Raven-Expiration-Date if (*(long *)state.StringBuffer != 8666383010116297042 || *(long *)(state.StringBuffer + sizeof(long)) != 7957695015158966640 || *(short *)(state.StringBuffer + sizeof(long) + sizeof(long)) != 17453 || state.StringBuffer[20] != (byte)'e') { aboutToReadPropertyName = true; return(true); } var expires = _metadataExpires; state.StringBuffer = expires.AllocatedMemoryData.Address; state.StringSize = expires.Size; aboutToReadPropertyName = true; return(true); case 23: //Raven-Document-Revision if (*(long *)state.StringBuffer != 8017583188798234962 || *(long *)(state.StringBuffer + sizeof(long)) != 5921517102558967139 || *(int *)(state.StringBuffer + sizeof(long) + sizeof(long)) != 1936291429 || *(short *)(state.StringBuffer + sizeof(long) + sizeof(long) + sizeof(int)) != 28521 || state.StringBuffer[22] != (byte)'n') { aboutToReadPropertyName = true; return(true); } goto case -1; case 24: //Raven-Replication-Source if (*(long *)state.StringBuffer != 7300947898092904786 || *(long *)(state.StringBuffer + sizeof(long)) != 8028075772393122928 || *(long *)(state.StringBuffer + sizeof(long) + sizeof(long)) != 7305808869229538670) { aboutToReadPropertyName = true; return(true); } goto case -1; case 25: //Raven-Replication-Version OR Raven-Replication-History if (*(long *)state.StringBuffer != 7300947898092904786 || *(long *)(state.StringBuffer + sizeof(long)) != 8028075772393122928) { aboutToReadPropertyName = true; return(true); } var value = *(long *)(state.StringBuffer + sizeof(long) + sizeof(long)); var lastByte = state.StringBuffer[24]; if ((value != 8028074745928232302 || lastByte != (byte)'n') && (value != 8245937481775066478 || lastByte != (byte)'y')) { aboutToReadPropertyName = true; return(true); } var isReplicationHistory = lastByte == (byte)'y'; if (reader.Read() == false) { _verifyStartArray = isReplicationHistory; _state = isReplicationHistory ? State.IgnoreArray : State.IgnoreProperty; aboutToReadPropertyName = false; return(true); } // Raven-Replication-History is an array if (isReplicationHistory) { goto case -2; } else if (state.CurrentTokenType == JsonParserToken.StartArray || state.CurrentTokenType == JsonParserToken.StartObject) { ThrowInvalidMetadataProperty(state, reader); } break; case 29: //Non-Authoritative-Information if (*(long *)state.StringBuffer != 7526769800038477646 || *(long *)(state.StringBuffer + sizeof(long)) != 8532478930943832687 || *(long *)(state.StringBuffer + sizeof(long) + sizeof(long)) != 7886488383206796645 || *(int *)(state.StringBuffer + sizeof(long) + sizeof(long) + sizeof(long)) != 1869182049 || state.StringBuffer[28] != (byte)'n') { aboutToReadPropertyName = true; return(true); } goto case -1; case 30: //Raven-Document-Parent-Revision OR Raven-Document-Revision-Status if (*(long *)state.StringBuffer != 8017583188798234962) { aboutToReadPropertyName = true; return(true); } if ((*(long *)(state.StringBuffer + sizeof(long)) != 5777401914483111267 || *(long *)(state.StringBuffer + sizeof(long) + sizeof(long)) != 7300947924012593761 || *(int *)(state.StringBuffer + sizeof(long) + sizeof(long) + sizeof(long)) != 1769171318 || *(short *)(state.StringBuffer + sizeof(long) + sizeof(long) + sizeof(long) + sizeof(int)) != 28271) && (*(long *)(state.StringBuffer + sizeof(long)) != 5921517102558967139 || *(long *)(state.StringBuffer + sizeof(long) + sizeof(long)) != 3273676477843469925 || *(int *)(state.StringBuffer + sizeof(long) + sizeof(long) + sizeof(long)) != 1952543827 || *(short *)(state.StringBuffer + sizeof(long) + sizeof(long) + sizeof(long) + sizeof(int)) != 29557)) { aboutToReadPropertyName = true; return(true); } var isRevisionStatusProperty = state.StringBuffer[29] == 's'; if (reader.Read() == false) { _state = isRevisionStatusProperty ? State.IgnoreRevisionStatusProperty : State.IgnoreProperty; aboutToReadPropertyName = false; return(true); } if (state.CurrentTokenType == JsonParserToken.StartArray || state.CurrentTokenType == JsonParserToken.StartObject) { ThrowInvalidMetadataProperty(state, reader); } if (isRevisionStatusProperty) { switch (CreateLazyStringValueFromParserState(state)) { case LegacyHasRevisionsDocumentState: NonPersistentFlags |= NonPersistentDocumentFlags.LegacyHasRevisions; break; case LegacyRevisionState: NonPersistentFlags |= NonPersistentDocumentFlags.LegacyRevision; break; } } break; case 32: //Raven-Replication-Merged-History if (*(long *)state.StringBuffer != 7300947898092904786 || *(long *)(state.StringBuffer + sizeof(long)) != 8028075772393122928 || *(long *)(state.StringBuffer + sizeof(long) + sizeof(long)) != 7234302117464059246 || *(long *)(state.StringBuffer + sizeof(long) + sizeof(long) + sizeof(long)) != 8751179571877464109) { aboutToReadPropertyName = true; return(true); } goto case -1; } return(false); }
private bool AboutToReadWithStateUnlikely(IJsonParser reader, JsonParserState state) { switch (_state) { case State.None: break; case State.IgnoreProperty: if (reader.Read() == false) { return(false); } if (state.CurrentTokenType == JsonParserToken.StartArray || state.CurrentTokenType == JsonParserToken.StartObject) { ThrowInvalidMetadataProperty(state, reader); } break; case State.IgnoreArray: if (_verifyStartArray) { if (reader.Read() == false) { return(false); } _verifyStartArray = false; if (state.CurrentTokenType != JsonParserToken.StartArray) { ThrowInvalidReplicationHistoryType(state, reader); } } while (state.CurrentTokenType != JsonParserToken.EndArray) { if (reader.Read() == false) { return(false); } } break; case State.IgnoreRevisionStatusProperty: if (reader.Read() == false) { return(false); } if (state.CurrentTokenType != JsonParserToken.String && state.CurrentTokenType != JsonParserToken.Integer) { ThrowInvalidEtagType(state, reader); } switch (CreateLazyStringValueFromParserState(state)) { case LegacyHasRevisionsDocumentState: NonPersistentFlags |= NonPersistentDocumentFlags.LegacyHasRevisions; break; case LegacyRevisionState: NonPersistentFlags |= NonPersistentDocumentFlags.LegacyRevision; break; } break; case State.ReadingId: if (reader.Read() == false) { return(false); } if (state.CurrentTokenType != JsonParserToken.String) { ThrowExpectedFieldTypeOfString(Constants.Documents.Metadata.Id, state, reader); } Id = CreateLazyStringValueFromParserState(state); break; case State.ReadingFlags: if (reader.Read() == false) { return(false); } if (state.CurrentTokenType != JsonParserToken.String) { ThrowExpectedFieldTypeOfString(Constants.Documents.Metadata.Flags, state, reader); } Flags = ReadFlags(state); break; case State.ReadingLastModified: case State.ReadingLegacyLastModified: if (reader.Read() == false) { return(false); } if (state.CurrentTokenType != JsonParserToken.String) { ThrowExpectedFieldTypeOfString(Constants.Documents.Metadata.LastModified, state, reader); } LastModified = ReadDateTime(state, reader, _state); break; case State.ReadingChangeVector: if (reader.Read() == false) { return(false); } if (state.CurrentTokenType != JsonParserToken.String) { ThrowExpectedFieldTypeOfString(Constants.Documents.Metadata.ChangeVector, state, reader); } ChangeVector = CreateLazyStringValueFromParserState(state); break; case State.ReadingFirstEtagOfLegacyRevision: if (reader.Read() == false) { return(false); } if (state.CurrentTokenType != JsonParserToken.String) { ThrowExpectedFieldTypeOfString("@etag", state, reader); } FirstEtagOfLegacyRevision = LegacyEtag = CreateLazyStringValueFromParserState(state); ChangeVector = ChangeVectorUtils.NewChangeVector("RV", ++LegacyRevisionsCount, new Guid(FirstEtagOfLegacyRevision).ToBase64Unpadded()); break; case State.ReadingEtag: if (reader.Read() == false) { return(false); } if (state.CurrentTokenType != JsonParserToken.String) { ThrowExpectedFieldTypeOfString("@etag", state, reader); } LegacyEtag = CreateLazyStringValueFromParserState(state); break; } return(true); }
private void DeleteOldRevisions(DocumentsOperationContext context, Table table, Slice prefixSlice, CollectionName collectionName, RevisionsCollectionConfiguration configuration, long revisionsCount, NonPersistentDocumentFlags nonPersistentFlags, string changeVector, long lastModifiedTicks) { if ((nonPersistentFlags & NonPersistentDocumentFlags.FromSmuggler) == NonPersistentDocumentFlags.FromSmuggler) { return; } if (configuration.MinimumRevisionsToKeep.HasValue == false && configuration.MinimumRevisionAgeToKeep.HasValue == false) { return; } var numberOfRevisionsToDelete = revisionsCount - configuration.MinimumRevisionsToKeep ?? 0; if (numberOfRevisionsToDelete <= 0) { return; } var deletedRevisionsCount = DeleteRevisions(context, table, prefixSlice, collectionName, numberOfRevisionsToDelete, configuration.MinimumRevisionAgeToKeep, changeVector, lastModifiedTicks); Debug.Assert(numberOfRevisionsToDelete >= deletedRevisionsCount); IncrementCountOfRevisions(context, prefixSlice, -deletedRevisionsCount); }
private void Delete(DocumentsOperationContext context, Slice lowerId, Slice idSlice, string id, CollectionName collectionName, BlittableJsonReaderObject deleteRevisionDocument, string changeVector, long lastModifiedTicks, NonPersistentDocumentFlags nonPersistentFlags, DocumentFlags flags) { Debug.Assert(changeVector != null, "Change vector must be set"); if (flags.Contain(DocumentFlags.HasAttachments)) { flags &= ~DocumentFlags.HasAttachments; } var configuration = GetRevisionsConfiguration(collectionName.Name, flags); if (configuration.Disabled) { return; } var table = EnsureRevisionTableCreated(context.Transaction.InnerTransaction, collectionName); if (configuration.PurgeOnDelete) { using (GetKeyPrefix(context, lowerId, out Slice prefixSlice)) { DeleteRevisions(context, table, prefixSlice, collectionName, long.MaxValue, null, changeVector, lastModifiedTicks); DeleteCountOfRevisions(context, prefixSlice); } return; } var fromReplication = (nonPersistentFlags & NonPersistentDocumentFlags.FromReplication) == NonPersistentDocumentFlags.FromReplication; if (fromReplication) { void DeleteFromRevisionIfChangeVectorIsGreater() { TableValueReader tvr; try { var hasDoc = _documentsStorage.GetTableValueReaderForDocument(context, lowerId, throwOnConflict: true, tvr: out tvr); if (hasDoc == false) { return; } } catch (DocumentConflictException) { // Do not modify the document. return; } var docChangeVector = TableValueToChangeVector(context, (int)DocumentsTable.ChangeVector, ref tvr); if (ChangeVectorUtils.GetConflictStatus(changeVector, docChangeVector) == ConflictStatus.Update) { _documentsStorage.Delete(context, lowerId, id, null, lastModifiedTicks, changeVector, collectionName, nonPersistentFlags | NonPersistentDocumentFlags.FromRevision); } } DeleteFromRevisionIfChangeVectorIsGreater(); } var newEtag = _database.DocumentsStorage.GenerateNextEtag(); var newEtagSwapBytes = Bits.SwapBytes(newEtag); using (table.Allocate(out TableValueBuilder tvb)) using (Slice.From(context.Allocator, changeVector, out var cv)) { tvb.Add(cv.Content.Ptr, cv.Size); tvb.Add(lowerId); tvb.Add(SpecialChars.RecordSeparator); tvb.Add(newEtagSwapBytes); tvb.Add(idSlice); tvb.Add(deleteRevisionDocument.BasePointer, deleteRevisionDocument.Size); tvb.Add((int)(DocumentFlags.DeleteRevision | flags)); tvb.Add(newEtagSwapBytes); tvb.Add(lastModifiedTicks); tvb.Add(context.GetTransactionMarker()); if (flags.Contain(DocumentFlags.Resolved)) { tvb.Add((int)DocumentFlags.Resolved); } else { tvb.Add(0); } tvb.Add(Bits.SwapBytes(lastModifiedTicks)); var isNew = table.Set(tvb); if (isNew == false) { // It might be just an update from replication as we call this twice, both for the doc delete and for deleteRevision. return; } } DeleteOldRevisions(context, table, lowerId, collectionName, configuration, nonPersistentFlags, changeVector, lastModifiedTicks); }
public static bool Contain(this NonPersistentDocumentFlags current, NonPersistentDocumentFlags flag) { return((current & flag) == flag); }
private bool AboutToReadWithStateUnlikely(IJsonParser reader, JsonParserState state) { switch (_state) { case State.None: break; case State.IgnoreProperty: if (reader.Read() == false) { return(false); } if (state.CurrentTokenType == JsonParserToken.StartArray || state.CurrentTokenType == JsonParserToken.StartObject) { ThrowInvalidMetadataProperty(state); } break; case State.IgnoreArray: if (_verifyStartArray) { if (reader.Read() == false) { return(false); } _verifyStartArray = false; if (state.CurrentTokenType != JsonParserToken.StartArray) { ThrowInvalidReplicationHistoryType(state); } } while (state.CurrentTokenType != JsonParserToken.EndArray) { if (reader.Read() == false) { return(false); } } break; case State.IgnoreRevisionStatusProperty: if (reader.Read() == false) { return(false); } if (state.CurrentTokenType != JsonParserToken.String && state.CurrentTokenType != JsonParserToken.Integer) { ThrowInvalidEtagType(state); } switch (CreateLazyStringValueFromParserState(state)) { case LegacyHasRevisionsDocumentState: NonPersistentFlags |= NonPersistentDocumentFlags.LegacyHasRevisions; break; case LegacyRevisionState: NonPersistentFlags |= NonPersistentDocumentFlags.LegacyRevision; break; } break; case State.ReadingId: if (reader.Read() == false) { return(false); } if (state.CurrentTokenType != JsonParserToken.String) { ThrowExpectedFieldTypeOfString(Constants.Documents.Metadata.Id, state); } Id = CreateLazyStringValueFromParserState(state); break; case State.ReadingFlags: if (reader.Read() == false) { return(false); } if (state.CurrentTokenType != JsonParserToken.String) { ThrowExpectedFieldTypeOfString(Constants.Documents.Metadata.Flags, state); } Flags = ReadFlags(state); break; case State.ReadingChangeVector: if (reader.Read() == false) { return(false); } if (state.CurrentTokenType != JsonParserToken.String) { ThrowExpectedFieldTypeOfString(Constants.Documents.Metadata.ChangeVector, state); } ChangeVector = CreateLazyStringValueFromParserState(state); break; } return(true); }
private unsafe bool AboutToReadPropertyNameInMetadataUnlikely(IJsonParser reader, JsonParserState state, out bool aboutToReadPropertyName) { aboutToReadPropertyName = true; switch (state.StringSize) { default: // accept this property { return(true); } case -1: // IgnoreProperty { if (reader.Read() == false) { _state = State.IgnoreProperty; { aboutToReadPropertyName = false; return(true); } } if (state.CurrentTokenType == JsonParserToken.StartArray || state.CurrentTokenType == JsonParserToken.StartObject) { ThrowInvalidMetadataProperty(state); } break; } case 3: // @id if (state.StringBuffer[0] != (byte)'@' || *(short *)(state.StringBuffer + 1) != 25705) { aboutToReadPropertyName = true; return(true); } if (reader.Read() == false) { _state = State.ReadingId; { aboutToReadPropertyName = false; return(true); } } if (state.CurrentTokenType != JsonParserToken.String) { ThrowExpectedFieldTypeOfString(Constants.Documents.Metadata.Id, state); } Id = CreateLazyStringValueFromParserState(state); break; case 6: // @flags if (state.StringBuffer[0] != (byte)'@' || *(int *)(state.StringBuffer + 1) != 1734437990 || state.StringBuffer[1 + sizeof(int)] != (byte)'s') { aboutToReadPropertyName = true; return(true); } if (reader.Read() == false) { _state = State.ReadingFlags; { aboutToReadPropertyName = false; return(true); } } if (state.CurrentTokenType != JsonParserToken.String) { ThrowExpectedFieldTypeOfString(Constants.Documents.Metadata.Flags, state); } Flags = ReadFlags(state); break; case 12: // @index-score if (state.StringBuffer[0] != (byte)'@' || *(long *)(state.StringBuffer + 1) != 7166121427196997225 || *(short *)(state.StringBuffer + 1 + sizeof(long)) != 29295 || state.StringBuffer[1 + sizeof(long) + sizeof(short)] != (byte)'e') { aboutToReadPropertyName = true; return(true); } goto case -1; case 13: //Last-Modified if (*(long *)state.StringBuffer != 7237087983830262092 || *(int *)(state.StringBuffer + sizeof(long)) != 1701406313 || state.StringBuffer[12] != (byte)'d') { aboutToReadPropertyName = true; return(true); } goto case -1; case 14: if (state.StringBuffer[0] == (byte)'@') { // @change-vector if (*(long *)(state.StringBuffer + 1) == 8515573965335390307 && *(int *)(state.StringBuffer + 1 + sizeof(long)) == 1869898597 && state.StringBuffer[1 + sizeof(long) + sizeof(int)] == (byte)'r') { if (reader.Read() == false) { _state = State.ReadingChangeVector; { aboutToReadPropertyName = false; return(true); } } if (state.CurrentTokenType != JsonParserToken.String) { ThrowExpectedFieldTypeOfString(Constants.Documents.Metadata.ChangeVector, state); } ChangeVector = CreateLazyStringValueFromParserState(state); break; } // @last-modified if (*(long *)(state.StringBuffer + 1) == 7237123168202350956 && *(int *)(state.StringBuffer + 1 + sizeof(long)) == 1701406313 && state.StringBuffer[1 + sizeof(long) + sizeof(int)] == (byte)'d') { goto case -1; } } { aboutToReadPropertyName = true; return(true); } case 15: //Raven-Read-Only if (*(long *)state.StringBuffer != 7300947898092904786 || *(int *)(state.StringBuffer + sizeof(long)) != 1328374881 || *(short *)(state.StringBuffer + sizeof(long) + sizeof(int)) != 27758 || state.StringBuffer[14] != (byte)'y') { aboutToReadPropertyName = true; return(true); } goto case -1; case 17: //Raven-Entity-Name --> @collection if (*(long *)state.StringBuffer != 7945807069737017682 || *(long *)(state.StringBuffer + sizeof(long)) != 7881666780093245812 || state.StringBuffer[16] != (byte)'e') { aboutToReadPropertyName = true; return(true); } var collection = _metadataCollections; state.StringBuffer = collection.AllocatedMemoryData.Address; state.StringSize = collection.Size; { aboutToReadPropertyName = true; return(true); } case 19: //Raven-Last-Modified if (*(long *)state.StringBuffer != 7011028672080929106 || *(long *)(state.StringBuffer + sizeof(long)) != 7379539893622240371 || *(short *)(state.StringBuffer + sizeof(long) + sizeof(long)) != 25961 || state.StringBuffer[18] != (byte)'d') { aboutToReadPropertyName = true; return(true); } goto case -1; case 21: //Raven-Expiration-Date if (*(long *)state.StringBuffer != 8666383010116297042 || *(long *)(state.StringBuffer + sizeof(long)) != 7957695015158966640 || *(short *)(state.StringBuffer + sizeof(long) + sizeof(long)) != 17453 || state.StringBuffer[20] != (byte)'e') { aboutToReadPropertyName = true; return(true); } var expires = _metadataExpires; state.StringBuffer = expires.AllocatedMemoryData.Address; state.StringSize = expires.Size; { aboutToReadPropertyName = true; return(true); } case 23: //Raven-Document-Revision if (*(long *)state.StringBuffer != 8017583188798234962 || *(long *)(state.StringBuffer + sizeof(long)) != 5921517102558967139 || *(int *)(state.StringBuffer + sizeof(long) + sizeof(long)) != 1936291429 || *(short *)(state.StringBuffer + sizeof(long) + sizeof(long) + sizeof(int)) != 28521 || state.StringBuffer[22] != (byte)'n') { aboutToReadPropertyName = true; return(true); } goto case -1; case 24: //Raven-Replication-Source if (*(long *)state.StringBuffer != 7300947898092904786 || *(long *)(state.StringBuffer + sizeof(long)) != 8028075772393122928 || *(long *)(state.StringBuffer + sizeof(long) + sizeof(long)) != 7305808869229538670) { aboutToReadPropertyName = true; return(true); } goto case -1; case 25: //Raven-Replication-Version OR Raven-Replication-History if (*(long *)state.StringBuffer != 7300947898092904786 || *(long *)(state.StringBuffer + sizeof(long)) != 8028075772393122928) { aboutToReadPropertyName = true; return(true); } var value = *(long *)(state.StringBuffer + sizeof(long) + sizeof(long)); var lastByte = state.StringBuffer[24]; if ((value != 8028074745928232302 || lastByte != (byte)'n') && (value != 8245937481775066478 || lastByte != (byte)'y')) { aboutToReadPropertyName = true; return(true); } var isReplicationHistory = lastByte == (byte)'y'; if (reader.Read() == false) { _verifyStartArray = isReplicationHistory; _state = isReplicationHistory ? State.IgnoreArray : State.IgnoreProperty; { aboutToReadPropertyName = false; return(true); } } // Raven-Replication-History is an array if (isReplicationHistory) { if (state.CurrentTokenType != JsonParserToken.StartArray) { ThrowInvalidReplicationHistoryType(state); } do { if (reader.Read() == false) { _state = State.IgnoreArray; { aboutToReadPropertyName = false; return(true); } } } while (state.CurrentTokenType != JsonParserToken.EndArray); } else if (state.CurrentTokenType == JsonParserToken.StartArray || state.CurrentTokenType == JsonParserToken.StartObject) { ThrowInvalidMetadataProperty(state); } break; case 29: //Non-Authoritative-Information if (*(long *)state.StringBuffer != 7526769800038477646 || *(long *)(state.StringBuffer + sizeof(long)) != 8532478930943832687 || *(long *)(state.StringBuffer + sizeof(long) + sizeof(long)) != 7886488383206796645 || *(int *)(state.StringBuffer + sizeof(long) + sizeof(long) + sizeof(long)) != 1869182049 || state.StringBuffer[28] != (byte)'n') { aboutToReadPropertyName = true; return(true); } goto case -1; case 30: //Raven-Document-Parent-Revision OR Raven-Document-Revision-Status if (*(long *)state.StringBuffer != 8017583188798234962) { aboutToReadPropertyName = true; return(true); } if ((*(long *)(state.StringBuffer + sizeof(long)) != 5777401914483111267 || *(long *)(state.StringBuffer + sizeof(long) + sizeof(long)) != 7300947924012593761 || *(int *)(state.StringBuffer + sizeof(long) + sizeof(long) + sizeof(long)) != 1769171318 || *(short *)(state.StringBuffer + sizeof(long) + sizeof(long) + sizeof(long) + sizeof(int)) != 28271) && (*(long *)(state.StringBuffer + sizeof(long)) != 5921517102558967139 || *(long *)(state.StringBuffer + sizeof(long) + sizeof(long)) != 3273676477843469925 || *(int *)(state.StringBuffer + sizeof(long) + sizeof(long) + sizeof(long)) != 1952543827 || *(short *)(state.StringBuffer + sizeof(long) + sizeof(long) + sizeof(long) + sizeof(int)) != 29557)) { aboutToReadPropertyName = true; return(true); } var isRevisionStatusProperty = state.StringBuffer[29] == 's'; if (reader.Read() == false) { _state = isRevisionStatusProperty ? State.IgnoreRevisionStatusProperty : State.IgnoreProperty; { aboutToReadPropertyName = false; return(true); } } if (state.CurrentTokenType == JsonParserToken.StartArray || state.CurrentTokenType == JsonParserToken.StartObject) { ThrowInvalidMetadataProperty(state); } if (isRevisionStatusProperty) { switch (CreateLazyStringValueFromParserState(state)) { case LegacyHasRevisionsDocumentState: NonPersistentFlags |= NonPersistentDocumentFlags.LegacyHasRevisions; break; case LegacyRevisionState: NonPersistentFlags |= NonPersistentDocumentFlags.LegacyRevision; break; } } break; case 32: //Raven-Replication-Merged-History if (*(long *)state.StringBuffer != 7300947898092904786 || *(long *)(state.StringBuffer + sizeof(long)) != 8028075772393122928 || *(long *)(state.StringBuffer + sizeof(long) + sizeof(long)) != 7234302117464059246 || *(long *)(state.StringBuffer + sizeof(long) + sizeof(long) + sizeof(long)) != 8751179571877464109) { aboutToReadPropertyName = true; return(true); } goto case -1; } return(false); }
private bool ShouldRecreateAttachments(DocumentsOperationContext context, Slice lowerId, BlittableJsonReaderObject oldDoc, BlittableJsonReaderObject document, ref DocumentFlags flags, NonPersistentDocumentFlags nonPersistentFlags) { if ((nonPersistentFlags & NonPersistentDocumentFlags.ResolveAttachmentsConflict) == NonPersistentDocumentFlags.ResolveAttachmentsConflict) { document.TryGet(Constants.Documents.Metadata.Key, out BlittableJsonReaderObject metadata); RecreateAttachments(context, lowerId, document, metadata, ref flags); return(true); } if ((flags & DocumentFlags.HasAttachments) == DocumentFlags.HasAttachments && (nonPersistentFlags & NonPersistentDocumentFlags.ByAttachmentUpdate) != NonPersistentDocumentFlags.ByAttachmentUpdate && (nonPersistentFlags & NonPersistentDocumentFlags.FromReplication) != NonPersistentDocumentFlags.FromReplication) { if (oldDoc != null && oldDoc.TryGet(Constants.Documents.Metadata.Key, out BlittableJsonReaderObject oldMetadata) && oldMetadata.TryGet(Constants.Documents.Metadata.Attachments, out BlittableJsonReaderArray oldAttachments)) { // Make sure the user did not changed the value of @attachments in the @metadata // In most cases it won't be changed so we can use this value // instead of recreating the document's blitable from scratch if (document.TryGet(Constants.Documents.Metadata.Key, out BlittableJsonReaderObject metadata) == false || metadata.TryGet(Constants.Documents.Metadata.Attachments, out BlittableJsonReaderArray attachments) == false || attachments.Equals(oldAttachments) == false) { RecreateAttachments(context, lowerId, document, metadata, ref flags); return(true); } } } return(false); }
public PutOperationResults PutDocument(DocumentsOperationContext context, string id, string expectedChangeVector, BlittableJsonReaderObject document, long?lastModifiedTicks = null, string changeVector = null, DocumentFlags flags = DocumentFlags.None, NonPersistentDocumentFlags nonPersistentFlags = NonPersistentDocumentFlags.None) { if (context.Transaction == null) { ThrowRequiresTransaction(); return(default(PutOperationResults)); // never hit } #if DEBUG var documentDebugHash = document.DebugHash; document.BlittableValidation(); BlittableJsonReaderObject.AssertNoModifications(document, id, assertChildren: true); AssertMetadataWasFiltered(document); #endif var newEtag = _documentsStorage.GenerateNextEtag(); var modifiedTicks = _documentsStorage.GetOrCreateLastModifiedTicks(lastModifiedTicks); id = BuildDocumentId(id, newEtag, out bool knownNewId); using (DocumentIdWorker.GetLowerIdSliceAndStorageKey(context, id, out Slice lowerId, out Slice idPtr)) { var collectionName = _documentsStorage.ExtractCollectionName(context, document); var table = context.Transaction.InnerTransaction.OpenTable(DocsSchema, collectionName.GetTableName(CollectionTableType.Documents)); var oldValue = default(TableValueReader); if (knownNewId == false) { // delete a tombstone if it exists, if it known that it is a new ID, no need, so we can skip it DeleteTombstoneIfNeeded(context, collectionName, lowerId.Content.Ptr, lowerId.Size); table.ReadByKey(lowerId, out oldValue); } BlittableJsonReaderObject oldDoc = null; if (oldValue.Pointer == null) { // expectedChangeVector being null means we don't care, // and empty means that it must be new if (string.IsNullOrEmpty(expectedChangeVector) == false) { ThrowConcurrentExceptionOnMissingDoc(id, expectedChangeVector); } } else { // expectedChangeVector has special meaning here // null - means, don't care, don't check // "" / empty - means, must be new // anything else - must match exactly if (expectedChangeVector != null) { var oldChangeVector = TableValueToChangeVector(context, (int)DocumentsTable.ChangeVector, ref oldValue); if (string.Compare(expectedChangeVector, oldChangeVector, StringComparison.Ordinal) != 0) { ThrowConcurrentException(id, expectedChangeVector, oldChangeVector); } } oldDoc = new BlittableJsonReaderObject(oldValue.Read((int)DocumentsTable.Data, out int oldSize), oldSize, context); var oldCollectionName = _documentsStorage.ExtractCollectionName(context, oldDoc); if (oldCollectionName != collectionName) { ThrowInvalidCollectionNameChange(id, oldCollectionName, collectionName); } var oldFlags = TableValueToFlags((int)DocumentsTable.Flags, ref oldValue); if ((nonPersistentFlags & NonPersistentDocumentFlags.ByAttachmentUpdate) != NonPersistentDocumentFlags.ByAttachmentUpdate && (nonPersistentFlags & NonPersistentDocumentFlags.FromReplication) != NonPersistentDocumentFlags.FromReplication) { if ((oldFlags & DocumentFlags.HasAttachments) == DocumentFlags.HasAttachments) { flags |= DocumentFlags.HasAttachments; } } } var result = BuildChangeVectorAndResolveConflicts(context, id, lowerId, newEtag, document, changeVector, expectedChangeVector, flags, oldValue); if (string.IsNullOrEmpty(result.ChangeVector)) { ChangeVectorUtils.ThrowConflictingEtag(id, changeVector, newEtag, _documentsStorage.Environment.Base64Id, _documentDatabase.ServerStore.NodeTag); } changeVector = result.ChangeVector; nonPersistentFlags |= result.NonPersistentFlags; if (nonPersistentFlags.Contain(NonPersistentDocumentFlags.Resolved)) { flags |= DocumentFlags.Resolved; } if (collectionName.IsHiLo == false && (flags & DocumentFlags.Artificial) != DocumentFlags.Artificial) { if (ShouldRecreateAttachments(context, lowerId, oldDoc, document, ref flags, nonPersistentFlags)) { #if DEBUG if (document.DebugHash != documentDebugHash) { throw new InvalidDataException("The incoming document " + id + " has changed _during_ the put process, " + "this is likely because you are trying to save a document that is already stored and was moved"); } #endif document = context.ReadObject(document, id, BlittableJsonDocumentBuilder.UsageMode.ToDisk); #if DEBUG documentDebugHash = document.DebugHash; document.BlittableValidation(); BlittableJsonReaderObject.AssertNoModifications(document, id, assertChildren: true); AssertMetadataWasFiltered(document); AttachmentsStorage.AssertAttachments(document, flags); #endif } if (nonPersistentFlags.Contain(NonPersistentDocumentFlags.FromReplication) == false && (flags.Contain(DocumentFlags.Resolved) || _documentDatabase.DocumentsStorage.RevisionsStorage.Configuration != null )) { var shouldVersion = _documentDatabase.DocumentsStorage.RevisionsStorage.ShouldVersionDocument(collectionName, nonPersistentFlags, oldDoc, document, ref flags, out RevisionsCollectionConfiguration configuration); if (shouldVersion) { _documentDatabase.DocumentsStorage.RevisionsStorage.Put(context, id, document, flags, nonPersistentFlags, changeVector, modifiedTicks, configuration, collectionName); } } } using (Slice.From(context.Allocator, changeVector, out var cv)) using (table.Allocate(out TableValueBuilder tvb)) { tvb.Add(lowerId); tvb.Add(Bits.SwapBytes(newEtag)); tvb.Add(idPtr); tvb.Add(document.BasePointer, document.Size); tvb.Add(cv.Content.Ptr, cv.Size); tvb.Add(modifiedTicks); tvb.Add((int)flags); tvb.Add(context.GetTransactionMarker()); if (oldValue.Pointer == null) { table.Insert(tvb); } else { table.Update(oldValue.Id, tvb); } } if (collectionName.IsHiLo == false) { _documentsStorage.ExpirationStorage.Put(context, lowerId, document); } context.LastDatabaseChangeVector = changeVector; _documentDatabase.Metrics.Docs.PutsPerSec.MarkSingleThreaded(1); _documentDatabase.Metrics.Docs.BytesPutsPerSec.MarkSingleThreaded(document.Size); context.Transaction.AddAfterCommitNotification(new DocumentChange { ChangeVector = changeVector, CollectionName = collectionName.Name, Id = id, Type = DocumentChangeTypes.Put, }); #if DEBUG if (document.DebugHash != documentDebugHash) { throw new InvalidDataException("The incoming document " + id + " has changed _during_ the put process, " + "this is likely because you are trying to save a document that is already stored and was moved"); } document.BlittableValidation(); BlittableJsonReaderObject.AssertNoModifications(document, id, assertChildren: true); AssertMetadataWasFiltered(document); AttachmentsStorage.AssertAttachments(document, flags); #endif return(new PutOperationResults { Etag = newEtag, Id = id, Collection = collectionName, ChangeVector = changeVector, Flags = flags, LastModified = new DateTime(modifiedTicks) }); } }