Skip to content

Commit 1c7dee9

Browse files
lmeyerovclaude
andcommitted
docs(changelog): entry for the cache-clear fix and cache registry
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011AB4RZpph3uSFUpzKnZJcr
1 parent 19d3707 commit 1c7dee9

1 file changed

Lines changed: 1 addition & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
4141
- **GFQL execution context is declared rather than attached at runtime**: the private `_gfql_*` per-execution fields (index policy and registry, row-pipeline base graph, carried seed nodes, edge aliases, shortest-path backend, and the indexed-bindings handoff) are now declared on `Plottable` with defaults on `PlotterBase`, instead of being set with `setattr` and read back with `getattr(..., default)`. No public API or behaviour change; internal call sites are typed, and hand-rolled `Plottable` stand-ins must now construct the full context.
4242

4343
### Fixed
44+
- **`gfql_clear_caches()` cleared the wrong name and silently did nothing**: the clear targeted `parse_cypher` while the memo lives on the `_parse_cypher_cached` body, so every "cold-process" measurement taken through it was warm. Every process-lifetime GFQL cache now self-registers in `graphistry/compute/gfql/cache_registry.py` at its own definition site -- a bound clear handle for caller-input-keyed memos (including the previously unaccounted polars single-alias lowering `OrderedDict`, found by audit), or a written exemption for maxsize=1 function-of-the-code singletons. A coverage lock fails CI on any unregistered memo (decorator AND dict-style scans), clears are recompute-safe against concurrent reads, and a thread-stress test pins value correctness under concurrent clearing. Convention: DEVELOP.md "GFQL Cache Registry".
4445
- **The compiled-query cache key omitted the engine, which a shared cache would have turned into a wrong answer.** The key relied on `node_dtypes` to capture engine-dependent compilation, but polars and polars-gpu report *identical* node dtypes and compile differently. While the cache was partitioned per `Plottable` this was masked rather than avoided; sharing plans process-wide exposed it immediately as 16 failures in `test_const_fold_engine_parity.py` on the polars-gpu arm. The key now carries the **resolved** engine -- resolved, because `auto` resolves per graph, so keying on the requested engine would let two graphs that resolve differently share a plan.
4546

4647
- **`master` is green again: the type-hygiene ratchet tripped on merge, and the fix is fewer `cast()`s rather than a raised cap**: `bin/ci_type_hygiene_baseline.json` is a per-file SNAPSHOT, so it goes stale whenever a PR *other than the one that owns the baseline* touches a baselined file. #1830 captured its baseline on a branch whose merge-base is `b6181d355` — before #1800, #1799 and #1816 landed — and each of those three added `cast()` calls to a file #1830 had already pinned. All four were green on their own bases; the combination was not, and nothing in any of the four merges signalled it: `explicit-cast` came out 7 over baseline across `gfql_fast_paths.py` (+5), `gfql_unified.py` (+1) and the polars `row_pipeline.py` (+1), failing `python-lint-types` on 3.11/3.13/3.14. The baseline is **unchanged** here; the findings were removed instead. Three of the seven were never `typing.cast` at all — the guard matches any call whose name is `cast`, including the attribute form, so `pl.Expr.cast`, a polars RUNTIME dtype conversion, counts as a typing finding; those three carry the documented `# hygiene-ok: explicit-cast` escape hatch with the reason. The other four were real, and are gone by DECLARATION rather than by call-site assertion, which is the idiom the guard is asking for: `_two_hop_cached_equal_domain_degree_counts` declares `counts: Tuple[DataFrameT, DataFrameT]` once and both arms assign to it, so four casts collapse to one localized `# type: ignore[assignment]` on the polars arm (`DataFrameT` is pinned to pandas at checking time); `_apply_connected_optional_match` declares `seed_ids: SeriesT` / `node_ids: SeriesT` instead of casting, because selecting one column off a frame is a Series on every engine. (The neighbouring `cast(DataFrameT, df_to_engine(...))` pair is deliberately left alone: `df_to_engine` carries no return annotation, so removing those would mean annotating a helper with 115 call sites, and the polars arm of that branch is not reached by any CI lane — rewriting it would have dragged a pre-existing coverage blind spot into the changed-line gate for no typing gain.) All three files now sit AT or BELOW baseline (18/18, 132/133, 41/42). Typing-only: `typing.cast` is the identity function at runtime, so every removal is provably value-preserving, and the one restructured line binds an unchanged `pl.Expr` list to a name so the per-line suppression fits the 127-column limit. **The hazard is latent, not spent** — any future PR adding a finding to a baselined file reds `master` the same way, and the guard cannot see it from inside either PR.

0 commit comments

Comments
 (0)