From 97dacea4011b93d9adbaa49a3612c9417a200b61 Mon Sep 17 00:00:00 2001 From: Xiaoyang Tan Date: Thu, 13 Aug 2026 15:44:37 -0700 Subject: [PATCH 1/2] fix(tgbdiff): propagate DepsCSR error from buildReverseCSR buildReverseCSR silently swallowed DepsCSR errors and returned an empty reverse adjacency, causing the BFS to produce distance -1 for all non-seed targets without any error signal to the caller. Propagate the error so compareInternal fails explicitly on a corrupt deps column. --- internal/tgbdiff/compare.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/internal/tgbdiff/compare.go b/internal/tgbdiff/compare.go index 7b325a3e..e4e7c6f0 100644 --- a/internal/tgbdiff/compare.go +++ b/internal/tgbdiff/compare.go @@ -377,7 +377,10 @@ func compareInternal(ctx context.Context, before, after *tgb.Reader, opts Option t3 := time.Now() nAfter := nAfterNodes var csrDepBuf []int32 - csrOffsets, csrTargets := buildReverseCSR(after, nAfter, &csrDepBuf) + csrOffsets, csrTargets, err := buildReverseCSR(after, nAfter, &csrDepBuf) + if err != nil { + return nil, phases, cnt, err + } phases.ReverseCSR = time.Since(t3) // ── Phase 3b: BFS ──────────────────────────────────────────────────────── @@ -926,13 +929,10 @@ func attrsChanged(before, after map[string]string) bool { // targets[offsets[i]:offsets[i+1]]. // // depBuf is a scratch buffer reused across calls to avoid allocation. -func buildReverseCSR(after *tgb.Reader, n int, depBuf *[]int32) (offsets []int32, targets []int32) { - // Decode the forward edges once into CSR form. Calling Deps per node walks - // the reader's offset table twice over and allocates per node; DepsCSR is a - // single sequential pass over the column. +func buildReverseCSR(after *tgb.Reader, n int, depBuf *[]int32) (offsets []int32, targets []int32, err error) { fwdOff, fwdTgt, err := after.DepsCSR() if err != nil { - return make([]int32, n+1), nil + return nil, nil, err } // Pass 1: count in-degrees. @@ -967,5 +967,5 @@ func buildReverseCSR(after *tgb.Reader, n int, depBuf *[]int32) (offsets []int32 } } - return offsets, targets + return offsets, targets, nil } From 31c52dec95ca21c31fa475d4d7922753f25fef8d Mon Sep 17 00:00:00 2001 From: Xiaoyang Tan Date: Thu, 13 Aug 2026 15:47:00 -0700 Subject: [PATCH 2/2] fix(tgbdiff): restore DepsCSR rationale comment in buildReverseCSR --- internal/tgbdiff/compare.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/tgbdiff/compare.go b/internal/tgbdiff/compare.go index e4e7c6f0..b561bf95 100644 --- a/internal/tgbdiff/compare.go +++ b/internal/tgbdiff/compare.go @@ -930,6 +930,9 @@ func attrsChanged(before, after map[string]string) bool { // // depBuf is a scratch buffer reused across calls to avoid allocation. func buildReverseCSR(after *tgb.Reader, n int, depBuf *[]int32) (offsets []int32, targets []int32, err error) { + // Decode the forward edges once into CSR form. Calling Deps per node walks + // the reader's offset table twice over and allocates per node; DepsCSR is a + // single sequential pass over the column. fwdOff, fwdTgt, err := after.DepsCSR() if err != nil { return nil, nil, err