| | | 1 | | using System.Collections.Concurrent; |
| | | 2 | | |
| | | 3 | | namespace Services.SearchServices.OrderSearch; |
| | | 4 | | |
| | | 5 | | /// <summary>In-memory журнал индексации и поисковых запросов (singleton на процесс приложения).</summary> |
| | | 6 | | public sealed class OrderSearchDiagnosticsStore : IOrderSearchDiagnosticsStore |
| | | 7 | | { |
| | | 8 | | private const int MaxIndexEvents = 400; |
| | | 9 | | private const int MaxSearchEvents = 200; |
| | | 10 | | private const int MaxDriftEvents = 100; |
| | | 11 | | |
| | 74 | 12 | | private readonly ConcurrentQueue<OrderSearchIndexEvent> _indexEvents = new(); |
| | 74 | 13 | | private readonly ConcurrentQueue<OrderSearchQueryEvent> _searchEvents = new(); |
| | 74 | 14 | | private readonly ConcurrentQueue<OrderSearchDriftEvent> _driftEvents = new(); |
| | | 15 | | private int _indexCount; |
| | | 16 | | private int _searchCount; |
| | | 17 | | private int _driftCount; |
| | | 18 | | |
| | | 19 | | public void RecordIndex(OrderSearchIndexOperation operation, int count, long durationMs, bool success, |
| | | 20 | | string message, int? orderId = null) |
| | | 21 | | { |
| | 4 | 22 | | _indexEvents.Enqueue(new OrderSearchIndexEvent |
| | 4 | 23 | | { |
| | 4 | 24 | | Operation = operation, |
| | 4 | 25 | | OrderId = orderId, |
| | 4 | 26 | | Count = count, |
| | 4 | 27 | | DurationMs = durationMs, |
| | 4 | 28 | | Success = success, |
| | 4 | 29 | | Message = message |
| | 4 | 30 | | }); |
| | 4 | 31 | | Trim(_indexEvents, ref _indexCount, MaxIndexEvents); |
| | 4 | 32 | | } |
| | | 33 | | |
| | | 34 | | public void RecordSearch(string? searchText, string filterSummary, long durationMs, int resultCount, |
| | | 35 | | bool usedFallback, string source = "Orders", long? totalHits = null, bool truncated = false, |
| | | 36 | | bool isLargeResult = false) |
| | | 37 | | { |
| | 19 | 38 | | _searchEvents.Enqueue(new OrderSearchQueryEvent |
| | 19 | 39 | | { |
| | 19 | 40 | | SearchText = searchText, |
| | 19 | 41 | | FilterSummary = filterSummary, |
| | 19 | 42 | | DurationMs = durationMs, |
| | 19 | 43 | | ResultCount = resultCount, |
| | 19 | 44 | | TotalHits = totalHits, |
| | 19 | 45 | | Truncated = truncated, |
| | 19 | 46 | | IsLargeResult = isLargeResult, |
| | 19 | 47 | | UsedFallback = usedFallback, |
| | 19 | 48 | | Source = source |
| | 19 | 49 | | }); |
| | 19 | 50 | | Trim(_searchEvents, ref _searchCount, MaxSearchEvents); |
| | 19 | 51 | | } |
| | | 52 | | |
| | | 53 | | public void RecordDrift(OrderSearchDriftEvent driftEvent) |
| | | 54 | | { |
| | 9 | 55 | | _driftEvents.Enqueue(driftEvent); |
| | 9 | 56 | | Trim(_driftEvents, ref _driftCount, MaxDriftEvents); |
| | 9 | 57 | | } |
| | | 58 | | |
| | | 59 | | public IReadOnlyList<OrderSearchDriftEvent> GetDriftEvents(int take = 30) => |
| | 11 | 60 | | _driftEvents.Reverse().Take(take).ToList(); |
| | | 61 | | |
| | | 62 | | public OrderSearchDriftEvent? GetLastDriftEvent() => |
| | 11 | 63 | | _driftEvents.Reverse().FirstOrDefault(); |
| | | 64 | | |
| | | 65 | | public IReadOnlyList<OrderSearchIndexEvent> GetIndexEvents(int take = 80) => |
| | 10 | 66 | | _indexEvents.Reverse().Take(take).ToList(); |
| | | 67 | | |
| | | 68 | | public IReadOnlyList<OrderSearchQueryEvent> GetSearchEvents(int take = 50) => |
| | 14 | 69 | | _searchEvents.Reverse().Take(take).ToList(); |
| | | 70 | | |
| | | 71 | | public (double AvgMs, long MaxMs, int Count) GetSearchStats() |
| | | 72 | | { |
| | 11 | 73 | | var items = _searchEvents.ToArray(); |
| | 11 | 74 | | if (items.Length == 0) |
| | | 75 | | { |
| | 8 | 76 | | return (0, 0, 0); |
| | | 77 | | } |
| | | 78 | | |
| | 9 | 79 | | var avg = items.Average(e => e.DurationMs); |
| | 9 | 80 | | var max = items.Max(e => e.DurationMs); |
| | 3 | 81 | | return (avg, max, items.Length); |
| | | 82 | | } |
| | | 83 | | |
| | | 84 | | private static void Trim<T>(ConcurrentQueue<T> queue, ref int counter, int max) |
| | | 85 | | { |
| | 32 | 86 | | var n = Interlocked.Increment(ref counter); |
| | 32 | 87 | | while (n > max && queue.TryDequeue(out _)) |
| | | 88 | | { |
| | 0 | 89 | | n = Interlocked.Decrement(ref counter); |
| | | 90 | | } |
| | 32 | 91 | | } |
| | | 92 | | } |