Skip to content

fix(view): prepare the draw.io import baseline once per rendering - #1694

Merged
HuiJun merged 2 commits into
developfrom
fix/drawio-round-trip-test-cost
Oct 29, 2026
Merged

HuiJun merged 2 commits into
developfrom
fix/drawio-round-trip-test-cost

Conversation

@devin-ai-integration

Copy link
Copy Markdown
Contributor

What and why

TestDrawioRoundTrip (added by #1679) took ~27 min under the race detector: ok internal/check/edit 1667s on #1679's own Actions run, and on develop CircleCI's Go race tests job killed the package at its 30-minute go test -race -pgo=off -timeout 30m budget (panic: test timed out after 30m0s, TestDrawioRoundTrip (29m35s)). The Actions shards allow 55 min per package (make test-shard), which is why only CircleCI went red.

Cause. Rendering.ImportDrawio(file, options) rebuilt its baseline on every call: drawioPrepare lays the rendering out and routes every edge through libavoid (wazero), then the writer's document is serialised and parsed back, and only then is file diffed against it. The baseline depends on the rendering and the options alone, not on the file, yet the test imported the same rendering 1 (untouched export) + up to E (trial imports of drawioEdge's pick, one per candidate edge) + 7 × 2 (each mutation's import and the re-import against the edited model) times per golden, and each import re-routed the whole rendering.

Where the time went (go test -race -run TestDrawioRoundTrip -cpuprofile, go tool pprof -top -cum, before the change, 8 vCPU): drawioImport 75 % of testing.tRunner's samples, all of it ImportDrawio → drawioPrepare → routeEdges → route.routeAll → wazero callEngine.callWithStack → callEngine.cloneStack (14 % of all samples): wazero's exnref try_table entry handler clones the whole wasm stack on every C++ try, and those allocations are what the GC (43.5 % of samples, scanObject/gcBgMarkWorker) and TSan (__tsan::MemoryAccessRangeT, 18.8 %) then chew on. Without the race detector the same profile is 42 % routing / 37.7 % cloneStack + GC. Model loading, rendering, Apply and the semantic diff itself are milliseconds. Pooling wazero sessions does not help (measured), and wazero main still clones the stack there, so the lever is to route once, not to route faster.

Change.

  • internal/ir/view: Rendering.DrawioImporter(options) prepares the baseline once (normalised rendering, writer, parsed baseline document, node/port index); DrawioImporter.Import(file) runs the matcher against it. ImportDrawio now delegates to the two, so the CLI and LSP callers are unchanged in behaviour. The matcher only reads norm, w, base, nodes and ports; its per-file state (framed, paired, matched, out) is still built per Import.
  • internal/check/edit/drawio_test.go: one importer per golden shared by the untouched check, every trial import of pick and every mutation's import, so a trial import is now ReadDrawio + the semantic diff. The re-import against the edited model still prepares its own importer (a different rendering). The per-golden subtests run with t.Parallel(); nothing they touch is shared mutable state (drawioModel builds a fresh model/index/renderer per golden over the frozen shared library base, Apply's observer is mutex-guarded, the routing engine pools sessions under a mutex) and -race reports nothing.

Contract preserved. Every golden under internal/ir/view/testdata/*.drawio.golden is still enumerated (the >= 30 guard stays), all seven drawioMutations and their pickers are untouched, every assertion is the same code, and no timeout, tier or shard changed. A new TestDrawioImporterSharesOneBaseline pins that an importer answers the untouched export and a moved cell exactly as a fresh ImportDrawio does and refuses another view's page.

How it was verified

Machine: 8 vCPU, go1.27.3, linux/amd64.

before after
go test -race -count=1 ./internal/check/edit (whole package) TestDrawioRoundTrip alone ok 1159.8s, 19m24s wall here; 27 min on #1679's Actions run; killed at 30 min on CircleCI ok 136.4s, 2m17s wall
go test -race -count=1 -parallel 1 -run TestDrawioRoundTrip ./internal/check/edit (the importer alone, goldens serial — what a 1-vCPU box would see) — ok 399.0s, 6m40s wall
go test -count=1 ./internal/check/edit (no race) 5m08s wall ok 44.6s, 0m45s wall

Gates: gofmt -l . empty, go vet ./..., go build ./..., make lint (staticcheck + gosec), go test -race -count=1 ./internal/check/edit ./internal/ir/view ./cmd/sysml green, 0 data races.

Checklist

  • make test and make lint pass locally (the touched packages under -race; the full tier runs in CI)
  • Tests added or updated for the change
  • Documentation extended where it already covers the surface — none covers ImportDrawio's internals
  • Changelog entry — not user-visible (an internal API and a test speed-up)
  • baselines regenerated — no gate count moved
  • No internal work-item labels in the body, docs, or changelog

Link to Devin session: https://nasa-jpl-demo.devinenterprise.com/sessions/1ce956f62ffa43ccbe9697e4134ceb2e
Open in Devin Desktop: https://nasa-jpl-demo.devinenterprise.com/desktop/session/1ce956f62ffa43ccbe9697e4134ceb2e?variant=devin
Requested by: @HuiJun

ImportDrawio laid the rendering out and routed every edge anew on each call to
rebuild the same baseline. DrawioImporter prepares it once and Import diffs any
file of the rendering against it; ImportDrawio delegates to it. TestDrawioRoundTrip
reuses one importer per golden, so its trial imports no longer re-route the
rendering, and runs its goldens in parallel.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

@HuiJun
HuiJun marked this pull request as ready for review October 29, 2026 19:02
devin-ai-integration[bot]

This comment was marked as resolved.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
@HuiJun
HuiJun merged commit b25ef52 into develop Oct 29, 2026
30 checks passed
@HuiJun
HuiJun deleted the fix/drawio-round-trip-test-cost branch October 29, 2026 20:23
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