Beispiel #1
0
        public IEnumerable <Slice> GetDocumentKeysFromCollectionThatReference(string collection, LazyStringValue referenceKey, RavenTransaction tx)
        {
            var collectionTree = tx.InnerTransaction.ReadTree("#" + collection);

            if (collectionTree == null)
            {
                yield break;
            }

            using (DocumentIdWorker.GetLower(tx.InnerTransaction.Allocator, referenceKey, out var k))
                using (var it = collectionTree.MultiRead(k))
                {
                    if (it.Seek(Slices.BeforeAllKeys) == false)
                    {
                        yield break;
                    }

                    do
                    {
                        yield return(it.CurrentKey);
                    } while (it.MoveNext());
                }
        }
        private IEnumerable <Document> GetDocumentFromCollectionThatReference(DocumentsOperationContext databaseContext, TransactionOperationContext indexContext, string collection, Reference referencedDocument, long lastIndexedEtag)
        {
            foreach (var key in _indexStorage.GetDocumentKeysFromCollectionThatReference(collection, referencedDocument.Key, indexContext.Transaction))
            {
                using (GetLower(out Slice loweredKey))
                {
                    // when there is conflict, we need to apply same behavior as if the document would not exist
                    var doc = _documentsStorage.Get(databaseContext, loweredKey, throwOnConflict: false);

                    if (doc == null)
                    {
                        continue;
                    }

                    if (doc.Etag > lastIndexedEtag)
                    {
                        doc.Dispose();
                        continue;
                    }

                    if (doc.Etag > referencedDocument.Etag)
                    {
                        //IF the map worker already mapped this "doc" version it must be with this version of "referencedDocument" and if the map worker didn't mapped the "doc" so it will process it later
                        doc.Dispose();
                        continue;
                    }

                    yield return(doc);
                }

                unsafe ByteStringContext <ByteStringMemoryCache> .InternalScope GetLower(out Slice loweredKey)
                {
                    return(DocumentIdWorker.GetLower(databaseContext.Allocator, key.Content.Ptr, key.Size, out loweredKey));
                }
            }
        }
