2024-08-02 06:56:49 +00:00
|
|
|
|
using Logging;
|
|
|
|
|
using Newtonsoft.Json;
|
2024-08-01 12:50:25 +00:00
|
|
|
|
|
|
|
|
|
namespace OverwatchTranscript
|
|
|
|
|
{
|
2024-08-02 12:25:38 +00:00
|
|
|
|
public class EventBucketWriter
|
2024-08-01 12:50:25 +00:00
|
|
|
|
{
|
2024-08-02 08:44:15 +00:00
|
|
|
|
private const int MaxBuffer = 1000;
|
2024-08-01 12:50:25 +00:00
|
|
|
|
|
|
|
|
|
private readonly object _lock = new object();
|
|
|
|
|
private bool closed = false;
|
2024-08-02 06:56:49 +00:00
|
|
|
|
private readonly ILog log;
|
2024-08-01 12:50:25 +00:00
|
|
|
|
private readonly string bucketFile;
|
2024-08-02 08:44:15 +00:00
|
|
|
|
private readonly int maxCount;
|
2024-08-01 12:50:25 +00:00
|
|
|
|
private readonly List<EventBucketEntry> buffer = new List<EventBucketEntry>();
|
|
|
|
|
|
2024-08-02 12:25:38 +00:00
|
|
|
|
public EventBucketWriter(ILog log, string bucketFile, int maxCount)
|
2024-08-01 12:50:25 +00:00
|
|
|
|
{
|
2024-08-02 06:56:49 +00:00
|
|
|
|
this.log = log;
|
2024-08-01 12:50:25 +00:00
|
|
|
|
this.bucketFile = bucketFile;
|
2024-08-02 08:44:15 +00:00
|
|
|
|
this.maxCount = maxCount;
|
2024-08-01 12:50:25 +00:00
|
|
|
|
if (File.Exists(bucketFile)) throw new Exception("Already exists");
|
|
|
|
|
|
2024-08-02 12:25:38 +00:00
|
|
|
|
log.Debug("Write Bucket open: " + bucketFile);
|
2024-08-01 12:50:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int Count { get; private set; }
|
|
|
|
|
public bool IsFull { get; private set; }
|
|
|
|
|
|
|
|
|
|
public void Add(DateTime utc, object payload)
|
|
|
|
|
{
|
2024-08-01 14:25:28 +00:00
|
|
|
|
lock (_lock)
|
|
|
|
|
{
|
|
|
|
|
if (closed) throw new Exception("Already closed");
|
|
|
|
|
AddToBuffer(utc, payload);
|
2024-08-02 06:56:49 +00:00
|
|
|
|
BufferToFile(emptyBuffer: false);
|
2024-08-01 14:25:28 +00:00
|
|
|
|
}
|
2024-08-01 12:50:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-08-01 14:25:28 +00:00
|
|
|
|
public IFinalizedBucket FinalizeBucket()
|
2024-08-01 12:50:25 +00:00
|
|
|
|
{
|
|
|
|
|
lock (_lock)
|
|
|
|
|
{
|
2024-08-01 14:25:28 +00:00
|
|
|
|
closed = true;
|
2024-08-02 06:56:49 +00:00
|
|
|
|
BufferToFile(emptyBuffer: true);
|
2024-08-01 12:50:25 +00:00
|
|
|
|
SortFileByTimestamps();
|
|
|
|
|
}
|
2024-08-02 06:56:49 +00:00
|
|
|
|
log.Debug($"Finalized bucket with {Count} entries");
|
2024-08-02 12:25:38 +00:00
|
|
|
|
return new EventBucketReader(log, bucketFile);
|
2024-08-01 12:50:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-08-02 06:56:49 +00:00
|
|
|
|
public override string ToString()
|
|
|
|
|
{
|
|
|
|
|
return $"EventBucket: " + Count;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-01 12:50:25 +00:00
|
|
|
|
private void AddToBuffer(DateTime utc, object payload)
|
|
|
|
|
{
|
|
|
|
|
var typeName = payload.GetType().FullName;
|
2024-08-01 14:25:28 +00:00
|
|
|
|
if (string.IsNullOrEmpty(typeName)) throw new Exception("Empty typename for payload");
|
|
|
|
|
if (utc == default) throw new Exception("DateTimeUtc not set");
|
2024-08-01 12:50:25 +00:00
|
|
|
|
|
|
|
|
|
var entry = new EventBucketEntry
|
|
|
|
|
{
|
|
|
|
|
Utc = utc,
|
|
|
|
|
Event = new OverwatchEvent
|
|
|
|
|
{
|
|
|
|
|
Type = typeName,
|
2024-08-21 07:51:22 +00:00
|
|
|
|
Payload = Json.Serialize(payload)
|
2024-08-01 12:50:25 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Count++;
|
2024-08-02 08:44:15 +00:00
|
|
|
|
IsFull = Count > maxCount;
|
2024-08-01 12:50:25 +00:00
|
|
|
|
|
|
|
|
|
buffer.Add(entry);
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-02 06:56:49 +00:00
|
|
|
|
private void BufferToFile(bool emptyBuffer)
|
2024-08-01 14:25:28 +00:00
|
|
|
|
{
|
2024-08-02 06:56:49 +00:00
|
|
|
|
if (emptyBuffer || buffer.Count > MaxBuffer)
|
2024-08-01 14:25:28 +00:00
|
|
|
|
{
|
|
|
|
|
using var file = File.Open(bucketFile, FileMode.Append);
|
|
|
|
|
using var writer = new StreamWriter(file);
|
|
|
|
|
foreach (var entry in buffer)
|
|
|
|
|
{
|
2024-08-21 07:51:22 +00:00
|
|
|
|
writer.WriteLine(Json.Serialize(entry));
|
2024-08-01 14:25:28 +00:00
|
|
|
|
}
|
2024-08-02 06:56:49 +00:00
|
|
|
|
log.Debug($"Bucket wrote {buffer.Count} entries to file.");
|
2024-08-01 14:25:28 +00:00
|
|
|
|
buffer.Clear();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-01 12:50:25 +00:00
|
|
|
|
private void SortFileByTimestamps()
|
|
|
|
|
{
|
|
|
|
|
var lines = File.ReadAllLines(bucketFile);
|
2024-08-21 07:51:22 +00:00
|
|
|
|
var entries = lines.Select(Json.Deserialize<EventBucketEntry>)
|
2024-08-01 12:50:25 +00:00
|
|
|
|
.Cast<EventBucketEntry>()
|
|
|
|
|
.OrderBy(e => e.Utc)
|
|
|
|
|
.ToArray();
|
|
|
|
|
|
|
|
|
|
File.Delete(bucketFile);
|
2024-08-21 07:51:22 +00:00
|
|
|
|
File.WriteAllLines(bucketFile, entries.Select(e => Json.Serialize(e)));
|
2024-08-01 12:50:25 +00:00
|
|
|
|
}
|
2024-08-01 14:25:28 +00:00
|
|
|
|
}
|
2024-08-01 12:50:25 +00:00
|
|
|
|
|
|
|
|
|
[Serializable]
|
|
|
|
|
public class EventBucketEntry
|
|
|
|
|
{
|
|
|
|
|
public DateTime Utc { get; set; }
|
|
|
|
|
public OverwatchEvent Event { get; set; } = new();
|
|
|
|
|
}
|
|
|
|
|
}
|