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 }