예제 #1
0
        public override void AfterDelete(string key, TransactionInformation transactionInformation)
        {
            var versioningConfig = Database.GetDocumentVersioningConfiguration(versionInformer.Value[key]);


            using (Database.DisableAllTriggersForCurrentThread())
            {
                Database.TransactionalStorage.Batch(accessor =>
                {
                    using (DocumentCacher.SkipSetDocumentsInDocumentCache())
                    {
                        foreach (var jsonDocument in accessor.Documents.GetDocumentsWithIdStartingWith(key + "/revisions/", 0, int.MaxValue, null))
                        {
                            if (jsonDocument == null)
                            {
                                continue;
                            }
                            if (versioningConfig != null && versioningConfig.PurgeOnDelete)
                            {
                                Database.Documents.Delete(jsonDocument.Key, null, transactionInformation);
                            }
                            else
                            {
                                jsonDocument.Metadata.Remove(Constants.RavenReadOnly);
                                accessor.Documents.AddDocument(jsonDocument.Key, jsonDocument.Etag, jsonDocument.DataAsJson, jsonDocument.Metadata);
                            }
                        }
                    }
                });
            }
        }
예제 #2
0
            protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
            {
                var bufferSize = queryOp.Header.TotalResults > 1024 ? 1024 * 64 : 1024 * 8;

                using (var bufferedStream = new BufferedStream(stream, bufferSize))
                    using (queryOp)
                        using (accessor)
                            using (_timeout)
                                using (var writer = GetOutputWriter(req, bufferedStream))
                                    // we may be sending a LOT of documents to the user, and most
                                    // of them aren't going to be relevant for other ops, so we are going to skip
                                    // the cache for that, to avoid filling it up very quickly
                                    using (DocumentCacher.SkipSettingDocumentsInDocumentCache())
                                    {
                                        outputContentTypeSetter(writer.ContentType);

                                        writer.WriteHeader();
                                        try
                                        {
                                            queryOp.Execute(o =>
                                            {
                                                _timeout.Delay();
                                                writer.Write(o);
                                            });
                                        }
                                        catch (Exception e)
                                        {
                                            writer.WriteError(e);
                                        }
                                    }
                return(Task.FromResult(true));
            }
예제 #3
0
        public void Index(string index,
                          AbstractViewGenerator viewGenerator,
                          IndexingBatch batch,
                          WorkContext context,
                          IStorageActionsAccessor actions,
                          DateTime minimumTimestamp)
        {
            Index value;

            if (indexes.TryGetValue(index, out value) == false)
            {
                log.Debug("Tried to index on a non existent index {0}, ignoring", index);
                return;
            }
            using (EnsureInvariantCulture())
                using (DocumentCacher.SkipSettingDocumentsInDocumentCache())
                {
                    value.IndexDocuments(viewGenerator, batch, context, actions, minimumTimestamp);
                    context.RaiseIndexChangeNotification(new IndexChangeNotification
                    {
                        Name = index,
                        Type = IndexChangeTypes.MapCompleted
                    });
                }
        }
예제 #4
0
        private HttpResponseMessage OnBulkOperation(Func <string, IndexQuery, BulkOperationOptions, Action <BulkOperationProgress>, RavenJArray> batchOperation, string index, CancellationTimeout timeout)
        {
            if (string.IsNullOrEmpty(index))
            {
                return(GetEmptyMessage(HttpStatusCode.BadRequest));
            }

            var option = new BulkOperationOptions
            {
                AllowStale      = GetAllowStale(),
                MaxOpsPerSec    = GetMaxOpsPerSec(),
                StaleTimeout    = GetStaleTimeout(),
                RetrieveDetails = GetRetrieveDetails()
            };

            var indexQuery = GetIndexQuery(maxPageSize: int.MaxValue);

            var  status = new BulkOperationStatus();
            long id;

            var task = Task.Factory.StartNew(() =>
            {
                using (DocumentCacher.SkipSetDocumentsInDocumentCache())
                {
                    status.State["Batch"] = batchOperation(index, indexQuery, option, x =>
                    {
                        status.MarkProgress(x);
                    });
                }
            }).ContinueWith(t =>
            {
                if (timeout != null)
                {
                    timeout.Dispose();
                }

                if (t.IsFaulted == false)
                {
                    status.MarkCompleted($"Processed {status.OperationProgress.ProcessedEntries} items");
                    return;
                }

                var exception = t.Exception.ExtractSingleInnerException();

                status.MarkFaulted(exception.Message);
            });

            Database.Tasks.AddTask(task, status, new TaskActions.PendingTaskDescription
            {
                StartTime   = SystemTime.UtcNow,
                TaskType    = TaskActions.PendingTaskType.IndexBulkOperation,
                Description = index
            }, out id, timeout.CancellationTokenSource);

            return(GetMessageWithObject(new { OperationId = id }, HttpStatusCode.Accepted));
        }
예제 #5
0
        public override void Respond(IHttpContext context)
        {
            using (context.Response.Streaming())
            {
                context.Response.ContentType = "application/json; charset=utf-8";

                var match = urlMatcher.Match(context.GetRequestUrl());
                var index = match.Groups[1].Value;

                var query = context.GetIndexQueryFromHttpContext(int.MaxValue);
                if (string.IsNullOrEmpty(context.Request.QueryString["pageSize"]))
                {
                    query.PageSize = int.MaxValue;
                }
                var isHeadRequest = context.Request.HttpMethod == "HEAD";
                if (isHeadRequest)
                {
                    query.PageSize = 0;
                }

                using (var writer = GetOutputWriter(context))
                {
                    // we may be sending a LOT of documents to the user, and most
                    // of them aren't going to be relevant for other ops, so we are going to skip
                    // the cache for that, to avoid filling it up very quickly
                    using (DocumentCacher.SkipSettingDocumentsInDocumentCache())
                        using (var cts = new CancellationTokenSource())
                            using (var timeout = cts.TimeoutAfter(Settings.DatbaseOperationTimeout))
                            {
                                Database.Query(index, query, cts.Token, information =>
                                {
                                    context.Response.AddHeader("Raven-Result-Etag", information.ResultEtag.ToString());
                                    context.Response.AddHeader("Raven-Index-Etag", information.IndexEtag.ToString());
                                    context.Response.AddHeader("Raven-Is-Stale", information.IsStable ? "true" : "false");
                                    context.Response.AddHeader("Raven-Index", information.Index);
                                    context.Response.AddHeader("Raven-Total-Results", information.TotalResults.ToString(CultureInfo.InvariantCulture));
                                    context.Response.AddHeader("Raven-Index-Timestamp",
                                                               information.IndexTimestamp.ToString(Default.DateTimeFormatsToWrite,
                                                                                                   CultureInfo.InvariantCulture));

                                    if (isHeadRequest)
                                    {
                                        return;
                                    }
                                    writer.WriteHeader();
                                }, o =>
                                {
                                    timeout.Delay();
                                    Database.WorkContext.UpdateFoundWork();
                                    writer.Write(o);
                                });
                            }
                }
            }
        }
예제 #6
0
        public override void Respond(IHttpContext context)
        {
            using (context.Response.Streaming())
            {
                context.Response.ContentType = "application/json; charset=utf-8";

                using (var writer = new JsonTextWriter(new StreamWriter(context.Response.OutputStream)))
                {
                    writer.WriteStartObject();
                    writer.WritePropertyName("Results");
                    writer.WriteStartArray();

                    Database.TransactionalStorage.Batch(accessor =>
                    {
                        var startsWith = context.Request.QueryString["startsWith"];
                        int pageSize   = context.GetPageSize(int.MaxValue);
                        if (string.IsNullOrEmpty(context.Request.QueryString["pageSize"]))
                        {
                            pageSize = int.MaxValue;
                        }

                        // we may be sending a LOT of documents to the user, and most
                        // of them aren't going to be relevant for other ops, so we are going to skip
                        // the cache for that, to avoid filling it up very quickly
                        using (DocumentCacher.SkipSettingDocumentsInDocumentCache())
                        {
                            if (string.IsNullOrEmpty(startsWith))
                            {
                                Database.GetDocuments(context.GetStart(), pageSize, context.GetEtagFromQueryString(),
                                                      doc => doc.WriteTo(writer));
                            }
                            else
                            {
                                Database.GetDocumentsWithIdStartingWith(
                                    startsWith,
                                    context.Request.QueryString["matches"],
                                    context.Request.QueryString["exclude"],
                                    context.GetStart(),
                                    pageSize,
                                    doc => doc.WriteTo(writer));
                            }
                        }
                    });

                    writer.WriteEndArray();
                    writer.WriteEndObject();
                    writer.Flush();
                }
            }
        }
