/ Framework / OverwatchTranscript / EventBucketWriter.cs
EventBucketWriter.cs
  1  using Logging;
  2  using Newtonsoft.Json;
  3  
  4  namespace OverwatchTranscript
  5  {
  6      public class EventBucketWriter
  7      {
  8          private const int MaxBuffer = 1000;
  9  
 10          private readonly object _lock = new object();
 11          private bool closed = false;
 12          private readonly ILog log;
 13          private readonly string bucketFile;
 14          private readonly int maxCount;
 15          private readonly List<EventBucketEntry> buffer = new List<EventBucketEntry>();
 16  
 17          public EventBucketWriter(ILog log, string bucketFile, int maxCount)
 18          {
 19              this.log = log;
 20              this.bucketFile = bucketFile;
 21              this.maxCount = maxCount;
 22              if (File.Exists(bucketFile)) throw new Exception("Already exists");
 23  
 24              log.Debug("Write Bucket open: " + bucketFile);
 25          }
 26  
 27          public int Count { get; private set; }
 28          public bool IsFull { get; private set; }
 29  
 30          public void Add(DateTime utc, object payload)
 31          {
 32              lock (_lock)
 33              {
 34                  if (closed) throw new Exception("Already closed");
 35                  AddToBuffer(utc, payload);
 36                  BufferToFile(emptyBuffer: false);
 37              }
 38          }
 39  
 40          public IFinalizedBucket FinalizeBucket()
 41          {
 42              lock (_lock)
 43              {
 44                  closed = true;
 45                  BufferToFile(emptyBuffer: true);
 46                  SortFileByTimestamps();
 47              }
 48              log.Debug($"Finalized bucket with {Count} entries");
 49              return new EventBucketReader(log, bucketFile);
 50          }
 51  
 52          public override string ToString()
 53          {
 54              return $"EventBucket: " + Count;
 55          }
 56  
 57          private void AddToBuffer(DateTime utc, object payload)
 58          {  
 59              var typeName = payload.GetType().FullName;
 60              if (string.IsNullOrEmpty(typeName)) throw new Exception("Empty typename for payload");
 61              if (utc == default) throw new Exception("DateTimeUtc not set");
 62  
 63              var entry = new EventBucketEntry
 64              {
 65                  Utc = utc,
 66                  Event = new OverwatchEvent
 67                  {
 68                      Type = typeName,
 69                      Payload = Json.Serialize(payload)
 70                  }
 71              };
 72  
 73              Count++;
 74              IsFull = Count > maxCount;
 75  
 76              buffer.Add(entry);
 77          }
 78  
 79          private void BufferToFile(bool emptyBuffer)
 80          {
 81              if (emptyBuffer || buffer.Count > MaxBuffer)
 82              {
 83                  using var file = File.Open(bucketFile, FileMode.Append);
 84                  using var writer = new StreamWriter(file);
 85                  foreach (var entry in buffer)
 86                  {
 87                      writer.WriteLine(Json.Serialize(entry));
 88                  }
 89                  log.Debug($"Bucket wrote {buffer.Count} entries to file.");
 90                  buffer.Clear();
 91              }
 92          }
 93  
 94          private void SortFileByTimestamps()
 95          {
 96              var lines = File.ReadAllLines(bucketFile);
 97              var entries = lines.Select(Json.Deserialize<EventBucketEntry>)
 98                  .Cast<EventBucketEntry>()
 99                  .OrderBy(e => e.Utc)
100                  .ToArray();
101  
102              File.Delete(bucketFile);
103              File.WriteAllLines(bucketFile, entries.Select(e => Json.Serialize(e)));
104          }
105      }
106  
107      [Serializable]
108      public class EventBucketEntry
109      {
110          public DateTime Utc { get; set; }
111          public OverwatchEvent Event { get; set; } = new();
112      }
113  }