Skip to content

rungen: serialize Subquery creation across concurrent flowgraph branches - #7332

Open
chiliec wants to merge 1 commit into
brimdata:mainfrom
chiliec:fix-concurrent-recursive-subquery
Open

chiliec wants to merge 1 commit into
brimdata:mainfrom
chiliec:fix-concurrent-recursive-subquery

Conversation

@chiliec

@chiliec chiliec commented Sep 18, 2026

Copy link
Copy Markdown

Fixes #7284

Problem

The repro in #7284 (a recursive function whose body is a subquery, called from four fork branches) segfaults:

panic: runtime error: invalid memory address or nil pointer dereference
github.com/brimdata/super/runtime/vam/expr.(*UDF).Call  runtime/vam/expr/udf.go:43

Building with -race shows the cause: the correlated-subquery create closure in compileVamSubquery calls back into the Builder at runtime (from Subquery.Eval when the recursion re-enters an instance that's still evaluating). With several fork branches doing this concurrently, they race on Builder.compiledVamUDFs (compileVamUDFCall inserts the UDF, compiles its body, then deletes the entry). One goroutine can observe another goroutine's half-built UDF with a nil Body and call through it.

Fix

Wrap the runtime-invoked create callback in a Builder mutex so on-demand subquery compilation is serialized. Compile-time construction of the first instance is unchanged.

Test

Added runtime/ztests/op/recursive-subquery-fork.yaml with the issue's query (4 branches × walk(200)count() = 800).

Verification

  • Without the fix, the new ztest panics with the nil dereference above; with it, it passes.
  • go build -race ./cmd/super, then the repro run 3× — 0 DATA RACE reports, output 800, exit 0 (before the fix: 4 races per run plus the crash).
  • go test -short -run 'TestSPQ/runtime/ztests/(op|expr)$' . — all pass, including recursive-subquery, udf, udf-overflow, udf-mutual-recursion.
  • go test -short ./compiler/... ./runtime/vam/..., go vet ./compiler/rungen/, gofmt -l clean.

The correlated-subquery create closure calls back into the Builder to
compile a fresh Subquery instance on demand at runtime. When a recursive
function containing a subquery runs in multiple fork branches, those
branches race on the Builder's maps (compiledVamUDFs), which can leave a
UDF with a nil Body and crash the process.

Guard the runtime create path with a mutex.

Fixes brimdata#7284
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.

Evaluating a recursive function concurrently crashes the process

1 participant