예제 #7
0
        private void StreamToClient(Stream stream, string startsWith, int start, int pageSize, Etag etag, string matches, int nextPageStart, string skipAfter)
        {
            using (var cts = new CancellationTokenSource())
                using (var timeout = cts.TimeoutAfter(DatabasesLandlord.SystemConfiguration.DatabaseOperationTimeout))
                    using (var writer = new JsonTextWriter(new StreamWriter(stream)))
                    {
                        writer.WriteStartObject();
                        writer.WritePropertyName("Results");
                        writer.WriteStartArray();

                        Database.TransactionalStorage.Batch(accessor =>
                        {
                            // we may be sending a LOT of documents to the user, and most
                            // of them aren't going to be relevant for other ops, so we are going to skip
                            // the cache for that, to avoid filling it up very quickly
                            using (DocumentCacher.SkipSettingDocumentsInDocumentCache())
                            {
                                if (string.IsNullOrEmpty(startsWith))
                                {
                                    Database.Documents.GetDocuments(start, pageSize, etag, cts.Token, doc =>
                                    {
                                        timeout.Delay();
                                        doc.WriteTo(writer);
                                        writer.WriteRaw(Environment.NewLine);
                                    });
                                }
                                else
                                {
                                    var nextPageStartInternal = nextPageStart;

                                    Database.Documents.GetDocumentsWithIdStartingWith(startsWith, matches, null, start, pageSize, cts.Token, ref nextPageStartInternal, doc =>
                                    {
                                        timeout.Delay();
                                        doc.WriteTo(writer);
                                        writer.WriteRaw(Environment.NewLine);
                                    }, skipAfter: skipAfter);

                                    nextPageStart = nextPageStartInternal;
                                }
                            }
                        });

                        writer.WriteEndArray();
                        writer.WritePropertyName("NextPageStart");
                        writer.WriteValue(nextPageStart);
                        writer.WriteEndObject();
                        writer.Flush();
                    }
        }
예제 #8
0
        public RavenJArray GetDocumentsWithIdStartingWith(string idPrefix, string matches, string exclude, int start,
                                                          int pageSize, CancellationToken token, ref int nextStart,
                                                          string transformer = null, Dictionary <string, RavenJToken> transformerParameters = null,
                                                          string skipAfter   = null)
        {
            using (DocumentCacher.SkipSetDocumentsInDocumentCache())
            {
                var list = new RavenJArray();
                GetDocumentsWithIdStartingWith(idPrefix, matches, exclude, start, pageSize, token, ref nextStart,
                                               doc => { if (doc != null)
                                                        {
                                                            list.Add(doc.ToJson());
                                                        }
                                               }, transformer, transformerParameters, skipAfter);

                return(list);
            }
        }
예제 #9
0
        private void WriteDocuments(JsonTextWriter jsonWriter)
        {
            long totalDocsCount = 0;


            storage.Batch(accsesor => totalDocsCount = accsesor.Documents.GetDocumentsCount());

            using (DocumentCacher.SkipSetDocumentsInDocumentCache())
            {
                if (DocumentsStartEtag == Etag.Empty)
                {
                    ExtractDocuments(jsonWriter, totalDocsCount);
                }
                else
                {
                    ExtractDocumentsFromEtag(jsonWriter, totalDocsCount);
                }
            }
        }
예제 #10
0
        public void Index(string index,
                          AbstractViewGenerator viewGenerator,
                          IEnumerable <dynamic> docs,
                          WorkContext context,
                          IStorageActionsAccessor actions,
                          DateTime minimumTimestamp)
        {
            Index value;

            if (indexes.TryGetValue(index, out value) == false)
            {
                log.Debug("Tried to index on a non existant index {0}, ignoring", index);
                return;
            }
            using (EnsureInvariantCulture())
                using (DocumentCacher.SkipSettingDocumentsInDocumentCache())
                {
                    value.IndexDocuments(viewGenerator, docs, context, actions, minimumTimestamp);
                }
        }
예제 #11
0
        public Etag GetDocuments(int start, int pageSize, Etag etag, CancellationToken token, Func <JsonDocument, bool> addDocument,
                                 string transformer = null, Dictionary <string, RavenJToken> transformerParameters = null, long?maxSize = null, TimeSpan?timeout = null)
        {
            Etag lastDocumentReadEtag = null;

            using (DocumentCacher.SkipSetDocumentsInDocumentCache())
                TransactionalStorage.Batch(actions =>
                {
                    AbstractTransformer storedTransformer = null;
                    if (transformer != null)
                    {
                        storedTransformer = IndexDefinitionStorage.GetTransformer(transformer);
                        if (storedTransformer == null)
                        {
                            throw new InvalidOperationException("No transformer with the name: " + transformer);
                        }
                    }

                    var returnedDocs = false;
                    while (true)
                    {
                        var documents = etag == null
                            ? actions.Documents.GetDocumentsByReverseUpdateOrder(start, pageSize)
                            : actions.Documents.GetDocumentsAfter(etag, pageSize, token, maxSize: maxSize, timeout: timeout);

                        var documentRetriever = new DocumentRetriever(Database.Configuration, actions, Database.ReadTriggers, transformerParameters);
                        var docCount          = 0;
                        var docCountOnLastAdd = 0;
                        foreach (var doc in documents)
                        {
                            docCount++;

                            token.ThrowIfCancellationRequested();

                            if (docCount - docCountOnLastAdd > 1000)
                            {
                                addDocument(null); // heartbeat
                            }

                            if (etag != null)
                            {
                                etag = doc.Etag;
                            }

                            JsonDocument.EnsureIdInMetadata(doc);

                            var nonAuthoritativeInformationBehavior = actions.InFlightStateSnapshot.GetNonAuthoritativeInformationBehavior <JsonDocument>(null, doc.Key);
                            var document = nonAuthoritativeInformationBehavior == null ? doc : nonAuthoritativeInformationBehavior(doc);

                            document = documentRetriever.ExecuteReadTriggers(document, null, ReadOperation.Load);
                            if (document == null)
                            {
                                continue;
                            }

                            returnedDocs = true;
                            Database.WorkContext.UpdateFoundWork();

                            document = TransformDocumentIfNeeded(document, storedTransformer, documentRetriever);

                            var canContinue = addDocument(document);
                            if (!canContinue)
                            {
                                break;
                            }

                            lastDocumentReadEtag = etag;

                            docCountOnLastAdd = docCount;
                        }

                        if (returnedDocs || docCount == 0)
                        {
                            break;
                        }

                        // No document was found that matches the requested criteria
                        // If we had a failure happen, we update the etag as we don't need to process those documents again (no matches there anyways).
                        if (lastDocumentReadEtag != null)
                        {
                            etag = lastDocumentReadEtag;
                        }

                        start += docCount;
                    }
                });

            return(lastDocumentReadEtag);
        }
