public void If_execution_takes_longer_than_period_it_triggers_next_execution_immediately_after_previous()
 {
     var counter = 0;
     var failure = false;
     var lastEndTime = DateTime.MinValue;
     var @event = new ManualResetEventSlim(false);
     var delay = TimeSpan.Zero;
     var executor = new PeriodicExecutor(() =>
     {
         delay = DateTime.Now - lastEndTime;
         if (lastEndTime != DateTime.MinValue && delay > TimeSpan.FromMilliseconds(100))
         {
             @event.Set();
             failure = true;
             return;
         }
         counter++;
         Thread.Sleep(2000);
         lastEndTime = DateTime.Now;
         if (counter == 2)
         {
             @event.Set();
         }
     }, TimeSpan.FromSeconds(1));
     executor.Start(true);
     @event.Wait();
     executor.Stop(CancellationToken.None);
     Assert.IsFalse(failure, string.Format("Time between finishing previous execution and starting this longer than {0} ms",delay));
 }
 public void Can_shutdown_while_waiting()
 {
     var @event = new ManualResetEventSlim(false);
     var executor = new PeriodicExecutor(@event.Set, TimeSpan.FromSeconds(10000));
     executor.Start(false);
     @event.Wait();
     Thread.Sleep(1000);
     executor.Stop(CancellationToken.None);
     Assert.Pass();
 }
        public void Execute(DocumentDatabase database)
        {
            Database = database;
            indexName = new ExpiryProcessedMessageIndex().IndexName;

            deletionBatchSize = Settings.ExpirationProcessBatchSize;
            deleteFrequencyInSeconds = Settings.ExpirationProcessTimerInSeconds;

            if (deleteFrequencyInSeconds == 0)
            {
                return;
            }

            logger.Info("Expired Documents every {0} seconds", deleteFrequencyInSeconds);
            logger.Info("Deletion Batch Size: {0}", deletionBatchSize);
            logger.Info("Retention Period: {0}", Settings.HoursToKeepMessagesBeforeExpiring);

            timer = new PeriodicExecutor(Delete,TimeSpan.FromSeconds(deleteFrequencyInSeconds));
            timer.Start(true);
        }
        public void Execute(DocumentDatabase database)
        {
            Database  = database;
            indexName = new ExpiryProcessedMessageIndex().IndexName;

            deletionBatchSize        = Settings.ExpirationProcessBatchSize;
            deleteFrequencyInSeconds = Settings.ExpirationProcessTimerInSeconds;

            if (deleteFrequencyInSeconds == 0)
            {
                return;
            }

            logger.Info("Expired Documents every {0} seconds", deleteFrequencyInSeconds);
            logger.Info("Deletion Batch Size: {0}", deletionBatchSize);
            logger.Info("Retention Period: {0}", Settings.HoursToKeepMessagesBeforeExpiring);

            timer = new PeriodicExecutor(Delete, TimeSpan.FromSeconds(deleteFrequencyInSeconds));
            timer.Start(true);
        }
 public void If_execution_throws_it_does_not_kill_the_executor()
 {
     var first = true;
     var success = false;
     var @event = new ManualResetEventSlim(false);
     var executor = new PeriodicExecutor(() =>
     {
         if (first)
         {
             first = false;
             throw new Exception();
         }
         success = true;
         @event.Set();
     }, TimeSpan.FromSeconds(1));
     executor.Start(true);
     @event.Wait();
     executor.Stop(CancellationToken.None);
     Assert.IsTrue(success);
 }
        void Delete(PeriodicExecutor executor)
        {
            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",
                    "MessageMetadata"
                },
                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);
                    var attachments = new List<string>(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
                                });

                                var bodyNotStored = doc.SelectToken("MessageMetadata.BodyNotStored", errorWhenNoMatch: false);
                                if (bodyNotStored == null || bodyNotStored.Value<bool>() == false)
                                {
                                    var msgId = doc.SelectToken("MessageMetadata.MessageId", errorWhenNoMatch: false);
                                    if (msgId != null)
                                    {
                                        var attachmentId = "messagebodies/" + msgId.Value<string>();
                                        attachments.Add(attachmentId);
                                    }
                                }
                            }
                        });
                    }
                    catch (OperationCanceledException)
                    {
                        //Ignore
                    }

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

                    docsToExpire += items.Count;
                    var results = Database.Batch(items.ToArray());
                    Database.TransactionalStorage.Batch(accessor =>
                    {
                        foreach (var attach in attachments)
                        {
                            accessor.Attachments.DeleteAttachment(attach, null);
                        }
                    });
                    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);
            }
        }