summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/fs.zig3
-rw-r--r--src/ninep/events.zig27
2 files changed, 25 insertions, 5 deletions
diff --git a/src/fs.zig b/src/fs.zig
index edd8fcb8..edead0ff 100644
--- a/src/fs.zig
+++ b/src/fs.zig
@@ -1334,6 +1334,9 @@ pub const Namespace = struct {
/// write, a shell, a language server): a failure said meanwhile is that
/// write's err, so it posts no msg (Messages.reportFailure).
write_waits: bool = false,
+ /// The whole text of the newest counted log record, hashed: its record
+ /// may be cut, and a repeat is only the same whole text again.
+ log_full_hash: u64 = 0,
/// Writes one open made in a row into one place of a pane's body (an
/// append to `body`, an insert at `data`'s address), held and put in
/// as one edit when anything else comes (ninep/pane.zig flushBatch): a
diff --git a/src/ninep/events.zig b/src/ninep/events.zig
index 570cae78..f76a5212 100644
--- a/src/ninep/events.zig
+++ b/src/ninep/events.zig
@@ -171,7 +171,7 @@ pub fn noteMessage(p: *Pardes, serial: u32, said: []const u8) void {
pushCounting(p, (if (serial == 0)
std.fmt.bufPrint(&buf, "msg - {s}{s}\n", .{ kept, cut })
else
- std.fmt.bufPrint(&buf, "msg {d} {s}{s}\n", .{ serial, kept, cut })) catch return);
+ std.fmt.bufPrint(&buf, "msg {d} {s}{s}\n", .{ serial, kept, cut })) catch return, said);
}
/// Records `err <serial|-> <file>: <why>` for a write or truncation that was
@@ -186,7 +186,7 @@ pub fn noteMessage(p: *Pardes, serial: u32, said: []const u8) void {
pub fn noteErrCounted(p: *Pardes, text: []const u8) void {
bootFirst(p);
var buf: [pardes.memory.limits.host_path_cap + 16]u8 = undefined;
- pushCounting(p, std.fmt.bufPrint(&buf, "err {s}\n", .{text}) catch return);
+ pushCounting(p, std.fmt.bufPrint(&buf, "err {s}\n", .{text}) catch return, text);
}
pub fn notePath(p: *Pardes, what: []const u8, path: []const u8) void {
@@ -239,7 +239,7 @@ pub fn noteError(p: *Pardes, req: Req, reply: Reply) void {
for (record[0 .. record.len - 1]) |*c| if (c.* < ' ') {
c.* = ' ';
};
- pushCounting(p, record);
+ pushCounting(p, record, reason);
}
/// Drops the newest record when it is a msg saying `text` (of any pane)
@@ -256,7 +256,11 @@ pub fn dropMessage(p: *Pardes, text: []const u8) void {
/// counts that one instead: `<record> (x2)`, the count being every time it
/// was said. One a follower has already read is not rewritten: the repeat
/// is a new line carrying the running count, `(x3)`.
-fn pushCounting(p: *Pardes, raw: []u8) void {
+/// `full` is the text before any cut: two records that agree only in what
+/// was cut to fit are no repeat (`fs.log_full_hash`).
+fn pushCounting(p: *Pardes, raw: []u8, full: []const u8) void {
+ const full_hash = std.hash.Wyhash.hash(0, full);
+ defer p.fs.log_full_hash = full_hash;
// A client retrying a write that fails the same way would fill the ring
// with one line, so a repeat of the newest record is that record counted,
// `(x3)`, as +Messages counts its repeats; unless a follower has read it
@@ -272,7 +276,7 @@ fn pushCounting(p: *Pardes, raw: []u8) void {
said = said[0..x];
} else |_| {}
};
- if (!std.mem.eql(u8, said, record[0 .. record.len - 1])) return pushLog(p, record);
+ if (!std.mem.eql(u8, said, record[0 .. record.len - 1]) or p.fs.log_full_hash != full_hash) return pushLog(p, record);
var counted: [pardes.Messages.LoggedMessage.cap + 64]u8 = undefined;
const again = std.fmt.bufPrint(&counted, "{s} (x{d})\n", .{ said, times +| 1 }) catch return;
if (!followerRead(p, last.seq)) p.fs.log.buf.shrinkRetainingCapacity(last.at);
@@ -373,6 +377,8 @@ fn shownAs(text: []const u8, out: []u8, escape_newline: bool) []u8 {
}
fn pushLog(p: *Pardes, raw: []u8) void {
+ // A record not counted: the next one is no repeat of a counted one.
+ p.fs.log_full_hash = 0;
var clean: [4 * 4096 + 256]u8 = undefined;
const record = sanitize(raw, &clean);
// One record larger than the ring would push every other out and then
@@ -1295,3 +1301,14 @@ test "a long err record is cut between words, with an ellipsis" {
try testing.expect(std.mem.endsWith(u8, record, "abcdefgh…\n"));
_ = call(p, .{ .tag = 3, .op = .release, .node = log, .handle = h });
}
+
+test "two failures that agree only in what their records cut are two records, not one counted" {
+ const p = try th.withFile(testing.allocator, "x\n");
+ defer p.deinit();
+ const long = "why " ** 80;
+ p.setMessage(0, long ++ "first");
+ p.setMessage(0, long ++ "second");
+ try testing.expect(!th.logHas(p, "(x2)"));
+ p.setMessage(0, long ++ "second");
+ try testing.expect(th.logHas(p, "(x2)"));
+}