예제 #12
0
        private void ApplyPrecomputedBatchForNewIndex(Index index, AbstractViewGenerator generator, int pageSize, CancellationTokenSource cts)
        {
            PrecomputedIndexingBatch result = null;

            var docsToIndex = new List <JsonDocument>();

            TransactionalStorage.Batch(actions =>
            {
                var query = QueryBuilder.GetQueryForAllMatchingDocumentsForIndex(Database, generator.ForEntityNames);

                using (DocumentCacher.SkipSetDocumentsInDocumentCache())
                    using (var linked = CancellationTokenSource.CreateLinkedTokenSource(cts.Token, WorkContext.CancellationToken))
                        using (var op = new QueryActions.DatabaseQueryOperation(Database, Constants.DocumentsByEntityNameIndex, new IndexQuery
                        {
                            Query = query,
                            PageSize = pageSize
                        }, actions, linked)
                        {
                            ShouldSkipDuplicateChecking = true
                        })
                        {
                            op.Init();

                            //if we are working on a test index, apply the optimization anyway, as the index is capped by small number of results
                            if (op.Header.TotalResults > pageSize && index.IsTestIndex == false)
                            {
                                // we don't apply this optimization if the total number of results
                                // to index is more than the max numbers to index in a single batch.
                                // The idea here is that we need to keep the amount
                                // of memory we use to a manageable level even when introducing a new index to a BIG
                                // database
                                try
                                {
                                    cts.Cancel();
                                    // we have to run just a little bit of the query to properly setup the disposal
                                    op.Execute(o => { });
                                }
                                catch (OperationCanceledException)
                                {
                                }
                                return;
                            }

                            if (Log.IsDebugEnabled)
                            {
                                Log.Debug("For new index {0}, using precomputed indexing batch optimization for {1} docs",
                                          index, op.Header.TotalResults);
                            }

                            var totalLoadedDocumentSize = 0;
                            const int totalSizeToCheck  = 16 * 1024 * 1024; //16MB
                            var localLoadedDocumentSize = 0;
                            op.Execute(document =>
                            {
                                var metadata     = document.Value <RavenJObject>(Constants.Metadata);
                                var key          = metadata.Value <string>("@id");
                                var etag         = Etag.Parse(metadata.Value <string>("@etag"));
                                var lastModified = DateTime.Parse(metadata.Value <string>(Constants.LastModified));
                                document.Remove(Constants.Metadata);
                                var serializedSizeOnDisk = metadata.Value <int>(Constants.SerializedSizeOnDisk);
                                metadata.Remove(Constants.SerializedSizeOnDisk);

                                var doc = new JsonDocument
                                {
                                    DataAsJson           = document,
                                    Etag                 = etag,
                                    Key                  = key,
                                    SerializedSizeOnDisk = serializedSizeOnDisk,
                                    LastModified         = lastModified,
                                    SkipDeleteFromIndex  = true,
                                    Metadata             = metadata
                                };

                                docsToIndex.Add(doc);
                                totalLoadedDocumentSize += serializedSizeOnDisk;
                                localLoadedDocumentSize += serializedSizeOnDisk;

                                if (totalLoadedDocumentSize > Database.Configuration.MaxPrecomputedBatchTotalDocumentSizeInBytes)
                                {
                                    var error = $"Aborting applying precomputed batch for index id: {index.indexId}, " +
                                                $"name: {index.PublicName} because we have {totalLoadedDocumentSize}MB of documents that were fetched" +
                                                $"and the configured max data to fetch is " +
                                                $"{Database.Configuration.MaxPrecomputedBatchTotalDocumentSizeInBytes/1024/1024}MB";

                                    //we are aborting operation, so don't keep the references
                                    docsToIndex.Clear();
                                    throw new TotalDataSizeExceededException(error);
                                }


                                if (localLoadedDocumentSize <= totalSizeToCheck)
                                {
                                    return;
                                }

                                localLoadedDocumentSize = 0;

                                if (Database.Configuration.MemoryLimitForProcessingInMb > MemoryStatistics.AvailableMemoryInMb)
                                {
                                    var error = $"Aborting applying precomputed batch for index id: {index.indexId}, " +
                                                $"name: {index.PublicName} because we have {MemoryStatistics.AvailableMemoryInMb}MB " +
                                                $"of available memory and the available memory for processing is: " +
                                                $"{Database.Configuration.MemoryLimitForProcessingInMb}MB";

                                    //we are aborting operation, so don't keep the references
                                    docsToIndex.Clear();
                                    throw new TotalDataSizeExceededException(error);
                                }
                            });

                            result = new PrecomputedIndexingBatch
                            {
                                LastIndexed  = op.Header.IndexEtag,
                                LastModified = op.Header.IndexTimestamp,
                                Documents    = docsToIndex,
                                Index        = index
                            };
                        }
            });

            if (result != null && result.Documents != null && result.Documents.Count >= 0)
            {
                using (var linked = CancellationTokenSource.CreateLinkedTokenSource(cts.Token, WorkContext.CancellationToken))
                {
                    Database.IndexingExecuter.IndexPrecomputedBatch(result, linked.Token);

                    if (index.IsTestIndex)
                    {
                        TransactionalStorage.Batch(accessor => accessor.Indexing.TouchIndexEtag(index.IndexId));
                    }
                }
            }
        }
