Exemple #1
0
        private void FillCollectionEtags(Transaction tx,
                                         Dictionary <string, IndexTransactionCache.CollectionEtags> map)
        {
            foreach (string collection in _index.Collections)
            {
                using (Slice.From(tx.LowLevelTransaction.Allocator, collection, out Slice collectionSlice))
                {
                    var etags = new IndexTransactionCache.CollectionEtags
                    {
                        LastIndexedEtag = IndexStorage.ReadLastEtag(tx,
                                                                    IndexStorage.IndexSchema.EtagsTree,
                                                                    collectionSlice
                                                                    ),
                        LastProcessedTombstoneEtag = IndexStorage.ReadLastEtag(tx,
                                                                               IndexStorage.IndexSchema.EtagsTombstoneTree,
                                                                               collectionSlice
                                                                               )
                    };

                    map[collection] = etags;
                }
            }

            var referencedCollections = _index.GetReferencedCollections();

            if (referencedCollections == null || referencedCollections.Count == 0)
            {
                return;
            }

            foreach (var(src, collections)  in referencedCollections)
            {
                var collectionEtags = map[src];
                collectionEtags.LastReferencedEtags ??= new Dictionary <string, IndexTransactionCache.ReferenceCollectionEtags>(StringComparer.OrdinalIgnoreCase);
                foreach (var collectionName in collections)
                {
                    collectionEtags.LastReferencedEtags[collectionName.Name] = new IndexTransactionCache.ReferenceCollectionEtags
                    {
                        LastEtag = IndexStorage.ReadLastProcessedReferenceEtag(tx, src, collectionName),
                        LastProcessedTombstoneEtag = IndexStorage.ReadLastProcessedReferenceTombstoneEtag(tx, src, collectionName),
                    };
                }
            }
        }
        private 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;
                }

                var totalProcessedCount = 0;

                foreach (var referencedCollection in referencedCollections)
                {
                    var inMemoryStats = _index.GetReferencesStats(referencedCollection.Name);

                    using (var collectionStats = stats.For("Collection_" + referencedCollection.Name))
                    {
                        long lastReferenceEtag;

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

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

                        default:
                            throw new NotSupportedException();
                        }

                        var lastEtag     = lastReferenceEtag;
                        var resultsCount = 0;

                        var sw = new Stopwatch();

                        var keepRunning        = true;
                        var lastCollectionEtag = -1L;
                        while (keepRunning)
                        {
                            var hasChanges = false;

                            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,
                                                                   DocumentFields.Id | DocumentFields.Etag)
                                                 .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();
                                }

                                var isTombstone = actionType == ActionType.Tombstone;

                                foreach (var referencedDocument in references)
                                {
                                    lastEtag   = referencedDocument.Etag;
                                    hasChanges = true;
                                    inMemoryStats.UpdateLastEtag(lastEtag, isTombstone);

                                    var documents = GetDocumentFromCollectionThatReference(databaseContext, indexContext, collection, referencedDocument, lastIndexedEtag);

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

                                            totalProcessedCount++;
                                            collectionStats.RecordMapReferenceAttempt();

                                            var current = docsEnumerator.Current;
                                            stats.RecordDocumentSize(current.Data.Size);

                                            try
                                            {
                                                var numberOfResults = _index.HandleMap(current.LowerId, current.Id, mapResults, writeOperation, indexContext, collectionStats);

                                                resultsCount += numberOfResults;
                                                collectionStats.RecordMapReferenceSuccess();
                                                _index.MapsPerSec.MarkSingleThreaded(numberOfResults);
                                            }
                                            catch (Exception e) when(e.IsIndexError())
                                            {
                                                docsEnumerator.OnError();
                                                _index.ErrorIndexIfCriticalException(e);

                                                collectionStats.RecordMapReferenceError();
                                                if (_logger.IsInfoEnabled)
                                                {
                                                    _logger.Info($"Failed to execute mapping function on '{current.Id}' for '{_index.Name}'.", e);
                                                }

                                                collectionStats.AddMapReferenceError(current.Id,
                                                                                     $"Failed to execute mapping function on {current.Id}. Exception: {e}");
                                            }

                                            _index.UpdateThreadAllocations(indexContext, writeOperation, stats, updateReduceStats: false);
                                        }
                                    }

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

                                    if (totalProcessedCount >= pageSize)
                                    {
                                        keepRunning = false;
                                        break;
                                    }

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

                                if (hasChanges == false)
                                {
                                    break;
                                }
                            }
                        }

                        if (lastReferenceEtag == lastEtag)
                        {
                            // the last referenced etag hasn't changed
                            continue;
                        }

                        moreWorkFound = true;

                        if (_logger.IsInfoEnabled)
                        {
                            _logger.Info($"Executed handle references for '{_index.Name}' index and '{referencedCollection.Name}' collection. " +
                                         $"Got {resultsCount:#,#;;0} map results 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();
                        }
                    }
                }
            }

            return(moreWorkFound);
        }