Skip to content

fix(controller): remove redundant scheme registration from NewController - #2046

Open
Haseeb-1698 wants to merge 1 commit into
bitnami:mainfrom
Haseeb-1698:fix/concurrent-scheme-registration
Open

fix(controller): remove redundant scheme registration from NewController#2046
Haseeb-1698 wants to merge 1 commit into
bitnami:mainfrom
Haseeb-1698:fix/concurrent-scheme-registration

Conversation

@Haseeb-1698

@Haseeb-1698 Haseeb-1698 commented Sep 4, 2026

Copy link
Copy Markdown

Fixes #2045.

NewController registered the SealedSecret types into client-go's global scheme.Scheme on every call:

utilruntime.Must(ssscheme.AddToScheme(scheme.Scheme))

That registration has already happened by then. ssscheme.AddToScheme is a SchemeBuilder whose only member is v1alpha1.AddToScheme (pkg/client/clientset/versioned/scheme/register.go:17-18), and the v1alpha1 package's own init() runs exactly that against scheme.Scheme (pkg/apis/sealedsecrets/v1alpha1/register.go:24). controller.go imports that package directly, so the init() has run before any controller is built.

The redundancy was harmless while startup was serial. #2018 made the additional-namespace bootstrap parallel (pkg/controller/main.go:321-343, concurrency 16), so prepareController — and through it NewController — now runs from many goroutines at once. runtime.Scheme.AddKnownTypes writes the scheme's internal maps; identical values still count as concurrent map writes, so the process aborts. A single namespace only ever calls it once, which matches default deployments being unaffected.

The fix is to drop the call and its now-unused import. utilruntime (used at lines 267/280/312/320) and scheme (99/543/563/585/604) both stay.

Tests

  • TestPrepareControllerConcurrently — builds controllers from 64 goroutines, mirroring the parallel bootstrap.
  • TestSealedSecretTypesAreRegisteredGlobally — asserts the registration that scheme.Codecs.UniversalDecoder depends on still happens without the removed call, so the deletion cannot silently break decoding.

Measured on linux/amd64, Go 1.27.1, go test -count=1 -run TestPrepareControllerConcurrently ./pkg/controller/...:

Result
Without the fix 5 of 5 runs fail with fatal error: concurrent map writes
With the fix 5 of 5 runs pass

Full package: go test ./pkg/controller/... passes (9.8s), go vet and gofmt clean.

At 16 goroutines the crash only reproduced in 2 of 3 runs, so the test uses 64 to make the failure reliable on unpatched code. It is deterministic in the direction that matters for CI — green on the fixed tree.

One unrelated data race, noted but not fixed here

While validating under -race I hit a second, pre-existing race on the same path, independent of this bug: NewController assigns the package-level maxRetries (pkg/controller/controller.go:122 before this change), so every parallel bootstrap goroutine writes one shared global that processNextItem reads at line 313. All callers currently write the same flag-derived value, so it does not crash — go test -race reports it, plain go test does not, and make test does not use -race.

I have deliberately left it out to keep this PR to the reported crash. Happy to send it as a follow-up if you'd like it fixed — the obvious shape is moving maxRetries onto the Controller struct.

NewController registered the SealedSecret types into client-go's global
scheme on every call. The v1alpha1 package init() already registers the
same types into the same scheme at process start, so the call was
redundant.

It was also harmless while the additional-namespace bootstrap was
serial. Since bitnami#2018 made it parallel, prepareController runs in up to 16
goroutines, each reaching NewController and writing runtime.Scheme's
internal maps concurrently. That aborts the process with "fatal error:
concurrent map writes" whenever --additional-namespaces lists two or
more namespaces. Single-namespace deployments call it once and are
unaffected.

Drop the registration and its now-unused import. Add a regression test
that builds controllers from 64 goroutines, and one asserting the global
registration the controller's decoders rely on still happens without it.

Fixes bitnami#2045.

Signed-off-by: Muhammad Haseeb <i221698@nu.edu.pk>
@Haseeb-1698
Haseeb-1698 force-pushed the fix/concurrent-scheme-registration branch from cac5c15 to eda8a99 Compare September 4, 2026 14:40
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.

bug: v0.39.0+: additional namespaces panic on startup

1 participant