Skip to content

v1.22.36

Choose a tag to compare

@Robbie1977 Robbie1977 released this 31 Jul 07:27
· 158 commits to main since this release
f69bd2a

A patch release on purpose. The cache namespaces entries by major.minor, so 1.23.0 would leave every warm answer behind; nothing here changes what a query returns, so there is nothing to invalidate.

Three of these fixes exist because a single query took production Neo4j down for an hour. That is the thread running through the release: work that was unbounded is now bounded, on both sides of the connection.

The server is now told how long it may spend

A requests read timeout only stops this process waiting. It does not reach the server, and the transaction carries on — which is how a client that had already given up left a query running for an hour. The client now sends max-execution-time on every statement, so the server abandons work it will never deliver. (Neo4j-Transaction-Timeout is silently ignored by this deployment; max-execution-time is honoured — measured, a 6s sleep with a 1000ms budget was killed at 2.16s.)

The budget is a flat 300s ceiling rather than a multiple of the read timeout, because deriving it from the read timeout produced 22 hard ExecutionFailed errors in one run. Raising VFBQUERY_NEO4J_READ_TIMEOUT_S above the ceiling still works for a genuinely long analytical query. Tunable via VFBQUERY_NEO4J_SERVER_MAX_EXECUTION_S and VFBQUERY_NEO4J_SERVER_BUDGET_FACTOR.

A statement the server stopped is now retried like a read that timed out on this side — the work ran long, not wrong. Before, the two paths disagreed: a client-side overrun retried and usually succeeded, a server-side one returned False on the first attempt.

Read-only caching no longer computes results it cannot store

@with_solr_cache forced limit=-1 on a miss for expensive query types, so it could store the whole table. In read-only mode there is nowhere to store it, and the caller who asked for ten rows was billed for all of them. That is not theoretical: expression_overlaps_here walks an unbounded has_source|SUBCLASSOF|INSTANCEOF path reaching 100,426 individuals for FBbt_00007228 — about six seconds at limit=10, and it does not finish at limit=-1. PR checks run read-only and so could never warm the cache, meaning every PR paid the uncapped price forever. One of those runs is the hour-long outage.

Read-only mode now honours the caller's limit, and a result computed that way is never written under the limit=-1 key — caching it would serve a truncated table to every later reader as complete.

Silent no-ops now say something

Parameters that were accepted and ignored now warn, exclude_dbs is validated rather than quietly matching nothing, and HTTP handlers explain their own rejections (an unknown type name comes back with suggestions) instead of returning a bare status code.

VFBQUERY_COMPUTE_BUDGET (default 180s) bounds how long an HTTP handler waits, not how long the work runs. The computation is now a detached task that owns the cache write, so a client hanging up no longer kills it mid-flight — previously a query slow enough to lose one client was a query that could never finish for anybody, because nothing was ever written. Set 0 to wait indefinitely. See CACHING.md.

Private cache namespaces

VFBQUERY_CACHE_NAMESPACE moves all reads, writes and deletes into a private id prefix, with VFBQUERY_CACHE_NAMESPACE_FALLBACK to read (never write) production on a miss, and a 48-hour default TTL so an abandoned branch's entries evaporate. This lets CI use a cache at all — it previously had to run with caching off, because the alternative was letting experimental code write to the shared one. purge_namespace() refuses to run without a namespace set, since its delete query would otherwise expand to id:vfb_query_*.

Tests that were measuring nothing

  • QueryConnectivity's performance fixture used a pair whose only 17 connections live in hb and fafb, both removed by DEFAULT_EXCLUDE_DBS — it timed an empty result and passed. It now uses a pair with 42 connections under the same defaults, and asserts the result is non-empty.
  • The class-connectivity tests ran with a 45s read timeout against a query measured at 106s. They did not time out so much as report an empty table, and nine of them failed on assertions about rows that had never arrived. Raised to 180s.
  • Neo4j settings are now resolved at construction rather than frozen at import, so the conda unittest job gets the test package's settings — it previously kept the library's REPL-tuned patience, and one test measured 582s against its own 120s threshold for that reason alone.
  • The performance suite's limit=-1 warm-up is skipped in read-only mode, where it was pure cost.

Merges #83, #84 and #85.