Summary
The secondary node property index landed in #1777 works, but it is reachable from exactly one query shape. Every other seeded shape builds the index, keeps it resident and valid, and then never consults it — falling back to a full scan of the node frame.
Measured on official LDBC SNB SF1 (pandas, master 84be35fb, 4 runs/arm, position-balanced ABBA+BAAB), property index ON vs OFF:
| query |
OFF ms |
ON ms |
speedup |
verdict |
| IS1 seed-lookup |
725.6 |
759.6 |
0.96x |
built, never engaged |
| IS2 expand-order-limit |
826.0 |
842.1 |
0.98x |
built, never engaged |
| IS4 message-content |
227.9 |
231.8 |
0.98x |
built, never engaged |
| IS5 message-creator |
549.3 |
580.1 |
0.95x |
built, never engaged |
| IS6 message-forum |
4286.6 |
4633.1 |
0.93x |
built, never engaged |
| IS7 message-replies |
71.7 |
19.5 |
3.68x |
engaged |
Non-IS7 deltas (0.5-7%) sit inside per-arm run-to-run spread (1.4-16.4%), i.e. no effect. The IS7 arms do not overlap (ON 18.6-20.0, OFF 69.8-73.0).
Root cause
The property index is consulted at one seam only: connected_bindings — multi-alias rows() over a connected fixed-hop path (_seed_rows_via_property_index). IS7 is the only interactive-short query with that shape.
- IS1 / IS2 / IS4 / IS5 use single-alias
rows(source=...) or a node-only lookup and never reach the seam at all (instrumented node_prop_seam_probes: 0).
- IS6 reaches the seam and is declined
unsupported_shape.
The single-alias seeded path (chain_fast_paths._resident_seed_indexes, feeding the destination_return seam) requires the seed predicate to be on the node-id binding. When the seed is on any other property it full-scans the node frame, without ever asking whether a resident property index covers that column.
That is the same degeneracy #1777 was filed to fix — SNB binds nodes on __pgbench_node_key__ while every seed predicate is on the id property — but it was only fixed for the connected-bindings shape.
Isolated reproduction
Same one-hop query at SF1 node counts, differing only in binding shape:
| shape |
without index |
with index |
property-index calls |
| multi-alias (connected bindings) |
737.8 ms |
15.1 ms |
engaged |
single-alias (rows(source=...)) |
682.6 ms |
639.2 ms |
0 |
Why it matters
This is the difference between #1777 accelerating one query and accelerating five. IS1/IS2/IS4/IS5 are all seeded point/short lookups — exactly the workload class the index exists for, and exactly the axis where we are weakest against indexed graph databases.
Suggested direction
Have the single-alias seeded path consult the resident property index before falling back to a scan, reusing #1777's existing lookup and cost gate rather than adding a second mechanism. Engagement must stay structural — driven by predicate/dtype/index preconditions, never by query or schema recognition — and must keep the existing decline-with-reason behaviour so a non-engaging shape is still explainable via index_trace().
Note on verification
index_trace() alone is not sufficient to prove engagement here: it reports the traversal seam's verdict, not where the seed rows came from. Confirming this required a pass-through counter on _seed_rows_via_property_index alongside the trace. Any fix should land with an engagement signal that is directly observable, otherwise 'built' will keep being mistaken for 'used'.
Found while re-baselining the LDBC lane after #1776/#1777 merged. Follows #1777.
Summary
The secondary node property index landed in #1777 works, but it is reachable from exactly one query shape. Every other seeded shape builds the index, keeps it resident and valid, and then never consults it — falling back to a full scan of the node frame.
Measured on official LDBC SNB SF1 (pandas, master
84be35fb, 4 runs/arm, position-balanced ABBA+BAAB), property index ON vs OFF:Non-IS7 deltas (0.5-7%) sit inside per-arm run-to-run spread (1.4-16.4%), i.e. no effect. The IS7 arms do not overlap (ON 18.6-20.0, OFF 69.8-73.0).
Root cause
The property index is consulted at one seam only:
connected_bindings— multi-aliasrows()over a connected fixed-hop path (_seed_rows_via_property_index). IS7 is the only interactive-short query with that shape.rows(source=...)or a node-only lookup and never reach the seam at all (instrumentednode_prop_seam_probes: 0).unsupported_shape.The single-alias seeded path (
chain_fast_paths._resident_seed_indexes, feeding thedestination_returnseam) requires the seed predicate to be on the node-id binding. When the seed is on any other property it full-scans the node frame, without ever asking whether a resident property index covers that column.That is the same degeneracy #1777 was filed to fix — SNB binds nodes on
__pgbench_node_key__while every seed predicate is on theidproperty — but it was only fixed for the connected-bindings shape.Isolated reproduction
Same one-hop query at SF1 node counts, differing only in binding shape:
rows(source=...))Why it matters
This is the difference between #1777 accelerating one query and accelerating five. IS1/IS2/IS4/IS5 are all seeded point/short lookups — exactly the workload class the index exists for, and exactly the axis where we are weakest against indexed graph databases.
Suggested direction
Have the single-alias seeded path consult the resident property index before falling back to a scan, reusing #1777's existing lookup and cost gate rather than adding a second mechanism. Engagement must stay structural — driven by predicate/dtype/index preconditions, never by query or schema recognition — and must keep the existing decline-with-reason behaviour so a non-engaging shape is still explainable via
index_trace().Note on verification
index_trace()alone is not sufficient to prove engagement here: it reports the traversal seam's verdict, not where the seed rows came from. Confirming this required a pass-through counter on_seed_rows_via_property_indexalongside the trace. Any fix should land with an engagement signal that is directly observable, otherwise 'built' will keep being mistaken for 'used'.Found while re-baselining the LDBC lane after #1776/#1777 merged. Follows #1777.