Skip to content

Reuse query plans across parameter values #14

Closed

Description

@prrao87

Problem

Every CypherEngine.execute() call parses, analyses and plans the query from scratch, even when only the parameters change. Parameters can't be bound to an existing plan, because semantic analysis substitutes them into the AST as literals before planning. execute() then plans the substituted query each time.

Applications, and the LDBC Interactive workload itself, run the same query text many times with different parameters, so planning cost is paid on every call.

Evidence

On SF1, in memory, explain() (which plans but doesn't execute) takes a large share of the total query time:

Query execute() explain()
IC7 21.1 ms 15.4 ms
IC11 5.0 ms 3.4 ms
IC13 4.5 ms 0.8 ms

explain() also formats the plan text, so these overstate planning time; the first step of this issue is to measure parse, analysis, logical and physical planning separately. On the 30-query workload, lance-csr's fastest queries sit at about 0.5 ms while FalkorDB and Neo4j reach 0.2 ms, which suggests a fixed per-query overhead (results).

Suggested fix

  1. Instrument the stages and report where the time goes.
  2. Cache the plan for a query text: keep parameters as placeholders through planning (DataFusion supports Expr::Placeholder and LogicalPlan::with_param_values), and bind values at execution. Invalidate the cache on refresh(), since statistics and the CSR change.
  3. Where a parameter changes the plan shape (for example, it feeds pattern ordering by selectivity), either keep it as a literal for that query or key the cache on it.

Acceptance criteria

  • A breakdown of planning stages for the IC queries, in the pull request.
  • Repeated execute() calls with the same text and different parameters reuse the plan, with results identical to fresh planning.
  • refresh() invalidates cached plans; a test covers a query before and after a refresh.
  • Benchmarks show the effect on IC7, IC11 and the 30-query workload's fastest queries.

Identified by profiling the LDBC IC benchmark; plan caching has not been prototyped.

Activity

  1. prrao87 commented on Oct 27, 2026

    @prrao87
    MemberAuthor

    Addressed on main by #55. CypherEngine now keeps unoptimized plan templates per catalog snapshot and rebinds scalar parameters into them (engine.rs, CypherQuery::prepare_cached_plan in query.rs). A refresh starts with an empty cache, a different parameter type gets its own template, and parameters that change the plan shape (row counts, CALL) skip the cache. Tests: cached_templates_rebind_scalars_and_preserve_schema, cached_templates_respect_catalog_and_parameter_types, parameterized_row_counts_bypass_templates.

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

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions