fix(build): the hash record is written under its build's start, and keeps only the keys used #2873

Open
github-sync wants to merge 1 commit from github/pr/2873 into main
Owner

@jordanella opened this pull request on GitHub · 2026-11-12 18:06 UTC · original

hckrman101's two non-blocking points on #2702: the hash record was written under the time it was saved, seconds after the sources were stat'd, which loosens the 2 s rule on coarse-timestamp filesystems; and the record only grew, one key per renderer source for every header edit.

Change

nx2/build.py:

  • The record's time is its build's start: load_hashes takes the time before any source is stat'd (HASHES_STARTED), and save_hashes writes that as written_ns, not the time of the save after the whole plan.
  • Only the keys used are kept: key_of and content_sha1 note what this build asked for, per source (USED). For each source this build looked at, the record keeps only those keys. A source it didn't look at, such as a renderer-only build's lifted C, keeps its record as it was.

Nothing about the keys themselves changes, so no object is renamed and nothing compiles again for this change.

Measured

Windows 11, Ryzen 9 3900X, a work folder of 874 sources. Each column is a build: a comment added to gpu/vk/vk_present_rect.h, a second comment, both taken out again, then a build with nothing changed. The numbers are the record's total keys after each build.

build.py start 1st edit 2nd edit reverted no change
main (#2702) 897 932 967 967 967
this branch 897 897 897 897 897

The record stays at one key per source per flag set, at most 3 on one source in both. On this branch the two edits compiled 2 files each, not 35, because main's run had already cached objects for the same header contents.

In the game

The game from this branch's last build, launched headless through the control bridge from a save (RTX 3080, NVIDIA 621.14, Windows 11): in play in Kokiri Forest, 27–32 fps over five samples, nothing in the log about a lost device or a crash. The change is build-only and shows nothing on screen.

Testing

  • tests/test_build_hash_cache.py, two new cases:

    • the record's time is the load's, not the save's;
    • a source looked at keeps only its used keys, and one not looked at keeps all of its.

    Both fail on main.

  • Every check that reads the changed files (35), through run_all.py on Windows with the Builder's Python and clang, rebased on today's main: 31 pass; four fail the same way on main here: test_driver_cache_folder, test_fpx_sticky, test_linux_update and nx2/test_dll.py (it needs a DLL build).

  • Not tested: Linux. Nothing here is platform-specific; the record's id is the inode there.

See #2702, #2636 (A5).

🤖 Generated with Claude Code

> [@jordanella](https://github.com/jordanella) opened this pull request on GitHub · 2026-11-12 18:06 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2873) hckrman101's two non-blocking points on #2702: the hash record was written under the time it was saved, seconds after the sources were stat'd, which loosens the 2 s rule on coarse-timestamp filesystems; and the record only grew, one key per renderer source for every header edit. ## Change `nx2/build.py`: - **The record's time is its build's start:** `load_hashes` takes the time before any source is stat'd (`HASHES_STARTED`), and `save_hashes` writes that as `written_ns`, not the time of the save after the whole plan. - **Only the keys used are kept:** `key_of` and `content_sha1` note what this build asked for, per source (`USED`). For each source this build looked at, the record keeps only those keys. A source it didn't look at, such as a renderer-only build's lifted C, keeps its record as it was. Nothing about the keys themselves changes, so no object is renamed and nothing compiles again for this change. ## Measured Windows 11, Ryzen 9 3900X, a work folder of 874 sources. Each column is a build: a comment added to `gpu/vk/vk_present_rect.h`, a second comment, both taken out again, then a build with nothing changed. The numbers are the record's total keys after each build. | `build.py` | start | 1st edit | 2nd edit | reverted | no change | |---|---|---|---|---|---| | `main` (#2702) | 897 | 932 | 967 | 967 | 967 | | this branch | 897 | 897 | 897 | 897 | 897 | The record stays at one key per source per flag set, at most 3 on one source in both. On this branch the two edits compiled 2 files each, not 35, because `main`'s run had already cached objects for the same header contents. ## In the game The game from this branch's last build, launched headless through the control bridge from a save (RTX 3080, NVIDIA 621.14, Windows 11): in play in Kokiri Forest, 27–32 fps over five samples, nothing in the log about a lost device or a crash. The change is build-only and shows nothing on screen. ## Testing - **`tests/test_build_hash_cache.py`, two new cases:** - the record's time is the load's, not the save's; - a source looked at keeps only its used keys, and one not looked at keeps all of its. Both fail on `main`. - **Every check that reads the changed files (35), through `run_all.py` on Windows with the Builder's Python and clang, rebased on today's `main`:** 31 pass; four fail the same way on `main` here: `test_driver_cache_folder`, `test_fpx_sticky`, `test_linux_update` and `nx2/test_dll.py` (it needs a DLL build). - **Not tested:** Linux. Nothing here is platform-specific; the record's `id` is the inode there. See #2702, #2636 (A5). 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- github-sync:item:2873 -->
hckrman101's two points on #2702. The record was written under the time of its save, after the whole plan, seconds after
each source was stat'd; the 2 s "racily clean" rule is about the stat, so it is written under the time load_hashes ran,
before any. And it only grew: each header edit added a key to every renderer source. Each source this build looked at
now keeps only the keys this build asked for (key_of, content_sha1); a source it did not look at, such as a
renderer-only build's lifted C, keeps its record as it was.

tests/test_build_hash_cache.py: the record's time is the load's, and a source looked at keeps only its used keys while
one not looked at keeps all of its (both fail on main).

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Commenting is not possible because the repository is archived.
No description provided.