Skip to content

test: the execution_plan artifact-identity tests are racy on fast filesystems #679

Description

@jamesbrink

Summary

A cluster of ~5 tests in crates/mold-server/src/execution_plan.rs fails non-deterministically on a fast local machine while passing in CI. The failing subset varies run to run — I have observed 2, 3, 4, and 5 failures from the same cluster within a single session, with no source change between runs:

  • cache_miss_identity_changes_when_artifact_metadata_changes
  • same_size_in_place_overwrite_changes_equivalence_content_identity
  • same_size_in_place_overwrite_with_restored_mtime_changes_content_identity
  • frozen_chain_model_uses_canonical_companions_and_ignores_changed_config

Root cause

artifact_metadata_identity derives identity from path + size + mtime. The fixtures overwrite a file with different content of identical size:

std::fs::write(&path, b"aaaa").unwrap();
let first_metadata = artifact_metadata_identity(&path, &path.metadata().unwrap());
// ...
std::fs::write(&path, b"bbbb").unwrap();
let second_metadata = artifact_metadata_identity(&path, &path.metadata().unwrap());
assert_ne!(
    first_metadata, second_metadata,
    "fixture must observe a new metadata identity"
);

Size is unchanged by construction, so the assertion rests entirely on the two writes landing in different filesystem mtime ticks. When they land in the same tick the identities are equal and the fixture's own precondition fails — before the test reaches the behaviour it means to check.

This is why it fails more on a fast machine: the two writes complete closer together. CI's slower runners and filesystem separate them, which is why main is green.

Why it matters beyond flakiness

The failing set drifts with machine load, so a local cargo test --workspace cannot distinguish "my change broke something" from "the box was busy". Every contributor running the documented CI-equivalent gate has to hold a mental list of which failures to ignore, and that list is not even stable. During recent LTX-2 milestone work this cost real time on several PRs, each time re-deriving that the failures were unrelated.

Suggested fix

Make the fixture control the clock instead of racing it — force a distinct mtime rather than hoping for one:

filetime::set_file_mtime(&path, FileTime::from_unix_time(t + 1, 0))?;

filetime is already in the dependency graph. Alternatively, assert on the content identity these tests actually care about and drop the metadata-identity precondition, which is an implementation detail of the cache rather than the property under test.

Either way the tests then verify the intended invariant deterministically, on any filesystem.

Reproduction

unshare -Urm --map-root-user sh -c 'mount -t tmpfs none /mnt; \
  env -u MOLD_HOME -u MOLD_MODELS_DIR cargo test -p mold-ai-server --lib execution_plan:: 2>&1' \
  | grep -E "^test .*FAILED|^test result"

Run it a few times under varying load; the failing subset changes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions