One line of colour

Part 7 of 7 of ./blog.sh v1.7

A reader changed one border colour in site.css, ran a rebuild, and waited while the build produced, byte for byte, every page that was already there. He asked why. The answer was one word in one list, and 1.7 takes it out.

What a build writes down

Since 1.6 the build keeps a record of what went into each page it wrote -- the post, the posts around it, the labels, the numbers a page counts. On the next build a page whose record has not moved is not rendered at all. Not rendered and then found identical: not rendered. The summary at the end says how many pages were left alone, the first number to look at when a build is slow.

The record holds only while the engine that wrote it is the same engine, so it hangs off a fingerprint of what the engine is made of -- the files under templates/, lib/, build/, locales/ and config/, hashed by contents rather than by timestamp. Change any of it and the record is discarded entire, because any of it can change every page.

Why the colour cost the archive

Until 1.7 that list also had assets/ on it, and assets/ is where the stylesheets live.

A page does not contain its stylesheet. It carries a <link> to one, and there is no ?v= on that link -- a version baked into the URL would make every page differ on every CSS edit, the same problem wearing a different coat. So the bytes of a rendered page cannot move when site.css moves. The fingerprint was covering a file no page has ever held.

Measured in a copy of the engine, on a test site of 270 posts: a rebuild that changes nothing skips all 378 pages the cache is asked about, and adding a rule to site.css still skips 378. Put assets back into the fingerprint -- the 1.6 behaviour -- and the same edit skips none.

Nothing is given up. Files under assets/ are compared with their published copies on every build, by a path that never consulted this record. The edit reaches the site as it always did, and now costs that one file and nothing else.

What is still fingerprinted, and what that costs

config/ stayed on the list, and the palette is one reason it had to.

The colours in config/site.yml are not read out of a stylesheet. They generate one -- assets/css/colors.css is written by the build, not copied from the tree -- and one of them reaches the pages a second way: the light and dark theme-color in the head of every page the layout renders is the palette's background. Change bg and every one of those pages really is different. Rebuilding them is the record being right.

Change accent and no page moves. It lives only in the generated stylesheet, and the site is rebuilt all the same: the fingerprint is taken over whole trees rather than the keys inside them, so config/site.yml moving throws config/ away. On the same test site that edit skipped nothing and rendered all 378. It is the cost this release took off assets/, kept on purpose in a smaller place -- a tree is a coarse thing to hash, and coarse errs towards work. A tree belongs in the fingerprint when a page can hold its contents; linking is not holding.

When the record is not believed

  • The first build after an upgrade. This release changed lib/build_cache.rb, and lib/ is in the fingerprint, so the first build on 1.7 renders everything. An engine that worked out which pages a template edit reaches would be wrong eventually, and wrong here means a live site serving stale markup with nothing saying so.
  • The facts the build hands to the record by name rather than by hashing a tree: the menu, which content types the archive holds, the address it is published at, and the timezone -- named by the contents of its zoneinfo file, so a system update that rewrites the rules of your zone throws the record away rather than leaving old times on skipped pages.
  • Any page whose published file has been deleted, or has moved in size or timestamp since the build wrote it. The record is checked against what is on disk, never only against itself.
  • A record that is missing, half-written or unreadable. Deleting .build_cache.json is always safe and costs one slow build.
  • A build that died halfway. It writes no record at all, because a record of a site half written would make the next build skip the half that never got written.

./blog.sh rebuild --full is the switch for the rest: render everything, compare everything, believe nothing. It is the escape hatch, and it is how the record is tested: a cached build and a full build over the same content must produce byte-identical trees.

What it costs

With no version in its URL, getting a changed stylesheet to a reader is the host's job: it works where files carry an ETag and are revalidated on each load, and a host that caches hard will serve the old one until it expires. And backdating is untouched by this: a post dated 2003 shifts every listing page between the front and where it lands, and those pages really did change.

Comments