private bool TryResolveUsingDefaultResolver( DocumentsOperationContext context, string id, string collection, string incomingChangeVector, BlittableJsonReaderObject doc) { if (_conflictResolver.ConflictSolver?.DatabaseResolverId == null) { return(false); } var conflicts = new List <DocumentConflict>(_database.DocumentsStorage.ConflictsStorage.GetConflictsFor(context, id)); var localDocumentTuple = _database.DocumentsStorage.GetDocumentOrTombstone(context, id, false); var localDoc = DocumentConflict.From(context, localDocumentTuple.Document) ?? DocumentConflict.From(localDocumentTuple.Tombstone); if (localDoc != null) { conflicts.Add(localDoc); } conflicts.Add(new DocumentConflict { ChangeVector = incomingChangeVector, Collection = context.GetLazyStringForFieldWithCaching( collection ?? CollectionName.GetCollectionName(id, doc)), Doc = doc, LowerId = context.GetLazyString(id) }); if (_conflictResolver.TryResolveUsingDefaultResolverInternal( context, _conflictResolver.ConflictSolver.DatabaseResolverId, conflicts, out var resolved)) { _conflictResolver.PutResolvedDocument(context, resolved); return(true); } return(false); }
public void HandleConflictForDocument( DocumentsOperationContext documentsContext, string id, string collection, long lastModifiedTicks, BlittableJsonReaderObject doc, string changeVector, string conflictedChangeVector, DocumentFlags flags) { if (id.StartsWith("Raven/Hilo/", StringComparison.OrdinalIgnoreCase)) { HandleHiloConflict(documentsContext, id, doc, changeVector); return; } if (TryResolveIdenticalDocument( documentsContext, id, doc, lastModifiedTicks, changeVector)) { return; } var conflictedDoc = new DocumentConflict { Doc = doc, Collection = documentsContext.GetLazyStringForFieldWithCaching( collection ?? CollectionName.GetCollectionName(doc) ), LastModified = new DateTime(lastModifiedTicks), LowerId = documentsContext.GetLazyString(id), Id = documentsContext.GetLazyString(id), ChangeVector = changeVector }; if (TryResolveConflictByScript( documentsContext, conflictedDoc)) { return; } if (_conflictResolver.ConflictSolver?.ResolveToLatest == true) { if (conflictedChangeVector == null) //precaution { throw new InvalidOperationException( "Detected conflict on replication, but could not figure out conflicted vector. This is not supposed to happen and is likely a bug."); } var conflicts = new List <DocumentConflict> { conflictedDoc.Clone() }; conflicts.AddRange(documentsContext.DocumentDatabase.DocumentsStorage.ConflictsStorage.GetConflictsFor( documentsContext, id)); var localDocumentTuple = documentsContext.DocumentDatabase.DocumentsStorage.GetDocumentOrTombstone(documentsContext, id, false); var local = DocumentConflict.From(documentsContext, localDocumentTuple.Document) ?? DocumentConflict.From(localDocumentTuple.Tombstone); if (local != null) { conflicts.Add(local); } var resolved = _conflictResolver.ResolveToLatest(documentsContext, conflicts); _conflictResolver.PutResolvedDocument(documentsContext, resolved, conflictedDoc); return; } _database.DocumentsStorage.ConflictsStorage.AddConflict(documentsContext, id, lastModifiedTicks, doc, changeVector, collection, flags); }
public static void RemoveMetadata(DocumentsOperationContext context, HashSet <LazyStringValue> columns) { var metadataField = context.GetLazyStringForFieldWithCaching(Constants.Documents.Metadata.Key); columns.Remove(metadataField); }
private bool TryResolveConflictByScript( DocumentsOperationContext documentsContext, string id, string incomingChangeVector, BlittableJsonReaderObject doc) { var collection = CollectionName.GetCollectionName(id, doc); var hasScript = _conflictResolver.ScriptConflictResolversCache.TryGetValue(collection, out ScriptResolver scriptResolver); if (!hasScript || scriptResolver == null) { if (_log.IsInfoEnabled) { _log.Info($"Script not found to resolve the {collection} collection"); } return(false); } var conflictedDocs = new List <DocumentConflict>(documentsContext.DocumentDatabase.DocumentsStorage.ConflictsStorage.GetConflictsFor(documentsContext, id)); if (conflictedDocs.Count == 0) { var relevantLocalDoc = documentsContext.DocumentDatabase.DocumentsStorage .GetDocumentOrTombstone( documentsContext, id); if (relevantLocalDoc.Document != null) { conflictedDocs.Add(DocumentConflict.From(documentsContext, relevantLocalDoc.Document)); } else if (relevantLocalDoc.Tombstone != null) { conflictedDocs.Add(DocumentConflict.From(relevantLocalDoc.Tombstone)); } } if (conflictedDocs.Count == 0) { InvalidConflictWhenThereIsNone(id); } conflictedDocs.Add(new DocumentConflict { LowerId = conflictedDocs[0].LowerId, Id = conflictedDocs[0].Id, Collection = documentsContext.GetLazyStringForFieldWithCaching(collection), ChangeVector = incomingChangeVector, Doc = doc }); if (_conflictResolver.TryResolveConflictByScriptInternal( documentsContext, scriptResolver, conflictedDocs, documentsContext.GetLazyString(collection), out var resolved)) { _conflictResolver.PutResolvedDocument(documentsContext, resolved); return(true); } return(false); }
public unsafe void HandleConflictForDocument( DocumentsOperationContext documentsContext, string id, string collection, long lastModifiedTicks, BlittableJsonReaderObject doc, string changeVector, DocumentFlags flags) { if (id.StartsWith(HiLoHandler.RavenHiloIdPrefix, StringComparison.OrdinalIgnoreCase)) { HandleHiloConflict(documentsContext, id, doc, changeVector); return; } if (TryResolveIdenticalDocument( documentsContext, id, doc, lastModifiedTicks, changeVector)) { return; } var lazyId = documentsContext.GetLazyString(id); using (DocumentIdWorker.GetLower(documentsContext.Allocator, lazyId, out var loweredKey)) { var conflictedDoc = new DocumentConflict { Doc = doc, Collection = documentsContext.GetLazyStringForFieldWithCaching( collection ?? CollectionName.GetCollectionName(doc) ), LastModified = new DateTime(lastModifiedTicks), LowerId = documentsContext.AllocateStringValue(null, loweredKey.Content.Ptr, loweredKey.Content.Length), Id = lazyId, ChangeVector = changeVector, Flags = flags }; if (TryResolveConflictByScript( documentsContext, conflictedDoc)) { return; } if (_conflictResolver.ConflictSolver?.ResolveToLatest ?? true) { var conflicts = new List <DocumentConflict> { conflictedDoc.Clone() }; conflicts.AddRange(_database.DocumentsStorage.ConflictsStorage.GetConflictsFor(documentsContext, id)); var localDocumentTuple = _database.DocumentsStorage.GetDocumentOrTombstone(documentsContext, id, false); var local = DocumentConflict.From(documentsContext, localDocumentTuple.Document) ?? DocumentConflict.From(localDocumentTuple.Tombstone); if (local != null) { conflicts.Add(local); } var resolved = _conflictResolver.ResolveToLatest(conflicts); _conflictResolver.PutResolvedDocument(documentsContext, resolved, resolvedToLatest: true, conflictedDoc); return; } _database.DocumentsStorage.ConflictsStorage.AddConflict(documentsContext, id, lastModifiedTicks, doc, changeVector, collection, flags); } }
/// <summary> /// Iterates on a batch in document collection, process it and send documents if found any match /// </summary> /// <param name="docsContext"></param> /// <param name="sendingCurrentBatchStopwatch"></param> /// <returns>Whether succeeded finding any documents to send</returns> private async Task <bool> TrySendingBatchToClient(DocumentsOperationContext docsContext, Stopwatch sendingCurrentBatchStopwatch) { bool anyDocumentsSentInCurrentIteration = false; int docsToFlush = 0; using (var writer = new BlittableJsonTextWriter(docsContext, _buffer)) { using (docsContext.OpenReadTransaction()) { foreach (var result in _documentsFetcher.GetDataToSend(docsContext, _startEtag)) { _startEtag = result.Doc.Etag; _lastChangeVector = string.IsNullOrEmpty(SubscriptionState.ChangeVectorForNextBatchStartingPoint) ? result.Doc.ChangeVector : ChangeVectorUtils.MergeVectors(result.Doc.ChangeVector, SubscriptionState.ChangeVectorForNextBatchStartingPoint); if (result.Doc.Data == null) { if (sendingCurrentBatchStopwatch.ElapsedMilliseconds > 1000) { await SendHeartBeat(); sendingCurrentBatchStopwatch.Restart(); } continue; } anyDocumentsSentInCurrentIteration = true; writer.WriteStartObject(); writer.WritePropertyName(docsContext.GetLazyStringForFieldWithCaching(TypeSegment)); writer.WriteValue(BlittableJsonToken.String, docsContext.GetLazyStringForFieldWithCaching(DataSegment)); writer.WriteComma(); writer.WritePropertyName(docsContext.GetLazyStringForFieldWithCaching(DataSegment)); result.Doc.EnsureMetadata(); if (result.Exception != null) { var metadata = result.Doc.Data[Client.Constants.Documents.Metadata.Key]; writer.WriteValue(BlittableJsonToken.StartObject, docsContext.ReadObject(new DynamicJsonValue { [Client.Constants.Documents.Metadata.Key] = metadata }, result.Doc.Id) ); writer.WriteComma(); writer.WritePropertyName(docsContext.GetLazyStringForFieldWithCaching(ExceptionSegment)); writer.WriteValue(BlittableJsonToken.String, docsContext.GetLazyStringForFieldWithCaching(result.Exception.ToString())); } else { writer.WriteDocument(docsContext, result.Doc, metadataOnly: false); } writer.WriteEndObject(); docsToFlush++; // perform flush for current batch after 1000ms of running or 1 MB if (_buffer.Length > Constants.Size.Megabyte || sendingCurrentBatchStopwatch.ElapsedMilliseconds > 1000) { if (docsToFlush > 0) { await FlushDocsToClient(writer, docsToFlush); docsToFlush = 0; sendingCurrentBatchStopwatch.Restart(); } else { await SendHeartBeat(); } } } } if (anyDocumentsSentInCurrentIteration) { docsContext.Write(writer, new DynamicJsonValue { [nameof(SubscriptionConnectionServerMessage.Type)] = nameof(SubscriptionConnectionServerMessage.MessageType.EndOfBatch) }); await FlushDocsToClient(writer, docsToFlush, true); if (_logger.IsInfoEnabled) { _logger.Info( $"Finished sending a batch with {docsToFlush} documents for subscription {Options.SubscriptionName}"); } } } return(anyDocumentsSentInCurrentIteration); }