예제 #13
0
        private void StreamToClient(long id, SubscriptionActions subscriptions, Stream stream)
        {
            var sentDocuments = false;

            var bufferStream = new BufferedStream(stream, 1024 * 64);

            using (var writer = new JsonTextWriter(new StreamWriter(bufferStream)))
            {
                var options = subscriptions.GetBatchOptions(id);

                writer.WriteStartObject();
                writer.WritePropertyName("Results");
                writer.WriteStartArray();

                using (var cts = new CancellationTokenSource())
                    using (var timeout = cts.TimeoutAfter(DatabasesLandlord.SystemConfiguration.DatabaseOperationTimeout))
                    {
                        Etag lastProcessedDocEtag = null;

                        var batchSize          = 0;
                        var batchDocCount      = 0;
                        var processedDocuments = 0;
                        var hasMoreDocs        = false;
                        var config             = subscriptions.GetSubscriptionConfig(id);
                        var startEtag          = config.AckEtag;
                        var criteria           = config.Criteria;

                        bool isPrefixCriteria = !string.IsNullOrWhiteSpace(criteria.KeyStartsWith);

                        Func <JsonDocument, bool> addDocument = doc =>
                        {
                            timeout.Delay();
                            if (doc == null)
                            {
                                // we only have this heartbeat when the streaming has gone on for a long time
                                // and we haven't send anything to the user in a while (because of filtering, skipping, etc).
                                writer.WriteRaw(Environment.NewLine);
                                writer.Flush();
                                return(true);
                            }
                            processedDocuments++;


                            // We cant continue because we have already maxed out the batch bytes size.
                            if (options.MaxSize.HasValue && batchSize >= options.MaxSize)
                            {
                                return(false);
                            }

                            // We cant continue because we have already maxed out the amount of documents to send.
                            if (batchDocCount >= options.MaxDocCount)
                            {
                                return(false);
                            }

                            // We can continue because we are ignoring system documents.
                            if (doc.Key.StartsWith("Raven/", StringComparison.InvariantCultureIgnoreCase))
                            {
                                return(true);
                            }

                            // We can continue because we are ignoring the document as it doesn't fit the criteria.
                            if (MatchCriteria(criteria, doc) == false)
                            {
                                return(true);
                            }

                            doc.ToJson().WriteTo(writer);
                            writer.WriteRaw(Environment.NewLine);

                            batchSize += doc.SerializedSizeOnDisk;
                            batchDocCount++;

                            return(true); // We get the next document
                        };

                        int retries = 0;
                        do
                        {
                            int lastIndex = processedDocuments;

                            Database.TransactionalStorage.Batch(accessor =>
                            {
                                // we may be sending a LOT of documents to the user, and most
                                // of them aren't going to be relevant for other ops, so we are going to skip
                                // the cache for that, to avoid filling it up very quickly
                                using (DocumentCacher.SkipSetAndGetDocumentsInDocumentCache())
                                {
                                    if (isPrefixCriteria)
                                    {
                                        // If we don't get any document from GetDocumentsWithIdStartingWith it could be that we are in presence of a lagoon of uninteresting documents, so we are hitting a timeout.
                                        lastProcessedDocEtag = Database.Documents.GetDocumentsWithIdStartingWith(criteria.KeyStartsWith, options.MaxDocCount - batchDocCount, startEtag, cts.Token, addDocument);

                                        hasMoreDocs = false;
                                    }
                                    else
                                    {
                                        // It doesn't matter if we match the criteria or not, the document has been already processed.
                                        lastProcessedDocEtag = Database.Documents.GetDocuments(-1, options.MaxDocCount - batchDocCount, startEtag, cts.Token, addDocument);

                                        // If we don't get any document from GetDocuments it may be a signal that something is wrong.
                                        if (lastProcessedDocEtag == null)
                                        {
                                            hasMoreDocs = false;
                                        }
                                        else
                                        {
                                            var lastDocEtag = accessor.Staleness.GetMostRecentDocumentEtag();
                                            hasMoreDocs     = EtagUtil.IsGreaterThan(lastDocEtag, lastProcessedDocEtag);

                                            startEtag = lastProcessedDocEtag;
                                        }

                                        retries = lastIndex == batchDocCount ? retries : 0;
                                    }
                                }
                            });

                            if (lastIndex == processedDocuments)
                            {
                                if (retries == 3)
                                {
                                    log.Warn("Subscription processing did not end up replicating any documents for 3 times in a row, stopping operation", retries);
                                }
                                else
                                {
                                    log.Warn("Subscription processing did not end up replicating any documents, due to possible storage error, retry number: {0}", retries);
                                }
                                retries++;
                            }
                        }while (retries < 3 && hasMoreDocs && batchDocCount < options.MaxDocCount && (options.MaxSize.HasValue == false || batchSize < options.MaxSize));

                        writer.WriteEndArray();

                        if (batchDocCount > 0 || isPrefixCriteria)
                        {
                            writer.WritePropertyName("LastProcessedEtag");
                            writer.WriteValue(lastProcessedDocEtag.ToString());

                            sentDocuments = true;
                        }

                        writer.WriteEndObject();
                        writer.Flush();

                        bufferStream.Flush();
                    }
            }

            if (sentDocuments)
            {
                subscriptions.UpdateBatchSentTime(id);
            }
        }
예제 #14
0
        private void StreamToClient(Stream stream, string startsWith, int start, int pageSize, Etag etag, string matches, int nextPageStart, string skipAfter, string transformer, Dictionary <string, RavenJToken> transformerParameters,
                                    Lazy <NameValueCollection> headers, IPrincipal user)
        {
            var old     = CurrentOperationContext.Headers.Value;
            var oldUser = CurrentOperationContext.User.Value;

            try
            {
                CurrentOperationContext.Headers.Value = headers;
                CurrentOperationContext.User.Value    = user;


                var bufferStream = new BufferedStream(stream, 1024 * 64);
                using (var cts = new CancellationTokenSource())
                    using (var timeout = cts.TimeoutAfter(DatabasesLandlord.SystemConfiguration.DatabaseOperationTimeout))
                        using (var writer = new JsonTextWriter(new StreamWriter(bufferStream)))
                        {
                            writer.WriteStartObject();
                            writer.WritePropertyName("Results");
                            writer.WriteStartArray();

                            Action <JsonDocument> addDocument = doc =>
                            {
                                timeout.Delay();
                                if (doc == null)
                                {
                                    // we only have this heartbit when the streaming has gone on for a long time
                                    // and we haven't send anything to the user in a while (because of filtering, skipping, etc).
                                    writer.WriteRaw(Environment.NewLine);
                                    writer.Flush();
                                    return;
                                }
                                doc.ToJson().WriteTo(writer);
                                writer.WriteRaw(Environment.NewLine);
                            };

                            Database.TransactionalStorage.Batch(accessor =>
                            {
                                // we may be sending a LOT of documents to the user, and most
                                // of them aren't going to be relevant for other ops, so we are going to skip
                                // the cache for that, to avoid filling it up very quickly
                                using (DocumentCacher.SkipSetAndGetDocumentsInDocumentCache())
                                {
                                    if (string.IsNullOrEmpty(startsWith))
                                    {
                                        Database.Documents.GetDocuments(start, pageSize, etag, cts.Token, doc => { addDocument(doc); return(true); }, transformer, transformerParameters);
                                    }
                                    else
                                    {
                                        var nextPageStartInternal = nextPageStart;

                                        Database.Documents.GetDocumentsWithIdStartingWith(startsWith, matches, null, start, pageSize, cts.Token, ref nextPageStartInternal, addDocument, transformer, transformerParameters, skipAfter);

                                        nextPageStart = nextPageStartInternal;
                                    }
                                }
                            });

                            writer.WriteEndArray();
                            writer.WritePropertyName("NextPageStart");
                            writer.WriteValue(nextPageStart);
                            writer.WriteEndObject();
                            writer.Flush();
                            bufferStream.Flush();
                        }
            }
            finally
            {
                CurrentOperationContext.Headers.Value = old;
                CurrentOperationContext.User.Value    = oldUser;
            }
        }
예제 #15
0
        private void StreamToClient(long id, SubscriptionActions subscriptions, Stream stream)
        {
            var sentDocuments = false;

            using (var streamWriter = new StreamWriter(stream))
                using (var writer = new JsonTextWriter(streamWriter))
                {
                    var options = subscriptions.GetBatchOptions(id);

                    writer.WriteStartObject();
                    writer.WritePropertyName("Results");
                    writer.WriteStartArray();

                    using (var cts = new CancellationTokenSource())
                        using (var timeout = cts.TimeoutAfter(DatabasesLandlord.SystemConfiguration.DatabaseOperationTimeout))
                        {
                            Etag lastProcessedDocEtag = null;
                            var  batchSize            = 0;
                            var  batchDocCount        = 0;
                            var  hasMoreDocs          = false;

                            var config    = subscriptions.GetSubscriptionConfig(id);
                            var startEtag = config.AckEtag;
                            var criteria  = config.Criteria;

                            do
                            {
                                Database.TransactionalStorage.Batch(accessor =>
                                {
                                    // we may be sending a LOT of documents to the user, and most
                                    // of them aren't going to be relevant for other ops, so we are going to skip
                                    // the cache for that, to avoid filling it up very quickly
                                    using (DocumentCacher.SkipSettingDocumentsInDocumentCache())
                                    {
                                        Database.Documents.GetDocuments(-1, options.MaxDocCount - batchDocCount, startEtag, cts.Token, doc =>
                                        {
                                            timeout.Delay();

                                            if (options.MaxSize.HasValue && batchSize >= options.MaxSize)
                                            {
                                                return;
                                            }

                                            if (batchDocCount >= options.MaxDocCount)
                                            {
                                                return;
                                            }

                                            lastProcessedDocEtag = doc.Etag;

                                            if (doc.Key.StartsWith("Raven/", StringComparison.InvariantCultureIgnoreCase))
                                            {
                                                return;
                                            }

                                            if (MatchCriteria(criteria, doc) == false)
                                            {
                                                return;
                                            }

                                            doc.ToJson().WriteTo(writer);
                                            writer.WriteRaw(Environment.NewLine);

                                            batchSize += doc.SerializedSizeOnDisk;
                                            batchDocCount++;
                                        });
                                    }

                                    if (lastProcessedDocEtag == null)
                                    {
                                        hasMoreDocs = false;
                                    }
                                    else
                                    {
                                        var lastDocEtag = accessor.Staleness.GetMostRecentDocumentEtag();
                                        hasMoreDocs     = EtagUtil.IsGreaterThan(lastDocEtag, lastProcessedDocEtag);

                                        startEtag = lastProcessedDocEtag;
                                    }
                                });
                            } while (hasMoreDocs && batchDocCount < options.MaxDocCount && (options.MaxSize.HasValue == false || batchSize < options.MaxSize));

                            writer.WriteEndArray();

                            if (batchDocCount > 0)
                            {
                                writer.WritePropertyName("LastProcessedEtag");
                                writer.WriteValue(lastProcessedDocEtag.ToString());

                                sentDocuments = true;
                            }

                            writer.WriteEndObject();
                            writer.Flush();
                        }
                }

            if (sentDocuments)
            {
                subscriptions.UpdateBatchSentTime(id);
            }
        }
        void Delete()
        {
            var currentTime = SystemTime.UtcNow;
            var currentExpiryThresholdTime = currentTime.AddHours(-Settings.HoursToKeepMessagesBeforeExpiring);

            logger.Debug("Trying to find expired documents to delete (with threshold {0})", currentExpiryThresholdTime.ToString(Default.DateTimeFormatsToWrite, CultureInfo.InvariantCulture));
            const string queryString = "Status:3 OR Status:4";
            var          query       = new IndexQuery
            {
                Start         = 0,
                PageSize      = deletionBatchSize,
                Cutoff        = currentTime,
                Query         = queryString,
                FieldsToFetch = new[]
                {
                    "__document_id",
                    "ProcessedAt"
                },
                SortedFields = new[]
                {
                    new SortedField("ProcessedAt")
                    {
                        Field      = "ProcessedAt",
                        Descending = false
                    }
                },
            };

            try
            {
                var docsToExpire = 0;
                // we may be receiving a LOT of documents to delete, so we are going to skip
                // the cache for that, to avoid filling it up very quickly
                var stopwatch = Stopwatch.StartNew();
                int deletionCount;
                using (DocumentCacher.SkipSettingDocumentsInDocumentCache())
                    using (Database.DisableAllTriggersForCurrentThread())
                        using (var cts = new CancellationTokenSource())
                        {
                            var documentWithCurrentThresholdTimeReached = false;
                            var items = new List <ICommandData>(deletionBatchSize);
                            try
                            {
                                Database.Query(indexName, query, CancellationTokenSource.CreateLinkedTokenSource(Database.WorkContext.CancellationToken, cts.Token).Token,
                                               null,
                                               doc =>
                                {
                                    if (documentWithCurrentThresholdTimeReached)
                                    {
                                        return;
                                    }

                                    if (doc.Value <DateTime>("ProcessedAt") >= currentExpiryThresholdTime)
                                    {
                                        documentWithCurrentThresholdTimeReached = true;
                                        cts.Cancel();
                                        return;
                                    }

                                    var id = doc.Value <string>("__document_id");
                                    if (!string.IsNullOrEmpty(id))
                                    {
                                        items.Add(new DeleteCommandData
                                        {
                                            Key = id
                                        });
                                    }
                                });
                            }
                            catch (OperationCanceledException)
                            {
                                //Ignore
                            }

                            logger.Debug("Batching deletion of {0} documents.", items.Count);

                            docsToExpire += items.Count;
                            var results = Database.Batch(items.ToArray());
                            deletionCount = results.Count(x => x.Deleted == true);
                            items.Clear();
                        }

                if (docsToExpire == 0)
                {
                    logger.Debug("No expired documents found");
                }
                else
                {
                    logger.Debug("Deleted {0} out of {1} expired documents batch - Execution time:{2}ms", deletionCount, docsToExpire, stopwatch.ElapsedMilliseconds);
                }
            }
            catch (Exception e)
            {
                logger.ErrorException("Error when trying to find expired documents", e);
            }
        }
