namespace NATS.Server.Raft; public sealed class RaftLog { private readonly List _entries = []; private long _baseIndex; public IReadOnlyList Entries => _entries; public RaftLogEntry Append(int term, string command) { var entry = new RaftLogEntry(_baseIndex + _entries.Count + 1, term, command); _entries.Add(entry); return entry; } public void AppendReplicated(RaftLogEntry entry) { if (_entries.Any(e => e.Index == entry.Index)) return; _entries.Add(entry); } public void ReplaceWithSnapshot(RaftSnapshot snapshot) { _entries.Clear(); _baseIndex = snapshot.LastIncludedIndex; } public async Task PersistAsync(string path, CancellationToken ct) { Directory.CreateDirectory(Path.GetDirectoryName(path)!); var model = new PersistedLog { BaseIndex = _baseIndex, Entries = [.. _entries], }; await File.WriteAllTextAsync(path, System.Text.Json.JsonSerializer.Serialize(model), ct); } public static async Task LoadAsync(string path, CancellationToken ct) { var log = new RaftLog(); if (!File.Exists(path)) return log; var json = await File.ReadAllTextAsync(path, ct); var model = System.Text.Json.JsonSerializer.Deserialize(json) ?? new PersistedLog(); log._baseIndex = model.BaseIndex; log._entries.AddRange(model.Entries); return log; } private sealed class PersistedLog { public long BaseIndex { get; set; } public List Entries { get; set; } = []; } } public sealed record RaftLogEntry(long Index, int Term, string Command);