public async Task CanGetByStackHashAsync() { long count = _cache.Count; long hits = _cache.Hits; long misses = _cache.Misses; var stack = await _repository.AddAsync(StackData.GenerateStack(id: TestConstants.StackId, projectId: TestConstants.ProjectId, organizationId: TestConstants.OrganizationId, dateFixed: SystemClock.UtcNow.SubtractMonths(1)), o => o.Cache()); Assert.NotNull(stack?.Id); Assert.Equal(count + 2, _cache.Count); Assert.Equal(hits, _cache.Hits); Assert.Equal(misses, _cache.Misses); var result = await _repository.GetStackBySignatureHashAsync(stack.ProjectId, stack.SignatureHash); Assert.Equal(stack.ToJson(), result.ToJson()); Assert.Equal(count + 2, _cache.Count); Assert.Equal(hits + 1, _cache.Hits); Assert.Equal(misses, _cache.Misses); }
public override async Task ProcessBatchAsync(ICollection <EventContext> contexts) { var stacks = new Dictionary <string, Tuple <bool, Stack> >(); foreach (var ctx in contexts) { if (String.IsNullOrEmpty(ctx.Event.StackId)) { // only add default signature info if no other signature info has been added if (ctx.StackSignatureData.Count == 0) { ctx.StackSignatureData.AddItemIfNotEmpty("Type", ctx.Event.Type); ctx.StackSignatureData.AddItemIfNotEmpty("Source", ctx.Event.Source); } string signatureHash = ctx.StackSignatureData.Values.ToSHA1(); ctx.SignatureHash = signatureHash; Tuple <bool, Stack> value; if (stacks.TryGetValue(signatureHash, out value)) { ctx.Stack = value.Item2; } else { ctx.Stack = await _stackRepository.GetStackBySignatureHashAsync(ctx.Event.ProjectId, signatureHash).AnyContext(); if (ctx.Stack != null) { stacks.Add(signatureHash, Tuple.Create(false, ctx.Stack)); } } if (ctx.Stack == null) { Logger.Trace().Message("Creating new event stack.").Write(); ctx.IsNew = true; string title = _formattingPluginManager.GetStackTitle(ctx.Event); var stack = new Stack { OrganizationId = ctx.Event.OrganizationId, ProjectId = ctx.Event.ProjectId, SignatureInfo = new SettingsDictionary(ctx.StackSignatureData), SignatureHash = signatureHash, Title = title?.Truncate(1000), Tags = ctx.Event.Tags ?? new TagSet(), Type = ctx.Event.Type, TotalOccurrences = 1, FirstOccurrence = ctx.Event.Date.UtcDateTime, LastOccurrence = ctx.Event.Date.UtcDateTime }; ctx.Stack = stack; stacks.Add(signatureHash, Tuple.Create(true, ctx.Stack)); } } else { ctx.Stack = await _stackRepository.GetByIdAsync(ctx.Event.StackId, true).AnyContext(); if (ctx.Stack == null || ctx.Stack.ProjectId != ctx.Event.ProjectId) { ctx.SetError("Invalid StackId."); continue; } ctx.SignatureHash = ctx.Stack.SignatureHash; if (!stacks.ContainsKey(ctx.Stack.SignatureHash)) { stacks.Add(ctx.Stack.SignatureHash, Tuple.Create(false, ctx.Stack)); } else { stacks[ctx.Stack.SignatureHash] = Tuple.Create(false, ctx.Stack); } } if (!ctx.IsNew && ctx.Event.Tags != null && ctx.Event.Tags.Count > 0) { if (ctx.Stack.Tags == null) { ctx.Stack.Tags = new TagSet(); } List <string> newTags = ctx.Event.Tags.Where(t => !ctx.Stack.Tags.Contains(t)).ToList(); if (newTags.Count > 0) { ctx.Stack.Tags.AddRange(newTags); // make sure the stack gets saved if (!stacks.ContainsKey(ctx.Stack.SignatureHash)) { stacks.Add(ctx.Stack.SignatureHash, Tuple.Create(true, ctx.Stack)); } else { stacks[ctx.Stack.SignatureHash] = Tuple.Create(true, stacks[ctx.Stack.SignatureHash].Item2); } } } ctx.Event.IsFirstOccurrence = ctx.IsNew; // sync the fixed and hidden flags to the error occurrence ctx.Event.IsFixed = ctx.Stack.DateFixed.HasValue; ctx.Event.IsHidden = ctx.Stack.IsHidden; } var stacksToAdd = stacks.Where(kvp => kvp.Value.Item1 && String.IsNullOrEmpty(kvp.Value.Item2.Id)).Select(kvp => kvp.Value.Item2).ToList(); if (stacksToAdd.Count > 0) { await _stackRepository.AddAsync(stacksToAdd, true, sendNotification : stacksToAdd.Count == 1).AnyContext(); if (stacksToAdd.Count > 1) { await _publisher.PublishAsync(new ExtendedEntityChanged { ChangeType = ChangeType.Added, Type = typeof(Stack).Name, OrganizationId = contexts.First().Organization.Id, ProjectId = contexts.First().Project.Id }).AnyContext(); } } var stacksToSave = stacks.Where(kvp => kvp.Value.Item1 && !String.IsNullOrEmpty(kvp.Value.Item2.Id)).Select(kvp => kvp.Value.Item2).ToList(); if (stacksToSave.Count > 0) { await _stackRepository.SaveAsync(stacksToSave, true, sendNotification : false).AnyContext(); // notification will get sent later in the update stats step } // Set stack ids after they have been saved and created contexts.ForEach(ctx => { ctx.Event.StackId = ctx.Stack?.Id; }); }
public override async Task ProcessBatchAsync(ICollection <EventContext> contexts) { var stacks = new Dictionary <string, Tuple <bool, Stack> >(); foreach (var ctx in contexts) { if (String.IsNullOrEmpty(ctx.Event.StackId)) { // only add default signature info if no other signature info has been added if (ctx.StackSignatureData.Count == 0) { ctx.StackSignatureData.AddItemIfNotEmpty("Type", ctx.Event.Type); ctx.StackSignatureData.AddItemIfNotEmpty("Source", ctx.Event.Source); } string signatureHash = ctx.StackSignatureData.Values.ToSHA1(); ctx.SignatureHash = signatureHash; if (stacks.TryGetValue(signatureHash, out var value)) { ctx.Stack = value.Item2; } else { ctx.Stack = await _stackRepository.GetStackBySignatureHashAsync(ctx.Event.ProjectId, signatureHash).AnyContext(); if (ctx.Stack != null) { stacks.Add(signatureHash, Tuple.Create(false, ctx.Stack)); } } if (ctx.Stack == null) { _logger.LogTrace("Creating new event stack."); ctx.IsNew = true; string title = _formattingPluginManager.GetStackTitle(ctx.Event); var stack = new Stack { OrganizationId = ctx.Event.OrganizationId, ProjectId = ctx.Event.ProjectId, SignatureInfo = new SettingsDictionary(ctx.StackSignatureData), SignatureHash = signatureHash, DuplicateSignature = ctx.Event.ProjectId + ":" + signatureHash, Title = title?.Truncate(1000), Tags = ctx.Event.Tags ?? new TagSet(), Type = ctx.Event.Type, TotalOccurrences = 1, FirstOccurrence = ctx.Event.Date.UtcDateTime, LastOccurrence = ctx.Event.Date.UtcDateTime }; if (ctx.Event.Type == Event.KnownTypes.Session) { stack.Status = StackStatus.Ignored; } ctx.Stack = stack; stacks.Add(signatureHash, Tuple.Create(true, ctx.Stack)); } } else { ctx.Stack = await _stackRepository.GetByIdAsync(ctx.Event.StackId, o => o.Cache()).AnyContext(); if (ctx.Stack == null || ctx.Stack.ProjectId != ctx.Event.ProjectId) { ctx.SetError("Invalid StackId."); continue; } ctx.SignatureHash = ctx.Stack.SignatureHash; if (!stacks.ContainsKey(ctx.Stack.SignatureHash)) { stacks.Add(ctx.Stack.SignatureHash, Tuple.Create(false, ctx.Stack)); } else { stacks[ctx.Stack.SignatureHash] = Tuple.Create(false, ctx.Stack); } if (ctx.Stack.Status == StackStatus.Discarded) { ctx.IsDiscarded = true; ctx.IsCancelled = true; } } if (!ctx.IsNew && ctx.Event.Tags != null && ctx.Event.Tags.Count > 0) { if (ctx.Stack.Tags == null) { ctx.Stack.Tags = new TagSet(); } var newTags = ctx.Event.Tags.Where(t => !ctx.Stack.Tags.Contains(t)).ToList(); if (newTags.Count > 0 || ctx.Stack.Tags.Count > 50 || ctx.Stack.Tags.Any(t => t.Length > 100)) { ctx.Stack.Tags.AddRange(newTags); ctx.Stack.Tags.RemoveExcessTags(); // make sure the stack gets saved if (!stacks.ContainsKey(ctx.Stack.SignatureHash)) { stacks.Add(ctx.Stack.SignatureHash, Tuple.Create(true, ctx.Stack)); } else { stacks[ctx.Stack.SignatureHash] = Tuple.Create(true, stacks[ctx.Stack.SignatureHash].Item2); } } } ctx.Event.IsFirstOccurrence = ctx.IsNew; } var stacksToAdd = stacks.Where(kvp => kvp.Value.Item1 && String.IsNullOrEmpty(kvp.Value.Item2.Id)).Select(kvp => kvp.Value.Item2).ToList(); if (stacksToAdd.Count > 0) { await _stackRepository.AddAsync(stacksToAdd, o => o.Cache().Notifications(stacksToAdd.Count == 1)).AnyContext(); if (stacksToAdd.Count > 1) { await _publisher.PublishAsync(new EntityChanged { ChangeType = ChangeType.Added, Type = StackTypeName, Data = { { ExtendedEntityChanged.KnownKeys.OrganizationId, contexts.First().Organization.Id }, { ExtendedEntityChanged.KnownKeys.ProjectId, contexts.First().Project.Id } } }).AnyContext(); } } var stacksToSave = stacks.Where(kvp => kvp.Value.Item1 && !String.IsNullOrEmpty(kvp.Value.Item2.Id)).Select(kvp => kvp.Value.Item2).ToList(); if (stacksToSave.Count > 0) { await _stackRepository.SaveAsync(stacksToSave, o => o.Cache().Notifications(false)).AnyContext(); // notification will get sent later in the update stats step } // Set stack ids after they have been saved and created contexts.ForEach(ctx => { ctx.Event.StackId = ctx.Stack?.Id; }); }