Beispiel #3
0
        private unsafe bool HandleDocuments(ActionType actionType, DocumentsOperationContext databaseContext, TransactionOperationContext indexContext, Lazy <IndexWriteOperation> writeOperation, IndexingStatsScope stats, int pageSize, TimeSpan maxTimeForDocumentTransactionToRemainOpen, CancellationToken token)
        {
            var moreWorkFound = false;
            Dictionary <string, long> lastIndexedEtagsByCollection = null;

            foreach (var collection in _index.Collections)
            {
                if (_referencedCollections.TryGetValue(collection, out HashSet <CollectionName> referencedCollections) == false)
                {
                    continue;
                }

                if (lastIndexedEtagsByCollection == null)
                {
                    lastIndexedEtagsByCollection = new Dictionary <string, long>(StringComparer.OrdinalIgnoreCase);
                }

                if (lastIndexedEtagsByCollection.TryGetValue(collection, out long lastIndexedEtag) == false)
                {
                    lastIndexedEtagsByCollection[collection] = lastIndexedEtag = _indexStorage.ReadLastIndexedEtag(indexContext.Transaction, collection);
                }

                if (lastIndexedEtag == 0) // we haven't indexed yet, so we are skipping references for now
                {
                    continue;
                }

                foreach (var referencedCollection in referencedCollections)
                {
                    using (var collectionStats = stats.For("Collection_" + referencedCollection.Name))
                    {
                        if (_logger.IsInfoEnabled)
                        {
                            _logger.Info($"Executing handle references for '{_index.Name}'. Collection: {referencedCollection.Name}. Type: {actionType}.");
                        }

                        long lastReferenceEtag;

                        switch (actionType)
                        {
                        case ActionType.Document:
                            lastReferenceEtag = _indexStorage.ReadLastProcessedReferenceEtag(indexContext.Transaction, collection, referencedCollection);
                            break;

                        case ActionType.Tombstone:
                            lastReferenceEtag = _indexStorage.ReadLastProcessedReferenceTombstoneEtag(indexContext.Transaction, collection, referencedCollection);
                            break;

                        default:
                            throw new NotSupportedException();
                        }

                        if (_logger.IsInfoEnabled)
                        {
                            _logger.Info($"Executing handle references for '{_index.Name}'. LastReferenceEtag: {lastReferenceEtag}.");
                        }

                        var lastEtag = lastReferenceEtag;
                        var count    = 0;

                        var sw = new Stopwatch();
                        IndexWriteOperation indexWriter = null;

                        var keepRunning        = true;
                        var lastCollectionEtag = -1L;
                        while (keepRunning)
                        {
                            var batchCount = 0;

                            using (databaseContext.OpenReadTransaction())
                            {
                                sw.Restart();

                                IEnumerable <Reference> references;
                                switch (actionType)
                                {
                                case ActionType.Document:
                                    if (lastCollectionEtag == -1)
                                    {
                                        lastCollectionEtag = _index.GetLastDocumentEtagInCollection(databaseContext, collection);
                                    }

                                    references = _documentsStorage
                                                 .GetDocumentsFrom(databaseContext, referencedCollection.Name, lastEtag + 1, 0, pageSize)
                                                 .Select(document =>
                                    {
                                        _reference.Key  = document.Id;
                                        _reference.Etag = document.Etag;

                                        return(_reference);
                                    });
                                    break;

                                case ActionType.Tombstone:
                                    if (lastCollectionEtag == -1)
                                    {
                                        lastCollectionEtag = _index.GetLastTombstoneEtagInCollection(databaseContext, collection);
                                    }

                                    references = _documentsStorage
                                                 .GetTombstonesFrom(databaseContext, referencedCollection.Name, lastEtag + 1, 0, pageSize)
                                                 .Select(tombstone =>
                                    {
                                        _reference.Key  = tombstone.LowerId;
                                        _reference.Etag = tombstone.Etag;

                                        return(_reference);
                                    });
                                    break;

                                default:
                                    throw new NotSupportedException();
                                }

                                foreach (var referencedDocument in references)
                                {
                                    if (_logger.IsInfoEnabled)
                                    {
                                        _logger.Info($"Executing handle references for '{_index.Name}'. Processing reference: {referencedDocument.Key}.");
                                    }

                                    lastEtag = referencedDocument.Etag;
                                    count++;
                                    batchCount++;

                                    var documents = new List <Document>();
                                    foreach (var key in _indexStorage
                                             .GetDocumentKeysFromCollectionThatReference(collection, referencedDocument.Key, indexContext.Transaction))
                                    {
                                        using (DocumentIdWorker.GetLower(databaseContext.Allocator, key.Content.Ptr, key.Size, out var loweredKey))
                                        {
                                            // when there is conflict, we need to apply same behavior as if the document would not exist
                                            var doc = _documentsStorage.Get(databaseContext, loweredKey, throwOnConflict: false);

                                            if (doc != null && doc.Etag <= lastIndexedEtag)
                                            {
                                                documents.Add(doc);
                                            }
                                        }
                                    }

                                    using (var docsEnumerator = _index.GetMapEnumerator(documents, collection, indexContext, collectionStats))
                                    {
                                        while (docsEnumerator.MoveNext(out IEnumerable mapResults))
                                        {
                                            token.ThrowIfCancellationRequested();

                                            var current = docsEnumerator.Current;

                                            if (indexWriter == null)
                                            {
                                                indexWriter = writeOperation.Value;
                                            }

                                            if (_logger.IsInfoEnabled)
                                            {
                                                _logger.Info($"Executing handle references for '{_index.Name}'. Processing document: {current.Id}.");
                                            }

                                            try
                                            {
                                                _index.HandleMap(current.LowerId, mapResults, indexWriter, indexContext, collectionStats);
                                            }
                                            catch (Exception e)
                                            {
                                                if (_logger.IsInfoEnabled)
                                                {
                                                    _logger.Info($"Failed to execute mapping function on '{current.Id}' for '{_index.Name}'.", e);
                                                }
                                            }
                                        }
                                    }

                                    if (CanContinueBatch(databaseContext, indexContext, collectionStats, indexWriter, lastEtag, lastCollectionEtag, batchCount) == false)
                                    {
                                        keepRunning = false;
                                        break;
                                    }

                                    if (MapDocuments.MaybeRenewTransaction(databaseContext, sw, _configuration, ref maxTimeForDocumentTransactionToRemainOpen))
                                    {
                                        break;
                                    }
                                }

                                if (batchCount == 0 || batchCount >= pageSize)
                                {
                                    break;
                                }
                            }
                        }

                        if (count == 0)
                        {
                            continue;
                        }

                        if (_logger.IsInfoEnabled)
                        {
                            _logger.Info($"Executing handle references for '{_index} ({_index.Name})'. Processed {count} references in '{referencedCollection.Name}' collection in {collectionStats.Duration.TotalMilliseconds:#,#;;0} ms.");
                        }

                        switch (actionType)
                        {
                        case ActionType.Document:
                            _indexStorage.WriteLastReferenceEtag(indexContext.Transaction, collection, referencedCollection, lastEtag);
                            break;

                        case ActionType.Tombstone:
                            _indexStorage.WriteLastReferenceTombstoneEtag(indexContext.Transaction, collection, referencedCollection, lastEtag);
                            break;

                        default:
                            throw new NotSupportedException();
                        }

                        moreWorkFound = true;
                    }
                }
            }

            return(moreWorkFound);
        }
Beispiel #4
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);
            }
        }