public bool TryResolveIdenticalDocument(DocumentsOperationContext context, string id, BlittableJsonReaderObject incomingDoc, long lastModifiedTicks, string incomingChangeVector) { var existing = _database.DocumentsStorage.GetDocumentOrTombstone(context, id, throwOnConflict: false); var existingDoc = existing.Document; var existingTombstone = existing.Tombstone; if (existingDoc != null) { var compareResult = DocumentCompare.IsEqualTo(existingDoc.Data, incomingDoc, DocumentCompare.DocumentCompareOptions.MergeMetadata); if (compareResult == DocumentCompareResult.NotEqual) { return(false); } // no real conflict here, both documents have identical content so we only merge the change vector without increasing the local etag to prevent ping-pong replication var mergedChangeVector = ChangeVectorUtils.MergeVectors(incomingChangeVector, existingDoc.ChangeVector); var nonPersistentFlags = NonPersistentDocumentFlags.FromResolver; nonPersistentFlags |= compareResult.HasFlag(DocumentCompareResult.AttachmentsNotEqual) ? NonPersistentDocumentFlags.ResolveAttachmentsConflict : NonPersistentDocumentFlags.None; if (compareResult.HasFlag(DocumentCompareResult.CountersNotEqual)) { nonPersistentFlags |= NonPersistentDocumentFlags.ResolveCountersConflict; } if (compareResult.HasFlag(DocumentCompareResult.TimeSeriesNotEqual)) { nonPersistentFlags |= NonPersistentDocumentFlags.ResolveTimeSeriesConflict; } _database.DocumentsStorage.Put(context, id, null, incomingDoc, lastModifiedTicks, mergedChangeVector, nonPersistentFlags: nonPersistentFlags); return(true); } if (existingTombstone != null && incomingDoc == null) { // Conflict between two tombstones resolves to the local tombstone existingTombstone.ChangeVector = ChangeVectorUtils.MergeVectors(incomingChangeVector, existingTombstone.ChangeVector); using (Slice.External(context.Allocator, existingTombstone.LowerId, out Slice lowerId)) { _database.DocumentsStorage.ConflictsStorage.DeleteConflicts(context, lowerId, null, existingTombstone.ChangeVector); } return(true); } return(false); }
public bool ShouldVersionDocument(CollectionName collectionName, NonPersistentDocumentFlags nonPersistentFlags, BlittableJsonReaderObject existingDocument, BlittableJsonReaderObject document, ref DocumentFlags documentFlags, out RevisionsCollectionConfiguration configuration) { configuration = GetRevisionsConfiguration(collectionName.Name); if (configuration.Disabled) { return(false); } try { if ((nonPersistentFlags & NonPersistentDocumentFlags.FromSmuggler) != NonPersistentDocumentFlags.FromSmuggler) { return(true); } if (existingDocument == null) { if ((nonPersistentFlags & NonPersistentDocumentFlags.SkipRevisionCreation) == NonPersistentDocumentFlags.SkipRevisionCreation) { // Smuggler is configured to avoid creating new revisions during import return(false); } // we are not going to create a revision if it's an import from v3 // (since this import is going to import revisions as well) return((nonPersistentFlags & NonPersistentDocumentFlags.LegacyHasRevisions) != NonPersistentDocumentFlags.LegacyHasRevisions); } // compare the contents of the existing and the new document if (DocumentCompare.IsEqualTo(existingDocument, document, false) != DocumentCompareResult.NotEqual) { // no need to create a new revision, both documents have identical content return(false); } return(true); } finally { documentFlags |= DocumentFlags.HasRevisions; } }
public bool TryResolveIdenticalDocument(DocumentsOperationContext context, string id, BlittableJsonReaderObject incomingDoc, long lastModifiedTicks, string incomingChangeVector) { var existing = _database.DocumentsStorage.GetDocumentOrTombstone(context, id, throwOnConflict: false); var existingDoc = existing.Document; var existingTombstone = existing.Tombstone; if (existingDoc != null) { var compareResult = DocumentCompare.IsEqualTo(existingDoc.Data, incomingDoc, true); if (compareResult == DocumentCompareResult.NotEqual) { return(false); } // no real conflict here, both documents have identical content var mergedChangeVector = ChangeVectorUtils.MergeVectors(incomingChangeVector, existingDoc.ChangeVector); var nonPersistentFlags = (compareResult & DocumentCompareResult.ShouldRecreateDocument) == DocumentCompareResult.ShouldRecreateDocument ? NonPersistentDocumentFlags.ResolveAttachmentsConflict : NonPersistentDocumentFlags.None; _database.DocumentsStorage.Put(context, id, null, incomingDoc, lastModifiedTicks, mergedChangeVector, nonPersistentFlags: nonPersistentFlags); return(true); } if (existingTombstone != null && incomingDoc == null) { // Conflict between two tombstones resolves to the local tombstone existingTombstone.ChangeVector = ChangeVectorUtils.MergeVectors(incomingChangeVector, existingTombstone.ChangeVector); using (Slice.External(context.Allocator, existingTombstone.LowerId, out Slice lowerId)) { _database.DocumentsStorage.ConflictsStorage.DeleteConflicts(context, lowerId, null, existingTombstone.ChangeVector); } return(true); } return(false); }
protected override long ExecuteCmd(DocumentsOperationContext context) { for (int i = 0; i < NumberOfCommands; i++) { var cmd = Commands[i]; Debug.Assert(cmd.Type == CommandType.PUT || cmd.Type == CommandType.Counters || cmd.Type == CommandType.TimeSeries || cmd.Type == CommandType.TimeSeriesBulkInsert || cmd.Type == CommandType.AttachmentPUT); switch (cmd.Type) { case CommandType.PUT: try { if (SkipOverwriteIfUnchanged) { var existingDoc = Database.DocumentsStorage.Get(context, cmd.Id, DocumentFields.Data, throwOnConflict: false); if (existingDoc != null) { var compareResult = DocumentCompare.IsEqualTo(existingDoc.Data, cmd.Document, DocumentCompare.DocumentCompareOptions.MergeMetadata); if (compareResult.HasFlag(DocumentCompareResult.Equal)) { Debug.Assert(BitOperations.PopCount((ulong)compareResult) == 1 || compareResult.HasFlag(DocumentCompareResult.AttachmentsNotEqual) || compareResult.HasFlag(DocumentCompareResult.CountersNotEqual) || compareResult.HasFlag(DocumentCompareResult.TimeSeriesNotEqual)); continue; } } } Database.DocumentsStorage.Put(context, cmd.Id, null, cmd.Document); } catch (VoronConcurrencyErrorException) { // RavenDB-10581 - If we have a concurrency error on "doc-id/" // this means that we have existing values under the current etag // we'll generate a new (random) id for them. // The TransactionMerger will re-run us when we ask it to as a // separate transaction for (; i < NumberOfCommands; i++) { cmd = Commands[i]; if (cmd.Type != CommandType.PUT) { continue; } if (cmd.Id?.EndsWith(Database.IdentityPartsSeparator) == true) { cmd.Id = MergedPutCommand.GenerateNonConflictingId(Database, cmd.Id); RetryOnError = true; } } throw; } break; case CommandType.Counters: { var collection = CountersHandler.ExecuteCounterBatchCommand.GetDocumentCollection(cmd.Id, Database, context, fromEtl: false, out _); foreach (var counterOperation in cmd.Counters.Operations) { counterOperation.DocumentId = cmd.Counters.DocumentId; Database.DocumentsStorage.CountersStorage.IncrementCounter(context, cmd.Id, collection, counterOperation.CounterName, counterOperation.Delta, out _); var updates = GetDocumentUpdates(cmd.Id); updates.AddCounter(counterOperation.CounterName); } break; } case CommandType.TimeSeries: case CommandType.TimeSeriesBulkInsert: { var docCollection = TimeSeriesHandler.ExecuteTimeSeriesBatchCommand.GetDocumentCollection(Database, context, cmd.Id, fromEtl: false); Database.DocumentsStorage.TimeSeriesStorage.AppendTimestamp(context, cmd.Id, docCollection, cmd.TimeSeries.Name, cmd.TimeSeries.Appends ); break; } case CommandType.AttachmentPUT: { using (cmd.AttachmentStream.Stream) { Database.DocumentsStorage.AttachmentsStorage.PutAttachment(context, cmd.Id, cmd.Name, cmd.ContentType ?? "", cmd.AttachmentStream.Hash, cmd.ChangeVector, cmd.AttachmentStream.Stream, updateDocument: false); } var updates = GetDocumentUpdates(cmd.Id); updates.AddAttachment(); break; } } } if (_documentsToUpdate.Count > 0) { foreach (var kvp in _documentsToUpdate) { var documentId = kvp.Key; var updates = kvp.Value; if (updates.Attachments) { Database.DocumentsStorage.AttachmentsStorage.UpdateDocumentAfterAttachmentChange(context, documentId); } if (updates.Counters != null && updates.Counters.Count > 0) { var docToUpdate = Database.DocumentsStorage.Get(context, documentId); if (docToUpdate != null) { Database.DocumentsStorage.CountersStorage.UpdateDocumentCounters(context, docToUpdate, documentId, updates.Counters, countersToRemove: null, NonPersistentDocumentFlags.ByCountersUpdate); } } } } if (Logger.IsInfoEnabled) { Logger.Info($"Executed {NumberOfCommands:#,#;;0} bulk insert operations, size: ({new Size(TotalSize, SizeUnit.Bytes)})"); } return(NumberOfCommands); }