milestone 27: diagnostics — operational failures land in one curated log, resolved history purgeable
Gates / frontend (push) Successful in 1m33s
Gates / test (push) Successful in 1m48s
Gates / test-aarch64 (push) Successful in 7m10s
Gates / package (push) Successful in 5m31s
Gates / container (push) Successful in 15s
CI / gates (push) Successful in 14m51s
Gates / frontend (push) Successful in 1m33s
Gates / test (push) Successful in 1m48s
Gates / test-aarch64 (push) Successful in 7m10s
Gates / package (push) Successful in 5m31s
Gates / container (push) Successful in 15s
CI / gates (push) Successful in 14m51s
This commit is contained in:
@@ -88,6 +88,18 @@ pub const OpenResult = struct {
|
||||
database: db.Db,
|
||||
/// Non-null feeds a counter and the `/api/health` rollup.
|
||||
recreated: ?RecreateReason,
|
||||
/// The path the previous file was kept as, by value. It existed only in a
|
||||
/// stack buffer inside `open` before the diagnostics event needed it, and a
|
||||
/// slice of that buffer would dangle the moment `open` returned.
|
||||
///
|
||||
/// Empty when nothing was renamed aside, which `.missing` and a clean open
|
||||
/// both are.
|
||||
aside_buf: [path_buf_len]u8 = undefined,
|
||||
aside_len: u16 = 0,
|
||||
|
||||
pub fn aside(self: *const OpenResult) []const u8 {
|
||||
return self.aside_buf[0..self.aside_len];
|
||||
}
|
||||
};
|
||||
|
||||
pub const Error = db.Error || error{AsideNameCollision} ||
|
||||
@@ -156,7 +168,12 @@ pub fn open(io: std.Io, dir: std.Io.Dir, path: [:0]const u8) Error!OpenResult {
|
||||
aside.?,
|
||||
});
|
||||
}
|
||||
return .{ .database = fresh, .recreated = cause };
|
||||
var result: OpenResult = .{ .database = fresh, .recreated = cause };
|
||||
if (aside) |name| {
|
||||
result.aside_len = @intCast(name.len);
|
||||
@memcpy(result.aside_buf[0..name.len], name);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// The whitelist. `null` means "propagate, do not touch the file".
|
||||
@@ -321,3 +338,53 @@ test "recreatable selects exactly two of db.Error's members" {
|
||||
}
|
||||
try testing.expectEqual(@as(usize, 2), whitelisted);
|
||||
}
|
||||
|
||||
test "a recreate returns the aside name by value and a fresh create returns none" {
|
||||
var threaded: std.Io.Threaded = .init(testing.allocator, .{});
|
||||
defer threaded.deinit();
|
||||
const io = threaded.io();
|
||||
|
||||
var tmp = testing.tmpDir(.{ .iterate = true });
|
||||
defer tmp.cleanup();
|
||||
|
||||
var path_buf: [path_buf_len]u8 = undefined;
|
||||
const path = try std.fmt.bufPrintZ(&path_buf, ".zig-cache/tmp/{s}/querylog.db", .{tmp.sub_path});
|
||||
|
||||
// First open: the file is missing, so nothing is renamed aside. The
|
||||
// one-shot event is deliberately not emitted for this case.
|
||||
var created = try open(io, std.Io.Dir.cwd(), path);
|
||||
created.database.close();
|
||||
try testing.expectEqual(RecreateReason.missing, created.recreated.?);
|
||||
try testing.expectEqualStrings("", created.aside());
|
||||
|
||||
try tmp.dir.writeFile(io, .{ .sub_path = "querylog.db", .data = "not a database at all" });
|
||||
|
||||
var recreated = try open(io, std.Io.Dir.cwd(), path);
|
||||
recreated.database.close();
|
||||
try testing.expectEqual(RecreateReason.not_a_database, recreated.recreated.?);
|
||||
try testing.expect(recreated.aside().len != 0);
|
||||
|
||||
// The name is a real file, which is the whole reason it travels out.
|
||||
const kept = std.fs.path.basename(recreated.aside());
|
||||
try tmp.dir.access(io, kept, .{});
|
||||
}
|
||||
|
||||
test "a clean reopen reports no recreate and no aside" {
|
||||
var threaded: std.Io.Threaded = .init(testing.allocator, .{});
|
||||
defer threaded.deinit();
|
||||
const io = threaded.io();
|
||||
|
||||
var tmp = testing.tmpDir(.{ .iterate = true });
|
||||
defer tmp.cleanup();
|
||||
|
||||
var path_buf: [path_buf_len]u8 = undefined;
|
||||
const path = try std.fmt.bufPrintZ(&path_buf, ".zig-cache/tmp/{s}/querylog.db", .{tmp.sub_path});
|
||||
|
||||
var first = try open(io, std.Io.Dir.cwd(), path);
|
||||
first.database.close();
|
||||
|
||||
var second = try open(io, std.Io.Dir.cwd(), path);
|
||||
second.database.close();
|
||||
try testing.expectEqual(@as(?RecreateReason, null), second.recreated);
|
||||
try testing.expectEqualStrings("", second.aside());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user