From e4f860dc659b218bf88647565aa924d11d90745c Mon Sep 17 00:00:00 2001 From: Varun Chawla Date: Sun, 22 Feb 2026 19:56:31 -0800 Subject: [PATCH] fix(query): evict highest-cost node when MaxFrontierSize is exceeded in shortest path When MaxFrontierSize is triggered, the code was calling pq.Pop() directly on the slice instead of using heap.Pop(). This has two problems: 1. pq.Pop() removes the last element of the underlying array, not the highest-cost item. In a min-heap, the last element has no guaranteed cost ordering, so it may remove a low-cost node that's actually needed for the shortest path. 2. Calling pq.Pop() directly (without heap.Pop) doesn't maintain the heap invariant, corrupting the priority queue for subsequent operations. Fix this by adding a removeMax() method that finds and removes the highest-cost item from the priority queue using heap.Remove(). This ensures we always evict the least promising node while keeping the heap valid, so the shortest path is preserved even when the frontier size is limited. Fixes #9577 --- query/shortest.go | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/query/shortest.go b/query/shortest.go index 435942496b2..89fc5d13872 100644 --- a/query/shortest.go +++ b/query/shortest.go @@ -88,6 +88,22 @@ func (h *priorityQueue) Pop() interface{} { return val } +// removeMax removes and returns the highest cost item from the priority queue. +// This is used to evict the least promising node when the frontier exceeds its +// size limit, preserving the lowest cost nodes needed for shortest paths. +func (h *priorityQueue) removeMax() { + if len(*h) == 0 { + return + } + maxIdx := 0 + for i := 1; i < len(*h); i++ { + if (*h)[i].cost > (*h)[maxIdx].cost { + maxIdx = i + } + } + heap.Remove(h, maxIdx) +} + type mapItem struct { attr string cost float64 @@ -406,7 +422,7 @@ func runKShortestPaths(ctx context.Context, sg *SubGraph) ([]*SubGraph, error) { path: route{route: curPath}, } if int64(pq.Len()) > sg.Params.MaxFrontierSize { - pq.Pop() + pq.removeMax() } heap.Push(&pq, node) } @@ -562,7 +578,7 @@ func shortestPath(ctx context.Context, sg *SubGraph) ([]*SubGraph, error) { hop: item.hop + 1, } if int64(pq.Len()) > sg.Params.MaxFrontierSize { - pq.Pop() + pq.removeMax() } heap.Push(&pq, node) } else {