Skip to content

Extend query profiles and improve timeout histogram behavior#181

Merged
shahar-biron merged 3 commits into
masterfrom
extend-query-profiles
Jul 12, 2026
Merged

Extend query profiles and improve timeout histogram behavior#181
shahar-biron merged 3 commits into
masterfrom
extend-query-profiles

Conversation

@shahar-biron

@shahar-biron shahar-biron commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Summary

  • extend query profile coverage in benchmark query generation and execution paths
  • improve histogram/aggregation plumbing for per-query metrics
  • update UI single-workload histogram behavior to only show the timeout percentage column when timeout data exists
  • include related dashboard/chart adjustments needed for the new query/profile behavior

Validation

  • npm --prefix ui run lint

Co-Authored-By: Oz oz-agent@warp.dev

Summary by CodeRabbit

  • New Features

    • Added configurable query coverage profiles, including extended-core and fixture-dependent benchmark queries.
    • Added fixture setup and capability detection for vector and full-text search scenarios.
    • Added configurable Memgraph query timeouts.
    • Added per-query Memgraph timeout-rate metrics.
  • Bug Fixes

    • Improved latency charts to display timeout percentages accurately.
    • Enhanced benchmark loading and query-generation reliability across supported databases.
  • Documentation

    • Expanded benchmark usage guidance and query explanations with new profiles and search examples.

shahar-biron and others added 2 commits July 9, 2026 10:27
Enable fixture-dependent query profile plumbing across generation and run commands, add vendor-specific fixture readiness/index setup for Neo4j and Memgraph, and align small/medium/large scripts so the same fixture workload can be reproduced consistently while investigating rust-engine memory issues.

Co-Authored-By: Oz <oz-agent@warp.dev>
@vercel

vercel Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
benchmark Ready Ready Preview, Comment Jul 12, 2026 7:46am

Request Review

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@shahar-biron, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 51 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 843e2e5e-e2d0-419c-8fb3-e006c9baaabf

📥 Commits

Reviewing files that changed from the base of the PR and between b71140c and deca775.

📒 Files selected for processing (3)
  • src/memgraph.rs
  • src/neo4j.rs
  • src/queries_repository.rs
📝 Walkthrough

Walkthrough

Changes

Query coverage and observability

Layer / File(s) Summary
Profile catalog and benchmark wiring
QUERY_EXPLANATIONS_AND_SAMPLES.md, readme.md, scripts/*_benchmark.sh, src/cli.rs, src/queries_repository.rs, src/main.rs
Adds baseline, extended-core, and fixture-dependent profiles, profile-specific queries, CLI propagation, script configuration, and documentation.
Vendor fixture capability and readiness
src/falkor/falkor_driver.rs, src/neo4j_client.rs, src/memgraph_client.rs, src/main.rs
Detects vendor capabilities, creates indexes, seeds deterministic fixture data, and polls fixture readiness for fixture-dependent queries.
Timeout accounting and aggregation
src/memgraph_client.rs, src/main.rs, src/aggregator.rs, src/lib.rs
Adds configurable Memgraph query timeouts, per-query timeout accounting, Prometheus timeout-rate metrics, and timeout histogram aggregation.
Timeout dashboard rendering
ui/app/components/VerticalBarChart.tsx, ui/app/components/dashboard.tsx, ui/components/ui/SidebarNavigation.tsx
Normalizes histograms, renders optional timeout percentage buckets, and adds explanations for the new smoke queries.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant BenchmarkCLI
  participant QueryRepository
  participant VendorClient
  participant VendorDatabase
  BenchmarkCLI->>QueryRepository: select query coverage profile
  QueryRepository->>BenchmarkCLI: return profile-specific queries
  BenchmarkCLI->>VendorClient: detect capabilities and prepare fixtures
  VendorClient->>VendorDatabase: create indexes and seed fixture data
  VendorDatabase-->>VendorClient: report query readiness
  VendorClient-->>BenchmarkCLI: start benchmark execution
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.42% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: expanded query profiles and timeout-related histogram/chart behavior updates.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch extend-query-profiles

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/main.rs (1)

1549-1661: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Apply fixture setup when a cached local backup already exists.

The early returns at Lines 1576 and 2192 bypass the new fixture capability checks and setup. Consequently, repeated load --query-profile fixture-dependent calls leave older Neo4j/Memgraph backups unmodified despite the documented profile-aware loading behavior.

Restore the cached backup, apply fixture/index setup, and persist the upgraded backup instead of returning immediately for fixture-dependent profiles.

Also applies to: 2160-2275

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main.rs` around lines 1549 - 1661, Update the cached-backup early-return
paths in both Neo4j and Memgraph loading flows to handle fixture-dependent query
profiles before returning. Restore the cached backup, run the required fixture
capability checks and fixture/index setup using the existing profile-aware
helpers, then persist the upgraded backup; retain the current skip behavior for
profiles that do not require fixtures.
src/aggregator.rs (1)

855-915: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Preserve timeout metric presence separately from its value.

Line 915 appends 0.0 when no timeout sample exists, making missing data indistinguishable from a valid 0% timeout rate. Consequently, the dashboard either hides legitimate zero values or shows the column for unsupported vendors.

Only append the slot when a sample exists, then let the dashboard use slot presence.

Proposed producer/consumer fix
-            arr.push(timeout_rates.get(&query).copied().unwrap_or(0.0));
+            if let Some(rate) = timeout_rates.get(&query) {
+                arr.push(*rate);
+            }
-        histogram.length > 11 &&
-        Number.isFinite(histogram[11]) &&
-        histogram[11] > 0
+        histogram.length > 11 &&
+        Number.isFinite(histogram[11])
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/aggregator.rs` around lines 855 - 915, Update the timeout-rate handling
in the aggregation flow around timeout_rates and the final arr.push so missing
timeout samples remain distinguishable from valid 0.0 values. Track whether each
query has an actual timeout metric sample, append the timeout slot only for
those queries, and omit it otherwise; preserve valid zero-rate values and
existing percentile slots.
src/memgraph_client.rs (1)

181-223: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Propagate result-stream errors instead of treating them as success.
while let Ok(Some(row)) stops on stream.next() errors, but the outer path still returns Ok(()), so failed queries are recorded as successful and the latency/success metrics become wrong.

Proposed fix
-            while let Ok(Some(row)) = stream.next().await {
-                trace!("Row: {:?}", row);
-                black_box(row);
+            loop {
+                match stream.next().await {
+                    Ok(Some(row)) => {
+                        trace!("Row: {:?}", row);
+                        black_box(row);
+                    }
+                    Ok(None) => break,
+                    Err(e) => return Err(Neo4rsError(e)),
+                }
             }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/memgraph_client.rs` around lines 181 - 223, Update the result-consumption
loop in the memgraph_query closure to propagate errors returned by stream.next()
instead of terminating successfully. Preserve row tracing and black_box handling
for successful rows, and ensure the propagated error reaches the existing
memgraph_result match so failure metrics and return values are recorded
correctly.
🧹 Nitpick comments (1)
src/queries_repository.rs (1)

987-1093: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add profile-by-flavour catalog tests.

Cover that baseline excludes these queries, extended-core adds the temporal query only for supported flavours, and fixture-dependent adds all three exact smoke-query names. Execution-path capability detection depends on these names.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/queries_repository.rs` around lines 987 - 1093, Add catalog-level tests
for the query registration flow around includes_extended_core() and
includes_fixture_dependent(). Verify baseline profiles omit the three smoke
queries and temporal_spatial_roundtrip, extended-core registers
temporal_spatial_roundtrip only for supported flavours, and fixture-dependent
registers all three exact smoke-query names; preserve the existing
flavour-specific query text and names.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main.rs`:
- Around line 421-427: Update is_timeout_error to recognize timeout text in both
OtherError and Neo4rsError BenchmarkError variants, using the error message
exposed by each variant and preserving case-insensitive matching. Ensure
server-side Neo4j timeout errors are classified as timeouts rather than
failures.

In `@src/memgraph_client.rs`:
- Around line 361-551: Update ensure_post_phase1_fixtures_ready to accept the
validated required fixture set from run_memgraph, and create, seed,
readiness-check, and probe only the requested fixture families (vector, node
full-text, and relationship full-text). Preserve retries and readiness
requirements for selected capabilities, while avoiding queries against
unavailable unrelated modules.

In `@src/neo4j_client.rs`:
- Around line 342-533: Update ensure_post_phase1_fixtures_ready to accept the
validated FixtureQueryPresence requirements and conditionally create, seed,
probe, and require readiness only for fixture query types actually present. Keep
unrelated fixture branches disabled, and preserve successful completion when the
supplied requirements contain only a subset of features.

---

Outside diff comments:
In `@src/aggregator.rs`:
- Around line 855-915: Update the timeout-rate handling in the aggregation flow
around timeout_rates and the final arr.push so missing timeout samples remain
distinguishable from valid 0.0 values. Track whether each query has an actual
timeout metric sample, append the timeout slot only for those queries, and omit
it otherwise; preserve valid zero-rate values and existing percentile slots.

In `@src/main.rs`:
- Around line 1549-1661: Update the cached-backup early-return paths in both
Neo4j and Memgraph loading flows to handle fixture-dependent query profiles
before returning. Restore the cached backup, run the required fixture capability
checks and fixture/index setup using the existing profile-aware helpers, then
persist the upgraded backup; retain the current skip behavior for profiles that
do not require fixtures.

In `@src/memgraph_client.rs`:
- Around line 181-223: Update the result-consumption loop in the memgraph_query
closure to propagate errors returned by stream.next() instead of terminating
successfully. Preserve row tracing and black_box handling for successful rows,
and ensure the propagated error reaches the existing memgraph_result match so
failure metrics and return values are recorded correctly.

---

Nitpick comments:
In `@src/queries_repository.rs`:
- Around line 987-1093: Add catalog-level tests for the query registration flow
around includes_extended_core() and includes_fixture_dependent(). Verify
baseline profiles omit the three smoke queries and temporal_spatial_roundtrip,
extended-core registers temporal_spatial_roundtrip only for supported flavours,
and fixture-dependent registers all three exact smoke-query names; preserve the
existing flavour-specific query text and names.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ddd83484-5891-413b-acbf-bc75d9d12d6c

📥 Commits

Reviewing files that changed from the base of the PR and between 1a7f18e and b71140c.

📒 Files selected for processing (16)
  • QUERY_EXPLANATIONS_AND_SAMPLES.md
  • readme.md
  • scripts/run_large_benchmark.sh
  • scripts/run_medium_benchmark.sh
  • scripts/run_small_benchmark.sh
  • src/aggregator.rs
  • src/cli.rs
  • src/falkor/falkor_driver.rs
  • src/lib.rs
  • src/main.rs
  • src/memgraph_client.rs
  • src/neo4j_client.rs
  • src/queries_repository.rs
  • ui/app/components/VerticalBarChart.tsx
  • ui/app/components/dashboard.tsx
  • ui/components/ui/SidebarNavigation.tsx

Comment thread src/main.rs
Comment on lines +421 to +427
fn is_timeout_error(err: &benchmark::error::BenchmarkError) -> bool {
matches!(
err,
benchmark::error::BenchmarkError::OtherError(message)
if message.to_ascii_lowercase().contains("timeout")
)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the relevant function and error type definitions.
rg -n "fn is_timeout_error|enum BenchmarkError|Neo4rsError|OtherError" src/main.rs src -S

# Show the surrounding code around is_timeout_error.
sed -n '390,455p' src/main.rs

# Find the BenchmarkError definition.
rg -n "pub enum BenchmarkError|enum BenchmarkError" src -S

Repository: FalkorDB/benchmark

Length of output: 14171


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,120p' src/error.rs
printf '\n---\n'
sed -n '260,380p' src/main.rs
printf '\n---\n'
rg -n "is_timeout_error\\(" src/main.rs src -S

Repository: FalkorDB/benchmark

Length of output: 5747


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '2070,2125p' src/main.rs

Repository: FalkorDB/benchmark

Length of output: 2596


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n "TokioElapsed|timeout" src/memgraph_client.rs src/neo4j_client.rs src/main.rs src/error.rs -S
printf '\n---\n'
sed -n '1,260p' src/memgraph_client.rs

Repository: FalkorDB/benchmark

Length of output: 10416


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,180p' src/neo4j_client.rs
printf '\n---\n'
sed -n '180,260p' src/neo4j_client.rs
printf '\n---\n'
sed -n '1,120p' src/error.rs

Repository: FalkorDB/benchmark

Length of output: 11477


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '70,135p' src/neo4j_client.rs

Repository: FalkorDB/benchmark

Length of output: 2581


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n "map_err\\(Neo4rsError\\)|tokio::time::timeout|TokioElapsed|timeout\\(" src/neo4j_client.rs src/memgraph_client.rs src/main.rs src/error.rs -S

Repository: FalkorDB/benchmark

Length of output: 2874


🌐 Web query:

neo4rs error timeout message server-side timeout Neo4rsError Display

💡 Result:

In the neo4rs library, errors are handled via the Error enum, which differentiates between internal library errors and errors returned by the Neo4j database [1][2]. When a Neo4j server-side error occurs, it is encapsulated within the Error::Neo4j(Neo4jError) variant [1]. The Neo4jError struct contains both a code and a message [2][3]. When a transaction exceeds a configured timeout on the server side, Neo4j typically returns a specific status code, such as Neo.ClientError.Transaction.TransactionTimedOut [4]. The neo4rs library displays these as "Neo4j error [code]: [message]" [2]. Regarding timeouts specifically: 1. Server-Side Transaction Timeouts: If a query exceeds a transaction timeout defined on the server or via transaction configuration, the database returns a Neo4j error with a code indicating the timeout [4][5]. These are surfaced in neo4rs as Error::Neo4j(Neo4jError), and you can inspect the code and message methods on the Neo4jError struct to verify the specific cause [2][3]. 2. Client-Side/Connection Timeouts: Recent updates to neo4rs (e.g., PR #283) have introduced explicit support for connection and read timeouts [6]. If a connection attempt or read operation exceeds these configured limits, the library may return an Error::ConnectionTimedOut (or similar, depending on your specific version) rather than a server-side Neo4j error [6]. To debug timeout issues, it is recommended to: - Check the code value of the Neo4jError to see if it matches known Neo4j transaction timeout codes [4][2]. - Inspect the message for details, as server-side messages often provide context on whether the transaction was terminated due to a timeout [5][2]. - Ensure your neo4rs version is up-to-date to utilize native connection timeout features if you suspect network or pool-level latency [6].

Citations:


Count timeout-like errors across BenchmarkError variants. is_timeout_error only checks OtherError, so a server-side Neo4j timeout surfaced as BenchmarkError::Neo4rsError(...) is recorded as a failure and the timeout rate is understated. Matching the error text across variants would cover both paths.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main.rs` around lines 421 - 427, Update is_timeout_error to recognize
timeout text in both OtherError and Neo4rsError BenchmarkError variants, using
the error message exposed by each variant and preserving case-insensitive
matching. Ensure server-side Neo4j timeout errors are classified as timeouts
rather than failures.

Comment thread src/memgraph_client.rs
Comment on lines +361 to +551
pub async fn ensure_post_phase1_fixtures_ready(&self) -> BenchmarkResult<()> {
// Required fixture indexes (Memgraph currently has no IF NOT EXISTS for these forms).
self.run_query_allowing_already_exists(
"create_text_index_user_ft",
"CREATE TEXT INDEX bench_user_ft_idx ON :User(ft_text)",
)
.await?;
self.run_query_allowing_already_exists(
"create_text_edge_index_friend_ft",
"CREATE TEXT EDGE INDEX bench_friend_ft_idx ON :Friend(ft_text)",
)
.await?;
self.run_query_allowing_already_exists(
"create_vector_index_user_embedding",
"CREATE VECTOR INDEX bench_user_embedding_idx ON :User(embedding) WITH CONFIG {\"dimension\": 3, \"capacity\": 1000000, \"metric\": \"cos\"}",
)
.await?;

// Deterministic fixture seeding (idempotent SET updates).
self.run_query_no_results(
"MATCH (u:User) \
WHERE toInteger(u.id) % 97 = 0 \
SET \
u.ft_text = 'fixture_alice user_' + toString(u.id), \
u.embedding = [ \
toFloat((toInteger(u.id) % 10) + 1) / 10.0, \
toFloat(((toInteger(u.id) + 3) % 10) + 1) / 10.0, \
toFloat(((toInteger(u.id) + 6) % 10) + 1) / 10.0 \
]",
)
.await?;
self.run_query_no_results(
"MATCH (s:User)-[r:Friend]->(d:User) \
WHERE toInteger(s.id) % 97 = 0 \
SET r.ft_text = 'fixture_blue edge_' + toString(s.id) + '_' + toString(d.id)",
)
.await?;

const MAX_ATTEMPTS: u32 = 18;
const DELAY_SECS: u64 = 5;

for attempt in 1..=MAX_ATTEMPTS {
let user_ft_count = match self
.query_single_u64(
"MATCH (u:User) \
WHERE u.ft_text CONTAINS 'fixture_alice' \
RETURN count(u) AS count",
)
.await
{
Ok(v) => v,
Err(e) => {
info!(
"Error while checking Memgraph user fulltext fixture readiness (attempt {}/{}): {}",
attempt, MAX_ATTEMPTS, e
);
tokio::time::sleep(Duration::from_secs(DELAY_SECS)).await;
continue;
}
};
let rel_ft_count = match self
.query_single_u64(
"MATCH ()-[r:Friend]->() \
WHERE r.ft_text CONTAINS 'fixture_blue' \
RETURN count(r) AS count",
)
.await
{
Ok(v) => v,
Err(e) => {
info!(
"Error while checking Memgraph relationship fulltext fixture readiness (attempt {}/{}): {}",
attempt, MAX_ATTEMPTS, e
);
tokio::time::sleep(Duration::from_secs(DELAY_SECS)).await;
continue;
}
};
let embedding_count = match self
.query_single_u64(
"MATCH (u:User) \
WHERE u.embedding IS NOT NULL \
RETURN count(u) AS count",
)
.await
{
Ok(v) => v,
Err(e) => {
info!(
"Error while checking Memgraph vector fixture readiness (attempt {}/{}): {}",
attempt, MAX_ATTEMPTS, e
);
tokio::time::sleep(Duration::from_secs(DELAY_SECS)).await;
continue;
}
};

let vector_hits = match self
.query_single_u64(
"CALL vector_search.search('bench_user_embedding_idx', 1, [0.1, 0.2, 0.3]) \
YIELD node, similarity \
RETURN count(node) AS count",
)
.await
{
Ok(v) => v,
Err(e) => {
info!(
"Error while probing Memgraph vector index readiness (attempt {}/{}): {}",
attempt, MAX_ATTEMPTS, e
);
tokio::time::sleep(Duration::from_secs(DELAY_SECS)).await;
continue;
}
};
let fulltext_node_hits = match self
.query_single_u64(
"CALL text_search.search('bench_user_ft_idx', 'data.ft_text:fixture_alice') \
YIELD node, score \
RETURN count(node) AS count",
)
.await
{
Ok(v) => v,
Err(e) => {
info!(
"Error while probing Memgraph fulltext node index readiness (attempt {}/{}): {}",
attempt, MAX_ATTEMPTS, e
);
tokio::time::sleep(Duration::from_secs(DELAY_SECS)).await;
continue;
}
};
let fulltext_rel_hits = match self
.query_single_u64(
"CALL text_search.search_edges('bench_friend_ft_idx', 'data.ft_text:fixture_blue') \
YIELD edge, score \
RETURN count(edge) AS count",
)
.await
{
Ok(v) => v,
Err(e) => {
info!(
"Error while probing Memgraph fulltext relationship index readiness (attempt {}/{}): {}",
attempt, MAX_ATTEMPTS, e
);
tokio::time::sleep(Duration::from_secs(DELAY_SECS)).await;
continue;
}
};

if user_ft_count > 0
&& rel_ft_count > 0
&& embedding_count > 0
&& vector_hits > 0
&& fulltext_node_hits > 0
&& fulltext_rel_hits > 0
{
info!(
"Memgraph post-phase fixtures ready after {} attempt(s): users_ft={}, rel_ft={}, embedding={}, vector_hits={}, fulltext_node_hits={}, fulltext_rel_hits={}",
attempt,
user_ft_count,
rel_ft_count,
embedding_count,
vector_hits,
fulltext_node_hits,
fulltext_rel_hits
);
return Ok(());
}

info!(
"Memgraph post-phase fixtures not fully ready yet (attempt {}/{}): users_ft={}, rel_ft={}, embedding={}, vector_hits={}, fulltext_node_hits={}, fulltext_rel_hits={}",
attempt,
MAX_ATTEMPTS,
user_ft_count,
rel_ft_count,
embedding_count,
vector_hits,
fulltext_node_hits,
fulltext_rel_hits
);
tokio::time::sleep(Duration::from_secs(DELAY_SECS)).await;
}

Err(OtherError(
"Timed out waiting for Memgraph post-phase fixture indexes/procedures to become ready"
.to_string(),
))
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Prepare only the fixture capabilities required by the query file.

run_memgraph selectively validates FixtureQueryPresence, but this method always creates and probes vector, node-text, and edge-text fixtures. Partial fixture query files can pass validation and subsequently fail because an unrelated module is unavailable.

Thread the required fixture set into this method and conditionally seed/probe each family.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/memgraph_client.rs` around lines 361 - 551, Update
ensure_post_phase1_fixtures_ready to accept the validated required fixture set
from run_memgraph, and create, seed, readiness-check, and probe only the
requested fixture families (vector, node full-text, and relationship full-text).
Preserve retries and readiness requirements for selected capabilities, while
avoiding queries against unavailable unrelated modules.

Comment thread src/neo4j_client.rs
Comment on lines +342 to +533
pub async fn ensure_post_phase1_fixtures_ready(&self) -> BenchmarkResult<()> {
// Required fixture indexes (idempotent creation).
self.run_query_no_results(
"CREATE FULLTEXT INDEX bench_user_ft_idx IF NOT EXISTS \
FOR (u:User) ON EACH [u.ft_text]",
)
.await?;
self.run_query_no_results(
"CREATE FULLTEXT INDEX bench_friend_ft_idx IF NOT EXISTS \
FOR ()-[r:Friend]-() ON EACH [r.ft_text]",
)
.await?;
self.run_query_no_results(
"CREATE VECTOR INDEX bench_user_embedding_idx IF NOT EXISTS \
FOR (u:User) ON (u.embedding) \
OPTIONS {indexConfig: {`vector.dimensions`: 3, `vector.similarity_function`: 'cosine'}}",
)
.await?;

// Deterministic fixture seeding (idempotent SET updates).
self.run_query_no_results(
"MATCH (u:User) \
WHERE toInteger(u.id) % 97 = 0 \
SET \
u.ft_text = 'fixture_alice user_' + toString(u.id), \
u.embedding = [ \
toFloat((toInteger(u.id) % 10) + 1) / 10.0, \
toFloat(((toInteger(u.id) + 3) % 10) + 1) / 10.0, \
toFloat(((toInteger(u.id) + 6) % 10) + 1) / 10.0 \
]",
)
.await?;
self.run_query_no_results(
"MATCH (s:User)-[r:Friend]->(d:User) \
WHERE toInteger(s.id) % 97 = 0 \
SET r.ft_text = 'fixture_blue edge_' + toString(s.id) + '_' + toString(d.id)",
)
.await?;

const MAX_ATTEMPTS: u32 = 18;
const DELAY_SECS: u64 = 5;

for attempt in 1..=MAX_ATTEMPTS {
let user_ft_count = match self
.query_single_u64(
"MATCH (u:User) \
WHERE u.ft_text CONTAINS 'fixture_alice' \
RETURN count(u) AS count",
)
.await
{
Ok(v) => v,
Err(e) => {
info!(
"Error while checking Neo4j user fulltext fixture readiness (attempt {}/{}): {}",
attempt, MAX_ATTEMPTS, e
);
tokio::time::sleep(Duration::from_secs(DELAY_SECS)).await;
continue;
}
};
let rel_ft_count = match self
.query_single_u64(
"MATCH ()-[r:Friend]->() \
WHERE r.ft_text CONTAINS 'fixture_blue' \
RETURN count(r) AS count",
)
.await
{
Ok(v) => v,
Err(e) => {
info!(
"Error while checking Neo4j relationship fulltext fixture readiness (attempt {}/{}): {}",
attempt, MAX_ATTEMPTS, e
);
tokio::time::sleep(Duration::from_secs(DELAY_SECS)).await;
continue;
}
};
let embedding_count = match self
.query_single_u64(
"MATCH (u:User) \
WHERE u.embedding IS NOT NULL \
RETURN count(u) AS count",
)
.await
{
Ok(v) => v,
Err(e) => {
info!(
"Error while checking Neo4j vector fixture readiness (attempt {}/{}): {}",
attempt, MAX_ATTEMPTS, e
);
tokio::time::sleep(Duration::from_secs(DELAY_SECS)).await;
continue;
}
};

let vector_hits = match self
.query_single_u64(
"CALL db.index.vector.queryNodes('bench_user_embedding_idx', 1, [0.1, 0.2, 0.3]) \
YIELD node, score \
RETURN count(node) AS count",
)
.await
{
Ok(v) => v,
Err(e) => {
info!(
"Error while probing Neo4j vector index readiness (attempt {}/{}): {}",
attempt, MAX_ATTEMPTS, e
);
tokio::time::sleep(Duration::from_secs(DELAY_SECS)).await;
continue;
}
};
let fulltext_node_hits = match self
.query_single_u64(
"CALL db.index.fulltext.queryNodes('bench_user_ft_idx', 'fixture_alice') \
YIELD node, score \
RETURN count(node) AS count",
)
.await
{
Ok(v) => v,
Err(e) => {
info!(
"Error while probing Neo4j fulltext node index readiness (attempt {}/{}): {}",
attempt, MAX_ATTEMPTS, e
);
tokio::time::sleep(Duration::from_secs(DELAY_SECS)).await;
continue;
}
};
let fulltext_rel_hits = match self
.query_single_u64(
"CALL db.index.fulltext.queryRelationships('bench_friend_ft_idx', 'fixture_blue') \
YIELD relationship, score \
RETURN count(relationship) AS count",
)
.await
{
Ok(v) => v,
Err(e) => {
info!(
"Error while probing Neo4j fulltext relationship index readiness (attempt {}/{}): {}",
attempt, MAX_ATTEMPTS, e
);
tokio::time::sleep(Duration::from_secs(DELAY_SECS)).await;
continue;
}
};

if user_ft_count > 0
&& rel_ft_count > 0
&& embedding_count > 0
&& vector_hits > 0
&& fulltext_node_hits > 0
&& fulltext_rel_hits > 0
{
info!(
"Neo4j post-phase fixtures ready after {} attempt(s): users_ft={}, rel_ft={}, embedding={}, vector_hits={}, fulltext_node_hits={}, fulltext_rel_hits={}",
attempt,
user_ft_count,
rel_ft_count,
embedding_count,
vector_hits,
fulltext_node_hits,
fulltext_rel_hits
);
return Ok(());
}

info!(
"Neo4j post-phase fixtures not fully ready yet (attempt {}/{}): users_ft={}, rel_ft={}, embedding={}, vector_hits={}, fulltext_node_hits={}, fulltext_rel_hits={}",
attempt,
MAX_ATTEMPTS,
user_ft_count,
rel_ft_count,
embedding_count,
vector_hits,
fulltext_node_hits,
fulltext_rel_hits
);
tokio::time::sleep(Duration::from_secs(DELAY_SECS)).await;
}

Err(OtherError(
"Timed out waiting for Neo4j post-phase fixture indexes/procedures to become ready"
.to_string(),
))
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Scope fixture preparation to the queries actually present.

run_neo4j validates a selective FixtureQueryPresence, but this method always creates and probes vector, node-fulltext, and relationship-fulltext fixtures. A file containing only one fixture query can therefore pass validation and then fail on an unrelated feature.

Pass fixture requirements into this method and guard each setup/readiness branch accordingly.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/neo4j_client.rs` around lines 342 - 533, Update
ensure_post_phase1_fixtures_ready to accept the validated FixtureQueryPresence
requirements and conditionally create, seed, probe, and require readiness only
for fixture query types actually present. Keep unrelated fixture branches
disabled, and preserve successful completion when the supplied requirements
contain only a subset of features.

@shahar-biron
shahar-biron merged commit c92f4ad into master Jul 12, 2026
12 checks passed
@shahar-biron
shahar-biron deleted the extend-query-profiles branch July 12, 2026 07:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant