-
Notifications
You must be signed in to change notification settings - Fork 5
Add new ContextBuilder type #39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
| // SINGLE KIND TESTS (adapted from builder_simple_test.go) | ||
| // A lot of these tests are redundant because they test code that just calls through to a Builder anyway. | ||
| // However, duplicating the tests means we can potentially rewrite KindBuilder to not use Builder at all, then deprecate | ||
| // and remove the old Builder. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All the tests from here down are fairly trivial copies from existing tests in builder_simple_test.go, but they use the new ContextBuilder, treating it as a builder of a single kind.
| }) | ||
| } | ||
|
|
||
| // MULTI KIND TESTS (loosely adapted from builder_multi_test.go) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tests from here down are newer / more handcrafted by me, because the multi-kind logic of ContextBuilder is somewhat different than what MultiBuilder can do.
| // Add adds one or more contexts to a ContextBuilder. Only one context of each kind is allowed. | ||
| // If multiple contexts are added with the same kind, the last one fully replaces the previous ones. | ||
| func (cb *ContextBuilder) Add(contexts ...Context) *ContextBuilder { | ||
| for _, c := range contexts { | ||
| if c.Multiple() { | ||
| for _, ic := range c.multiContexts { | ||
| cb.Add(ic) | ||
| } | ||
| } else { | ||
| b := &Builder{} | ||
| b.copyFrom(c) | ||
| cb.singleBuilders[c.Kind()] = b | ||
| } | ||
| } | ||
| return cb | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think something like this is necessary for ContextBuilder to be a full functional replacement to MultiBuilder. In general it's good to be able to have helper functions return individual contexts of different kinds, and some higher level thing can merge them all together.
One notable weird thing about this Add is that it has totally different context-kind-uniqueness rules as MultiBuilder's Add. This one doesn't care if you add duplicate kinds, and the last one wins. I think that's much simpler so we don't have to deal with a new error case, but the discrepancy could be confusing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With relatively little context (haha), I think last-one-wins sounds a lot more intuitive than raising an error!
|
Closing this since I'm no longer prioritizing putting this out there as part of the context propagation project, but I encourage reviving this if we're trying to improve Go ergonomics in general |
Problem
go-sdk-common currently offers a
BuilderandMultiBuilder, to create single-kind and multi-kind contexts, respectively. These offerings have the following limitations:The reason this matters is that we want to encourage incrementally building multi-contexts and passing them around, particularly in server-side codebases. However, to do that requires the ability to easily deep-copy a multi context, and make further updates to new kinds you want to add.
Solution
Introduce a new
ContextBuildertype and associated constructor methodsNewContextBuilder,NewContextFromContextBuilder. The intent is to then deprecateNewBuilderandNewMultiBuilderin favor of this new thing.New syntax
The new ContextBuilder offers a Rust-inspired pattern for building multiple contexts in a single chain.
Calling
Kindboth establishes an inner context with the desired kind and key if it doesn't exist yet, and returns aKindBuilderthat can be used to set more attributes on that inner context. KindBuilders can be chained - callingKindon a KindBuilder returns a new KindBuilder. CallingBuildon any KindBuilder will return the whole (multi)context.Deprecation
MultiBuilder,NewMultiBuilder, andNewMultiare now deprecated.Builderand friends aren't deprecated yet because:ContextBuilderusesBuilderstructs under the hood for holding individual contexts' dataNewBuilderandNewBuilderFromContextare used by some oldlduserstuff, so the linter complainsThe intent is to remove
BuilderandMultiBuilderin the next major version of the Go Server SDK and Go Client SDK.Performance
It's worth noting that performance-wise, ContextBuilder is probably inferior to both Builder and MultiBuilder because it allocates more memory and generates more garbage. This would probably be felt more by customers switching over from
Builder, since Builder was very well optimized for single-kind contexts. There's probably room for some perf optimizations that do fewer allocations in single-kind situations.Testing
New unit tests dedicated to the new
ContextBuilder. A lot ofContextBuilder's implementation utilizes the now-deprecatedBuilderunder the hood, so a lot of the new unit tests duplicate existing tests for the old builder. However, these duplicate tests mean that we can eventually rewrite the new code to share less of the old code, and then remove the old code.