Skip to content

Cache fingerprint: dead fast-path, fail-open reuse, adopted caches never re-validate, and PYTHONHASHSEED is recorded wrong #63

Description

@adamjohnwright

Four defects in the fingerprinting I added in #56. All verified.

1. stored == current can never be True (pathway_generator.py:127)

_write_cache_fingerprint injects provenance; _cache_fingerprint() does not produce that key. Against a cache generated minutes earlier by the same code and env:

stored keys:  ['db_identity', 'env', 'provenance', 'reactome_release', 'src_sha256']
current keys: ['db_identity', 'env', 'reactome_release', 'src_sha256']
stored == current ? False
difference: {'provenance': ('generated', None)}

So every reuse falls into the field-by-field diff, and when nothing has changed it logs something false:

INFO Cache fingerprint differs only in an unknown Reactome release; reusing the cache.

— the release is 97 on both sides. Worse, the safety property inverts: instead of "any difference rejects the cache" it becomes "only the four hand-enumerated differences reject it". Any field added to _cache_fingerprint() in future is written to disk and never compared.

2. An adopted cache never re-validates against source (:99-119, :131-137)

run1 (no fingerprint, csv present) reusable: True  -> provenance='adopted', src_sha256=None
run2 with COMPLETELY DIFFERENT source hash reusable: True
run3: True

Reachable from an ordinary Ctrl-C: any interruption between the first to_csv and the fingerprint write manufactures an adopted cache that is then immune to source-change detection forever. db_identity changes are still caught, so the hole is source-only.

3. A corrupt/partial cache with a matching fingerprint is reused silently

The fingerprint attests to code/env/db/release, not to the integrity of the CSVs, and the writes at :272 and :302-303 are plain to_csv to the final path (no tmp+rename). Truncating decomposed_uid_mapping.csv to 1 row and reaction_connections.csv to 2:

INFO Loading cached reaction connections from .../cache/reaction_connections.csv
INFO Loading cached decomposition from .../cache/decomposed_uid_mapping.csv
INFO Network contains 63 edges        (was 61)

No warning. The validator then reports 11/11 on the wrong network.

4. PYTHONHASHSEED is read from os.environ, not the interpreter (:40, :68)

Setting it after interpreter start has no effect, but the fingerprint records it anyway, so a non-reproducible run is stamped as reproducible and two such caches compare equal.

actual hash randomization active? -7152368689495947875   # differs every run
fingerprint records PYTHONHASHSEED = 0

Measured cost of the unseeded case (R-HSA-72163, LNG_COMPLEX_AS_NODE=0 LNG_HANDOFF_EDGES=1): two runs of the identical configuration differed by 1,494 canonical edges each way out of 342,365.

bin/create-pathways.py:13-15 does it correctly by re-exec; examples/generate_pathway_example.py has no pinning. Fix is sys.flags.hash_randomization / sys.hash_info, not os.environ.

Confirmed working (do not re-check): a corrupt fingerprint.json is rejected; an unknown release does not spuriously invalidate; a db_identity change rejects even an adopted cache; a fresh empty cache dir is correctly not treated as an adoption; and with the seed genuinely pinned before interpreter start, generation is reproducible and order-independent (342,365 edges, 0 differing, across separate processes and pathway orderings).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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