Skip to content

chore: replace github.com/mitchellh/copystructure#31342

Merged
scottrigby merged 2 commits into
helm:mainfrom
TerryHowe:chore-remove-mitchellh-dependency-3
Nov 14, 2025
Merged

chore: replace github.com/mitchellh/copystructure#31342
scottrigby merged 2 commits into
helm:mainfrom
TerryHowe:chore-remove-mitchellh-dependency-3

Conversation

@TerryHowe

@TerryHowe TerryHowe commented Sep 29, 2025

Copy link
Copy Markdown
Contributor

What this PR does / why we need it:

The github.com/mitchellh/copystructure repository has been archived and is no longer maintained.

Partial: #12707

I looked at several other alternatives to replace it and opted to implement an internal replacement because for example:

  • encoding/json can be used for deep copy, but it is hacky and performance is terrible
  • k8s.io/apimachinery has a deep copy, but it seemed a bit outside the scope of what that primarily does
  • https://github.com/brunoga/deep is not very popular, but nice simple implementation
  • The implementation of the functionality needed is pretty simple

The implementation in github.com/mitchellh/copystructure has many features which we were not using, so I expected and got better performance

go test -bench=. -benchmem ./internal/copystructure/
goos: darwin
goarch: arm64
pkg: helm.sh/helm/v4/internal/copystructure
cpu: Apple M1
BenchmarkInternalCopy-8              	  271351	      4429 ns/op	    5264 B/op	     111 allocs/op
BenchmarkMitchellhCopy-8             	   61238	     19645 ns/op	   15008 B/op	     433 allocs/op
BenchmarkInternalCopyLargeMap-8      	    1364	    889158 ns/op	  808347 B/op	   19022 allocs/op
BenchmarkMitchellhCopyLargeMap-8     	     315	   3784941 ns/op	 2130339 B/op	   75045 allocs/op
BenchmarkInternalCopyDeepNested-8    	   44197	     27205 ns/op	   27648 B/op	     651 allocs/op
BenchmarkMitchellhCopyDeepNested-8   	    5334	    226008 ns/op	   95231 B/op	    2696 allocs/op
PASS

Benchmark that is not included in PR https://gist.github.com/TerryHowe/f53f97016389c6d9cc849f573487d44c

Special notes for your reviewer:

If applicable:

  • this PR contains user facing changes (the docs needed label should be applied if so)
  • this PR contains unit tests
  • this PR has been tested for backwards compatibility

@pull-request-size pull-request-size Bot added the size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. label Sep 29, 2025
@TerryHowe

Copy link
Copy Markdown
Contributor Author

The reason for the indirect dependency:

% go mod why github.com/mitchellh/copystructure 
# github.com/mitchellh/copystructure
helm.sh/helm/v4/pkg/action
github.com/Masterminds/sprig/v3
github.com/mitchellh/copystructure

@TerryHowe TerryHowe changed the title chore: remplace github.com/mitchellh/copystructure chore: replace github.com/mitchellh/copystructure Sep 29, 2025
@TerryHowe TerryHowe force-pushed the chore-remove-mitchellh-dependency-3 branch from 7a3fef9 to 632904c Compare September 29, 2025 16:01
Signed-off-by: Terry Howe <terrylhowe@gmail.com>
@TerryHowe TerryHowe force-pushed the chore-remove-mitchellh-dependency-3 branch from 632904c to bee9c1a Compare September 29, 2025 16:30
@benoittgt

Copy link
Copy Markdown
Contributor

That's a great idea ! Thanks for providing benchmarks too.

Comment thread internal/copystructure/copystructure.go Outdated

// Copy performs a deep copy of the given interface{}.
// This implementation handles the specific use cases needed by Helm.
func Copy(src interface{}) (interface{}, error) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Helm only needs this for deep copy of values ie. copying map[string]any. And in particular, 'any' here is only POD types from values.

Would it make sense to restrict our "deep copy" code to meet only these criteria. In particular if this would simplify the implementation and/or provide performance improvements?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In particular, this reflect based approach was (very) non-performant for Wasm builds:
https://github.com/gjenkins8/copystructure_perf

(which may be of interest in the future)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I copied the code here over to that repo:
gjenkins8/copystructure_perf#1

The good news is that this new code is ~4-5 times faster (including for Wasm). The bad news is that Wasm is still 22x slower than native.

Overall, this is still a clear win. So also would be happy for further improvements to occur later.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gjenkins8 added a commit to gjenkins8/copystructure_perf that referenced this pull request Oct 1, 2025
Signed-off-by: George Jenkins <gjenkins8@bloomberg.net>
gjenkins8
gjenkins8 previously approved these changes Oct 1, 2025
@gjenkins8 gjenkins8 added the Has One Approval This PR has one approval. It still needs a second approval to be merged. label Oct 1, 2025
Comment thread internal/copystructure/copystructure.go Outdated
Signed-off-by: Terry Howe <terrylhowe@gmail.com>

@scottrigby scottrigby left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good, incremental improvement, and addresses the package dependency issue 👍 We can follow-up with incremental improvements (for example, potential alternatives to reflect).

@scottrigby scottrigby added refactor and removed Has One Approval This PR has one approval. It still needs a second approval to be merged. labels Nov 14, 2025
@scottrigby scottrigby merged commit b740071 into helm:main Nov 14, 2025
5 checks passed
@scottrigby scottrigby added this to the 4.1.0 milestone Nov 14, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

refactor size/XL Denotes a PR that changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants