Esempio n. 1
0
        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);
        }
Esempio n. 2
0
        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);
        }
Esempio n. 3
0
        public static void RemoveMetadata(DocumentsOperationContext context, HashSet <LazyStringValue> columns)
        {
            var metadataField = context.GetLazyStringForFieldWithCaching(Constants.Documents.Metadata.Key);

            columns.Remove(metadataField);
        }
Esempio n. 4
0
        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);
        }
Esempio n. 5
0
        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);
            }
        }
Esempio n. 6
0
        /// <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);
        }