Skip to content

Flake eval cache fixes - #15288

Merged
edolstra merged 3 commits into
NixOS:masterfrom
roberth:flake-eval-cache-fixes
Sep 1, 2026
Merged

Flake eval cache fixes#15288
edolstra merged 3 commits into
NixOS:masterfrom
roberth:flake-eval-cache-fixes

Conversation

@roberth

@roberth roberth commented Feb 18, 2026

Copy link
Copy Markdown
Member

Motivation

Get fixes into the release.

This fixes a problem where the third instead of second eval come from cache.
I haven't reproduced the string context splitting problem outside of a unit test in my WIP eval cache work - I would prefer to spend time improving that work than producing a test case for this unimportant corner case.

Context

  • Part of STF "Investigate evaluator performance improvements"

Add 👍 to pull requests you find important.

The Nix maintainer team uses a GitHub project board to schedule and track reviews.

@roberth
roberth requested a review from edolstra as a code owner February 18, 2026 20:49
@github-actions github-actions Bot added the with-tests Issues related to testing. PRs with tests have some priority label Feb 18, 2026
Comment thread src/libexpr/eval-cache.cc Outdated
} else {
state->insertAttribute.use()(key.first)(symbols[key.second])(AttrType::FullAttrs) (0, false).exec();
rowId = state->db.getLastInsertedRowId();
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the performance impact of this (since it doubles the number of SQLite calls)?

Maybe you could use INSERT ... ON CONFLICT DO UPDATE to do it in a single SQLite call that should preserve the rowid (unlike INSERT OR REPLACE).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@roberth
roberth force-pushed the flake-eval-cache-fixes branch from 2af7f1e to 06b9384 Compare February 23, 2026 10:48
@tomberek

Copy link
Copy Markdown
Contributor

Rebase and merge?

@roberth
roberth force-pushed the flake-eval-cache-fixes branch from 06b9384 to 36768cf Compare May 24, 2026 14:26

@pkpbynum pkpbynum left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 on this PR, was looking into nix develop cache misses just now and it's fixed precisely by f7d66e6

testing:

# Stable nix
% rm -rf ~/.cache/nix/eval-cache-v6
% nix --version
nix (Nix) 2.34.8
% nix develop -c true
nix develop -c true  1.86s user 0.67s system 68% cpu 3.684 total
% time nix develop -c true
nix develop -c true  1.36s user 0.55s system 62% cpu 3.039 total
% time nix develop -c true
nix develop -c true  0.17s user 0.19s system 44% cpu 0.809 total

# This branch
% rm -rf ~/.cache/nix/eval-cache-v6
% ./result/bin/nix --version
nix (Nix) 2.36.0pre20260720_dirty
% git diff
% time ./result/bin/nix develop -c true
./result/bin/nix develop -c true  2.20s user 0.82s system 69% cpu 4.359 total
% time ./result/bin/nix develop -c true
./result/bin/nix develop -c true  0.16s user 0.19s system 45% cpu 0.783 total

The in-memory cached value was created with an empty context `{}` instead
of copying the actual context from the value. This caused string contexts
to be lost even though they were correctly written to the database.
When setAttrs "seals" an attrset (transitions from Placeholder to
FullAttrs), the previous INSERT OR REPLACE would delete and recreate
the row with a new rowid. This orphaned any children cached under the
old rowid, causing cache misses on subsequent builds.

This explains the "third time's a charm" behavior: on the 2nd build,
orphaned children are re-cached under the new parent rowid. With
deeper nesting, even more evaluations could be required as each level
of sealing orphans the next level's children.

Fix by using INSERT ... ON CONFLICT DO UPDATE (upsert) to preserve
the rowid, and INSERT OR IGNORE for children to preserve existing
cached values. Also delete redundant Missing entries since we now
know the complete set.

This requires SQLite >= 3.35 for the RETURNING clause, now declared
as an explicit dependency of libexpr.
Verify that evaluation caching works on the second build by checking
that builtins.trace output only appears on the first evaluation.
@edolstra
edolstra force-pushed the flake-eval-cache-fixes branch from 36768cf to 4d9b962 Compare September 1, 2026 14:42
@edolstra

edolstra commented Sep 1, 2026

Copy link
Copy Markdown
Member

Sorry for not looking into this sooner. Thanks, I've resolved the merge conflict!

@edolstra
edolstra enabled auto-merge September 1, 2026 14:46
@edolstra
edolstra added this pull request to the merge queue Sep 1, 2026
Merged via the queue into NixOS:master with commit 72385de Sep 1, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

with-tests Issues related to testing. PRs with tests have some priority

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants