public async Task IncrementEventCounterTestAsync() { await RemoveDataAsync(); await _repository.AddAsync(StackData.GenerateStack(id: TestConstants.StackId, projectId: TestConstants.ProjectId, organizationId: TestConstants.OrganizationId)); await _client.RefreshAsync(); var stack = await _repository.GetByIdAsync(TestConstants.StackId); Assert.NotNull(stack); Assert.Equal(0, stack.TotalOccurrences); Assert.Equal(DateTime.MinValue, stack.FirstOccurrence); Assert.Equal(DateTime.MinValue, stack.LastOccurrence); var utcNow = DateTime.UtcNow; await _repository.IncrementEventCounterAsync(TestConstants.OrganizationId, TestConstants.ProjectId, TestConstants.StackId, utcNow, utcNow, 1); await _client.RefreshAsync(); stack = await _repository.GetByIdAsync(TestConstants.StackId); Assert.Equal(1, stack.TotalOccurrences); Assert.Equal(utcNow, stack.FirstOccurrence); Assert.Equal(utcNow, stack.LastOccurrence); await _repository.IncrementEventCounterAsync(TestConstants.OrganizationId, TestConstants.ProjectId, TestConstants.StackId, utcNow.SubtractDays(1), utcNow.SubtractDays(1), 1); await _client.RefreshAsync(); stack = await _repository.GetByIdAsync(TestConstants.StackId); Assert.Equal(2, stack.TotalOccurrences); Assert.Equal(utcNow.SubtractDays(1), stack.FirstOccurrence); Assert.Equal(utcNow, stack.LastOccurrence); await _repository.IncrementEventCounterAsync(TestConstants.OrganizationId, TestConstants.ProjectId, TestConstants.StackId, utcNow.AddDays(1), utcNow.AddDays(1), 1); await _client.RefreshAsync(); stack = await _repository.GetByIdAsync(TestConstants.StackId); Assert.Equal(3, stack.TotalOccurrences); Assert.Equal(utcNow.SubtractDays(1), stack.FirstOccurrence); Assert.Equal(utcNow.AddDays(1), stack.LastOccurrence); }
public async Task CanIncrementEventCounterAsync() { var stack = await _repository.AddAsync(StackData.GenerateStack(projectId: TestConstants.ProjectId, organizationId: TestConstants.OrganizationId), o => o.ImmediateConsistency()); Assert.NotNull(stack); Assert.Equal(0, stack.TotalOccurrences); Assert.True(stack.FirstOccurrence <= SystemClock.UtcNow); Assert.True(stack.LastOccurrence <= SystemClock.UtcNow); Assert.NotEqual(DateTime.MinValue, stack.CreatedUtc); Assert.NotEqual(DateTime.MinValue, stack.UpdatedUtc); Assert.Equal(stack.CreatedUtc, stack.UpdatedUtc); var updatedUtc = stack.UpdatedUtc; var utcNow = SystemClock.UtcNow; await _repository.IncrementEventCounterAsync(TestConstants.OrganizationId, TestConstants.ProjectId, stack.Id, utcNow, utcNow, 1); stack = await _repository.GetByIdAsync(stack.Id); Assert.Equal(1, stack.TotalOccurrences); Assert.Equal(utcNow, stack.FirstOccurrence); Assert.Equal(utcNow, stack.LastOccurrence); Assert.Equal(updatedUtc, stack.CreatedUtc); Assert.True(updatedUtc.IsBefore(stack.UpdatedUtc), $"Previous {updatedUtc}, Current: {stack.UpdatedUtc}"); await _repository.IncrementEventCounterAsync(TestConstants.OrganizationId, TestConstants.ProjectId, stack.Id, utcNow.SubtractDays(1), utcNow.SubtractDays(1), 1); stack = await _repository.GetByIdAsync(stack.Id); Assert.Equal(2, stack.TotalOccurrences); Assert.Equal(utcNow.SubtractDays(1), stack.FirstOccurrence); Assert.Equal(utcNow, stack.LastOccurrence); await _repository.IncrementEventCounterAsync(TestConstants.OrganizationId, TestConstants.ProjectId, stack.Id, utcNow.AddDays(1), utcNow.AddDays(1), 1); stack = await _repository.GetByIdAsync(stack.Id); Assert.Equal(3, stack.TotalOccurrences); Assert.Equal(utcNow.SubtractDays(1), stack.FirstOccurrence); Assert.Equal(utcNow.AddDays(1), stack.LastOccurrence); }
public override async Task ProcessBatchAsync(ICollection <EventContext> contexts) { var stacks = contexts.Where(c => !c.IsNew).GroupBy(c => c.Event.StackId); foreach (var stackGroup in stacks) { var stackContexts = stackGroup.ToList(); try { int count = stackContexts.Count; DateTime minDate = stackContexts.Min(s => s.Event.Date.UtcDateTime); DateTime maxDate = stackContexts.Max(s => s.Event.Date.UtcDateTime); await _stackRepository.IncrementEventCounterAsync(stackContexts[0].Event.OrganizationId, stackContexts[0].Event.ProjectId, stackGroup.Key, minDate, maxDate, count).AnyContext(); // Update stacks in memory since they are used in notifications. foreach (var ctx in stackContexts) { if (ctx.Stack.FirstOccurrence > minDate) { ctx.Stack.FirstOccurrence = minDate; } if (ctx.Stack.LastOccurrence < maxDate) { ctx.Stack.LastOccurrence = maxDate; } ctx.Stack.TotalOccurrences += count; } } catch (Exception ex) { foreach (var context in stackContexts) { bool cont = false; try { cont = HandleError(ex, context); } catch {} if (!cont) { context.SetError(ex.Message, ex); } } } } }
public async Task SaveStackUsagesAsync(bool sendNotifications = true, CancellationToken cancellationToken = default) { string occurrenceSetCacheKey = GetStackOccurrenceSetCacheKey(); var stackUsageSet = await _cache.GetListAsync <(string OrganizationId, string ProjectId, string StackId)>(occurrenceSetCacheKey).AnyContext(); if (!stackUsageSet.HasValue) { return; } foreach (var(organizationId, projectId, stackId) in stackUsageSet.Value) { if (cancellationToken.IsCancellationRequested) { break; } var removeFromSetTask = _cache.ListRemoveAsync(occurrenceSetCacheKey, (organizationId, projectId, stackId)); string countCacheKey = GetStackOccurrenceCountCacheKey(stackId); var countTask = _cache.GetAsync <long>(countCacheKey, 0); string minDateCacheKey = GetStackOccurrenceMinDateCacheKey(stackId); var minDateTask = _cache.GetUnixTimeMillisecondsAsync(minDateCacheKey, SystemClock.UtcNow); string maxDateCacheKey = GetStackOccurrenceMaxDateCacheKey(stackId); var maxDateTask = _cache.GetUnixTimeMillisecondsAsync(maxDateCacheKey, SystemClock.UtcNow); await Task.WhenAll( removeFromSetTask, countTask, minDateTask, maxDateTask ).AnyContext(); int occurrenceCount = (int)countTask.Result; if (occurrenceCount <= 0) { await _cache.RemoveAllAsync(new[] { minDateCacheKey, maxDateCacheKey }).AnyContext(); continue; } await Task.WhenAll( _cache.RemoveAllAsync(new[] { minDateCacheKey, maxDateCacheKey }), _cache.DecrementAsync(countCacheKey, occurrenceCount, _expireTimeout) ).AnyContext(); var occurrenceMinDate = minDateTask.Result; var occurrenceMaxDate = maxDateTask.Result; bool shouldRetry = false; try { if (!await _stackRepository.IncrementEventCounterAsync(organizationId, projectId, stackId, occurrenceMinDate, occurrenceMaxDate, occurrenceCount, sendNotifications).AnyContext()) { shouldRetry = true; await IncrementStackUsageAsync(organizationId, projectId, stackId, occurrenceMinDate, occurrenceMaxDate, occurrenceCount).AnyContext(); } else { _logger.LogTrace("Increment event count {OccurrenceCount} for organization:{OrganizationId} project:{ProjectId} stack:{StackId} with Min Date:{OccurrenceMinDate} Max Date:{OccurrenceMaxDate}", occurrenceCount, organizationId, projectId, stackId, occurrenceMinDate, occurrenceMaxDate); } } catch (Exception ex) { _logger.LogError(ex, "Error incrementing event count for organization: {OrganizationId} project:{ProjectId} stack:{StackId}", organizationId, projectId, stackId); if (!shouldRetry) { await IncrementStackUsageAsync(organizationId, projectId, stackId, occurrenceMinDate, occurrenceMaxDate, occurrenceCount).AnyContext(); } } } }