예제 #17
0
        private List <JsonDocument> GetJsonDocsFromDisk(Etag etag, Etag untilEtag, Reference <bool> earlyExit = null)
        {
            List <JsonDocument> jsonDocs = null;

            // We take an snapshot because the implementation of accessing Values from a ConcurrentDictionary involves a lock.
            // Taking the snapshot should be safe enough.
            var currentlyUsedBatchSizesInBytes = Size.Sum(autoTuner.CurrentlyUsedBatchSizesInBytes.Values);

            using (DocumentCacher.SkipSetDocumentsInDocumentCache())
                context.TransactionalStorage.Batch(actions =>
                {
                    //limit how much data we load from disk --> better adhere to memory limits
                    var totalSizeAllowedToLoadInBytes =
                        (context.Configuration.Memory.DynamicLimitForProcessing) -
                        (prefetchingQueue.LoadedSize + currentlyUsedBatchSizesInBytes);

                    // at any rate, we will load a min of 512Kb docs
                    long maxSize = Size.Max(Size.Min(totalSizeAllowedToLoadInBytes, autoTuner.MaximumSizeAllowedToFetchFromStorage), minSizeToLoadDocs).GetValue(SizeUnit.Bytes);

                    var sp               = Stopwatch.StartNew();
                    var totalSize        = 0L;
                    var largestDocSize   = 0L;
                    string largestDocKey = null;
                    jsonDocs             = actions.Documents
                                           .GetDocumentsAfter(
                        etag,
                        autoTuner.NumberOfItemsToProcessInSingleBatch,
                        context.CancellationToken,
                        maxSize,
                        untilEtag,
                        autoTuner.FetchingDocumentsFromDiskTimeout,
                        earlyExit: earlyExit
                        )
                                           .Where(x => x != null)
                                           .Select(doc =>
                    {
                        if (largestDocSize < doc.SerializedSizeOnDisk)
                        {
                            largestDocSize = doc.SerializedSizeOnDisk;
                            largestDocKey  = doc.Key;
                        }

                        totalSize += doc.SerializedSizeOnDisk;
                        JsonDocument.EnsureIdInMetadata(doc);
                        return(doc);
                    })
                                           .ToList();

                    loadTimes.Enqueue(new DiskFetchPerformanceStats
                    {
                        LoadingTimeInMillseconds = sp.ElapsedMilliseconds,
                        NumberOfDocuments        = jsonDocs.Count,
                        TotalSize      = totalSize,
                        LargestDocSize = largestDocSize,
                        LargestDocKey  = largestDocKey
                    });
                    while (loadTimes.Count > 8)
                    {
                        DiskFetchPerformanceStats _;
                        loadTimes.TryDequeue(out _);
                    }
                });

            return(jsonDocs);
        }