DEV Community

Mahiro Hirakawa
Mahiro Hirakawa

Posted on

My grep filter had no digits in it. The one field with a digit was the one I said was missing

I told a stranger his system had a defect. I had measured it. The measurement was a grep, and the grep had a character class in it, and the character class had no digits.

The setup

Someone had been picking apart a design of mine in a comment thread for three days, correctly, twice. On the third round he made a specific proposal: my crash recovery should be able to tell "the write never ran" apart from "the write ran and landed something else", and to do that it needs the pre-image fingerprint written down before the first byte moves, not held in memory where a crash takes it.

Good point. I went to check.

I listed the fields on the journal record, saw the pre-image fingerprint was not among them, traced where it lived instead, found it in an in-memory table, and wrote back that he was right. I opened the reply with the words "went and read the engine rather than answering from memory".

The field was there

It had been there since the commit that created the crate. git log -S on the field declaration returns exactly one result, the crate's first commit. It is a plain field, always serialised. The plan writes it. Replay reads it back out, and resume refuses to proceed on a record that lacks it.

Here is how I listed the fields:

grep -E "^\s+[a-z_]+: "
Enter fullscreen mode Exit fullscreen mode

The field is named fp0. [a-z_]+ has no digits in it. It is the only field on that record whose name contains one.

So the instrument dropped exactly the field the conclusion was about. I read the gap as absence. And because I had genuinely run something, I signed the result as measured.

The part that should have caught it

I have a rule for this and I did not apply it: when a check comes back empty, feed it something you know is there. One line, fp0: FingerprintRecord,, piped into that same grep, prints nothing. The whole error is visible in two seconds from a positive control, and a positive control is free.

I skipped it because "no output" did not feel like a result. It felt like the absence of one. That is the trap. A green result invites suspicion because green is a claim. Empty does not feel like a claim, so nothing in me went looking for a reason it might be lying.

Empty is a claim. It claims the filter ran. It claims the filter was pointed at the right text. It claims nothing in that text matched. Three assertions and I checked none of them.

It gets worse before it gets better

The repair I proposed was not just unnecessary. The codebase had already considered it and rejected it, and there is a test that keeps it rejected: it plants the pre-image-comparing recovery and asserts that it produces the wrong verdict.

The reason is one of those things that is obvious in hindsight. After a successful apply, the pre-image no longer matches, because the engine itself moved the world. A recovery that adjudicates by comparing against the pre-image mistakes its own footprint for someone else's interference. So the discriminator is a separate journal record announcing that apply began, and "never ran" is its own named outcome rather than a residue.

I had proposed, to a stranger, in public, with a confident tone, the exact thing the design had a passing mutation control against.

What actually survived

His point was not empty, it was narrower than I made it. Two situations, "the write landed something other than the plan" and "a third party overwrote after the apply", both surface as the same value. Detection separates them from the healthy path. Attribution does not separate them from each other, and no fingerprint on the plan record would, since both differ from the plan in the same direction. That limit was already written down in the project's limits page, in words close to his.

His other point I have no answer to at all: comparing two declarations against each other, a config and a document, cannot see a path that appears in neither. Only a probe that attempts the operation and requires the guard to have observed it fails on the day coverage actually stops.

The rule I am left with

Not "check your regexes". Everyone knows that and it did not help.

The rule is that an empty result is a positive claim about the world, and every positive claim gets a control. If the check can come back empty, hand it something that must not be empty, first, every time. The cost is one line. The alternative, in my case, was telling a person who had spent three days finding real defects in my design that I had found one in his, and being wrong about it in public, under a sentence claiming I had read the source.

I corrected it in the same thread. That is the only part of this I would repeat.

TraceFold, Rust, Apache-2.0, alpha. The crates are on crates.io at 0.1.2 with double-digit download counts, so treat them as published rather than released.

Top comments (0)