Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions src/tail/checkpoint/lane.zig
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,38 @@ test "checkpoint/lane: corrupted snapshot falls back to wal replay" {
try testing.expectEqual(@as(?u64, 777), recovered.getOffset(id));
}

test "checkpoint/lane: corrupted snapshot count degrades to wal replay" {
var tmp = testing.tmpDir(.{});
defer tmp.cleanup();
const state_dir = try tmp.dir.realPathFileAlloc(testing.io, ".", testing.allocator);
defer testing.allocator.free(state_dir);

const id: tail_types.FileIdentity = .{ .dev = 30, .inode = 31, .fingerprint = 32 };
const value: checkpoint_types.Value = .{
.identity = id,
.offset = 888,
.last_seen_ns = @intCast(std.Io.Timestamp.now(testing.io, .awake).toNanoseconds()),
};

var snap = try snapshot_mod.Snapshot.init(testing.allocator, testing.io, state_dir);
defer snap.deinit();
var vals: [1]checkpoint_types.Value = .{value};
try snap.write(vals[0..]);

const snap_path = try std.fs.path.join(testing.allocator, &.{ state_dir, "checkpoint.snap" });
defer testing.allocator.free(snap_path);
try corruptByte(testing.io, snap_path, 15);

var wal = try wal_mod.Wal.init(testing.allocator, testing.io, state_dir);
defer wal.deinit();
try wal.append(1, value);
try wal.sync();

var recovered = try Lane.init(testing.allocator, testing.io, state_dir, 16, 64, 5, 72 * 60 * 60 * 1000, 64, 60_000);
defer recovered.deinit();
try testing.expectEqual(@as(?u64, 888), recovered.getOffset(id));
}

test "checkpoint/lane: missing state files initialize cleanly" {
var tmp = testing.tmpDir(.{});
defer tmp.cleanup();
Expand Down
9 changes: 8 additions & 1 deletion src/tail/checkpoint/snapshot.zig
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,14 @@ pub const Snapshot = struct {
if (got_header != @sizeOf(SnapshotHeader)) return out;
if (header.magic != SNAP_MAGIC or header.version != SNAP_VERSION) return out;

try out.ensureTotalCapacity(allocator, @intCast(header.count));
const file_size = try file.length(self.io);
const entry_space = if (file_size > @sizeOf(SnapshotHeader))
file_size - @sizeOf(SnapshotHeader)
else
0;
const max_entries = entry_space / @sizeOf(SnapshotEntry);
const hint = @min(header.count, max_entries);
try out.ensureTotalCapacity(allocator, @intCast(hint));

var off: u64 = @sizeOf(SnapshotHeader);
var i: u64 = 0;
Expand Down
Loading