Thanks a lot for the discussion Austin and Galen. I think it would be fine to break the API at this point in time. Moreover, I don't assume that many people have their own statefun.Context implementations. Is it possible in Go to provide a default implementation for a method? Maybe that way the introduction of the new method would not be breaking.
Galen, do you want to open a JIRA issue for this proposal? Cheers, Till On Wed, Feb 23, 2022 at 12:50 AM Galen Warren <ga...@cvillewarrens.com> wrote: > Yeah, good point. I wasn't considering that someone else might be > implementing that interface. Practically, I think that's pretty unlikely, > but good question. > > > > On Tue, Feb 22, 2022 at 6:36 PM Austin Cawley-Edwards < > austin.caw...@gmail.com> wrote: > > > I think the only complication with adding `WithContext` to the interface > > is that it makes it breaking :/ If it were the concrete implementation, > > we'd have more flexibility. I'm not sure what guarantees are on those > > interfaces though – @sjwies...@gmail.com, wdyt? > > > > On Tue, Feb 22, 2022 at 4:19 PM Galen Warren <ga...@cvillewarrens.com> > > wrote: > > > >> I think I would choose the WithContext method at this point, assuming > >> that the implementation keeps any instance of the stateful context > >> immutable (which should be doable). It's the simplest option IMO, > simpler > >> than an adapter approach. Thanks for the suggestion. > >> > >> My slight preference would be to add 'WithContext' as a method to > >> statefun.Context (similar to your Request.WithContext example > >> <https://pkg.go.dev/net/http#Request.WithContext>), as opposed to a > >> top-level function, i.e. statefun.WithContext, but either one could > work. > >> > >> Would that work for you? What do the rest of you think? > >> > >> > >> > >> On Tue, Feb 22, 2022 at 3:52 PM Austin Cawley-Edwards < > >> austin.caw...@gmail.com> wrote: > >> > >>> What does "SomeOtherFunc" need with the statefun context > >>>> > >>>> I think it's hard to answer this question, in a general sense. > >>>> Depending on what is being done, it might need to read a value from > >>>> statefun Storage, write one back, etc. > >>> > >>> > >>> To me, this indicates that the context is responsible for too much and > >>> cannot properly be passed to functions with a distinct purpose. I > think the > >>> `context.Context` shares this design but gets away with it because its > >>> functionality is so constrained and generic (deadlines, cancellation, > >>> values – that's it). > >>> This is getting away from the original question of the thread, but I > >>> bring it up to suggest that we take a more holistic look at the > statefun > >>> Context interface and go with a simpler solution (either the > `WithContext` > >>> method or Till's suggestion) to avoid further muddying the possible > uses. > >>> WDYT? > >>> > >>> Austin > >>> > >>> On Tue, Feb 22, 2022 at 1:14 PM Galen Warren <ga...@cvillewarrens.com> > >>> wrote: > >>> > >>>> One place we could look is the `net/http` Request, which has a > >>>>> `WithContext` method[1] that seems to expose the behavior we're > looking > >>>>> for. > >>>>> > >>>> > >>>> I considered something similar, too, and upon another look, maybe I > >>>> dismissed it too quickly. The concern I had was that an > implementation that > >>>> simply updated the internal context.Context of an existing > statefun.Context > >>>> would violate the assumption that contexts are immutable. However, if > the > >>>> implementation copied all the statefun.Context parts to a new stateful > >>>> context structure and associated them with the passed-in > context.Context, > >>>> that seems like it could work. There's a sync.Mutex in the statefun > context > >>>> structure that we'd have to be careful about. > >>>> > >>>> > >>>>> What does "SomeOtherFunc" need with the statefun context? > >>>>> > >>>> > >>>> I think it's hard to answer this question, in a general sense. > >>>> Depending on what is being done, it might need to read a value from > >>>> statefun Storage, write one back, etc. > >>>> > >>>> The solution that Till proposed seems to fit the example you gave > >>>>> quite well, no? > >>>>> > >>>> > >>>> Yes, this would work, but I agree with Till that this is not a perfect > >>>> solution. In the event that downstream code needs to access both the > >>>> context.Context and the statefun.Context, one would be passing two > contexts > >>>> around, but it would be important not to use the statefun.Context one > for > >>>> any context values. That's workable, but it seems a bit clumsy to me. > >>>> > >>>> On Tue, Feb 22, 2022 at 12:47 PM Austin Cawley-Edwards < > >>>> austin.caw...@gmail.com> wrote: > >>>> > >>>>> Hey, > >>>>> > >>>>> Sorry for the late response – been off the ML for a few days. > >>>>> > >>>>> I am not too familiar with other Go libs that use a custom context. > >>>>> One place we could look is the `net/http` Request, which has a > >>>>> `WithContext` method[1] that seems to expose the behavior we're > looking > >>>>> for. That could be added to the statefun context package as a > standalone > >>>>> method (e.g. statefun.WithContext(sCtx Context, ctx > context.Context)), but > >>>>> would only work for the private implementation. I think the statefun > >>>>> Context type being an interface instead of a concrete type > complicates and > >>>>> restricts us a bit here. > >>>>> > >>>>> I guess I am not fully understanding why the statefun Context needs > to > >>>>> be used so far down the line. The solution that Till proposed seems > to fit > >>>>> the example you gave quite well, no? > >>>>> > >>>>> func (f *MyFunc) Invoke(ctx statefun.Context, message > >>>>> statefun.Message) error { > >>>>> logger := NewLogger() > >>>>> downCtx := context.WithValue(ctx, "logger", logger) > >>>>> return SomeOtherFunc(downCtx) > >>>>> } > >>>>> > >>>>> func SomeOtherFunc(ctx context.Context) error { > >>>>> logger := ctx.Value("logger") > >>>>> return nil > >>>>> } > >>>>> > >>>>> What does "SomeOtherFunc" need with the statefun context? I think > that > >>>>> would help me, at least, understand the role of the statefun context. > >>>>> > >>>>> I'm curious what you would think about an approach that kept > >>>>>> everything as-is, by default, but allowed for a separated context > and > >>>>>> runtime in the Invoke method, on an opt-in basis, via an adapter? > >>>>>> > >>>>> > >>>>> I am not involved in statefun really, but IMO that seems like quite a > >>>>> lot of overhead to just pass values via the context. Perhaps we > should > >>>>> consider decomposing the statefun context itself so pieces of > functionality > >>>>> can be passed around more easily? > >>>>> > >>>>> Best, > >>>>> Austin > >>>>> > >>>>> > >>>>> [1]: https://pkg.go.dev/net/http#Request.WithContext > >>>>> > >>>>> > >>>>> On Tue, Feb 22, 2022 at 10:51 AM Galen Warren < > ga...@cvillewarrens.com> > >>>>> wrote: > >>>>> > >>>>>> Thanks, Seth. > >>>>>> > >>>>>> I'm curious what you would think about an approach that kept > >>>>>> everything as-is, by default, but allowed for a separated context > and > >>>>>> runtime in the Invoke method, on an opt-in basis, via an adapter? > >>>>>> > >>>>>> On Tue, Feb 22, 2022 at 10:28 AM Seth Wiesman <sjwies...@gmail.com> > >>>>>> wrote: > >>>>>> > >>>>>>> Hi all, > >>>>>>> > >>>>>>> I believe the discussion revolved around: > >>>>>>> > >>>>>>> 1. fewer parameters > >>>>>>> 2. better aligned with other language sdks > >>>>>>> 3. we found precedent in other libraries (apologies this was long > >>>>>>> enough > >>>>>>> ago I cannot remember which ones, I'm looking through old > >>>>>>> discussions now) > >>>>>>> > >>>>>>> I would in general champion a solution that keeps the SDKs looking > >>>>>>> similar > >>>>>>> across languages. A big part of statefun's positioning in the > market > >>>>>>> is the > >>>>>>> polyglot nature and making the transition between languages as > >>>>>>> seamless as > >>>>>>> possible is very important. > >>>>>>> > >>>>>>> Seth > >>>>>>> > >>>>>>> > >>>>>>> On Tue, Feb 22, 2022 at 4:33 AM Till Rohrmann < > trohrm...@apache.org> > >>>>>>> wrote: > >>>>>>> > >>>>>>> > Hi Galen, > >>>>>>> > > >>>>>>> > Thanks for explaining the problems with the current design. I > >>>>>>> think I've > >>>>>>> > already learned quite a bit wrt Go thanks to you :-) > >>>>>>> > > >>>>>>> > From what you describe it seems indeed a bit restrictive to let > the > >>>>>>> > statefun.Context contain the context.Context w/o giving access to > >>>>>>> it. Maybe @Seth > >>>>>>> > Wiesman <sjwies...@gmail.com> can elaborate a bit more on the > >>>>>>> design > >>>>>>> > decisions to make sure that we have the full picture. > >>>>>>> > > >>>>>>> > As a cheap workaround you could create a context.Context object > by > >>>>>>> calling > >>>>>>> > > >>>>>>> > https://github.com/knative/pkg/blob/d48172451966/logging/logger.go#L45 > >>>>>>> on > >>>>>>> > the statefun.Context and then pass this Context instance to the > >>>>>>> downstream > >>>>>>> > function. But I agree that this is not the perfect solution. > >>>>>>> > > >>>>>>> > How do other libraries handle this situation if they offer a > custom > >>>>>>> > Context type? Maybe @Austin Cawley-Edwards < > >>>>>>> austin.caw...@gmail.com> you > >>>>>>> > have an opinion on the matter. > >>>>>>> > > >>>>>>> > Cheers, > >>>>>>> > Till > >>>>>>> > > >>>>>>> > On Mon, Feb 21, 2022 at 7:42 PM Galen Warren < > >>>>>>> ga...@cvillewarrens.com> > >>>>>>> > wrote: > >>>>>>> > > >>>>>>> >> So, upon further fiddling, I think it would be possible to keep > >>>>>>> full > >>>>>>> >> backward compatibility and add the option for someone to use an > >>>>>>> Invoke > >>>>>>> >> method with a separate context.Context and statefun.Runtime, via > >>>>>>> an > >>>>>>> >> adapter, if direct manipulation of the context.Context is > needed. > >>>>>>> So, > >>>>>>> >> basically, the idea would be to let the user choose the form of > >>>>>>> the Invoke > >>>>>>> >> method, with the default behavior remaining the same as now. > >>>>>>> >> > >>>>>>> >> This would require: > >>>>>>> >> > >>>>>>> >> - Recreating the Runtime interface (all methods currently > >>>>>>> defined on > >>>>>>> >> Context except not embedding context.Context) and embedding > it > >>>>>>> in the > >>>>>>> >> statefun.Context interface, so that statefun.Context remains > >>>>>>> >> effectively > >>>>>>> >> unchanged > >>>>>>> >> - Add StatefulFunctionV2 and StatefunFunctionV2Pointer to > >>>>>>> support the > >>>>>>> >> new signature with separate context and runtime > >>>>>>> >> - Add StatefulFunctionV2Adapter to wrap a StatefulFunctionV2 > >>>>>>> and expose > >>>>>>> >> it as a StatefulFunction. The statefun.Context would get > split > >>>>>>> into a > >>>>>>> >> context.Context and a statefun.Runtime here in order to call > >>>>>>> the new > >>>>>>> >> signature. > >>>>>>> >> > >>>>>>> >> Thoughts? I'd be happy to take a crack at it. > >>>>>>> >> > >>>>>>> >> > >>>>>>> >> On Mon, Feb 21, 2022 at 12:06 PM Galen Warren < > >>>>>>> ga...@cvillewarrens.com> > >>>>>>> >> wrote: > >>>>>>> >> > >>>>>>> >> > Was the reason to combine them the desire to have two > >>>>>>> parameters vs. > >>>>>>> >> > three, or was there another motivation? > >>>>>>> >> > > >>>>>>> >> > On Mon, Feb 21, 2022 at 12:02 PM Seth Wiesman < > >>>>>>> sjwies...@gmail.com> > >>>>>>> >> wrote: > >>>>>>> >> > > >>>>>>> >> >> FWIW I received a lot of early feedback explicitly asking me > >>>>>>> to couple > >>>>>>> >> the > >>>>>>> >> >> statefun specific operations with the Context (why the > runtime > >>>>>>> type > >>>>>>> >> went > >>>>>>> >> >> away). > >>>>>>> >> >> > >>>>>>> >> >> Seth > >>>>>>> >> >> > >>>>>>> >> >> On Mon, Feb 21, 2022 at 10:32 AM Galen Warren < > >>>>>>> ga...@cvillewarrens.com > >>>>>>> >> > > >>>>>>> >> >> wrote: > >>>>>>> >> >> > >>>>>>> >> >> > Thanks for looking into this! > >>>>>>> >> >> > > >>>>>>> >> >> > The issue I think we'd run into with your proposal is that, > >>>>>>> often, > >>>>>>> >> >> > libraries use non-exported types for context keys. Here is > >>>>>>> an example > >>>>>>> >> >> > < > >>>>>>> >> > >>>>>>> > https://github.com/knative/pkg/blob/d48172451966/logging/logger.go#L45 > >>>>>>> >> >> >; > >>>>>>> >> >> > in this case, the non-exported loggerKey{} is used as the > >>>>>>> key, inside > >>>>>>> >> >> the > >>>>>>> >> >> > exported WithLogger function. The key that would have to be > >>>>>>> supplied > >>>>>>> >> to > >>>>>>> >> >> the > >>>>>>> >> >> > proposed Value and WithValue functions would not be > >>>>>>> accessible in > >>>>>>> >> this > >>>>>>> >> >> > case. > >>>>>>> >> >> > > >>>>>>> >> >> > Honestly, if *everything *were on the table -- and > >>>>>>> understand it very > >>>>>>> >> >> well > >>>>>>> >> >> > might not be -- I'd suggest decoupling the Golang > >>>>>>> context.Context and > >>>>>>> >> >> the > >>>>>>> >> >> > statefun Context, i.e. have two separate parameters to > >>>>>>> >> >> > StatefulFunction.Invoke representing Golang context and > >>>>>>> statefun > >>>>>>> >> >> > operations. This is actually how things were in an earlier > >>>>>>> version of > >>>>>>> >> >> the > >>>>>>> >> >> > Golang SDK; the first parameter to Invoke was the > >>>>>>> plain-vanilla > >>>>>>> >> >> > context.Context and a separate parameter provided the > >>>>>>> statefun > >>>>>>> >> >> "runtime". > >>>>>>> >> >> > So maybe something like this: > >>>>>>> >> >> > > >>>>>>> >> >> > > > >>>>>>> >> >> > > type StatefulFunction interface { > >>>>>>> >> >> > > Invoke(ctx context.Context, runtime Runtime, message > >>>>>>> Message) error > >>>>>>> >> >> > > } > >>>>>>> >> >> > > >>>>>>> >> >> > > >>>>>>> >> >> > ... instead of the current: > >>>>>>> >> >> > > >>>>>>> >> >> > type StatefulFunction interface { > >>>>>>> >> >> > > Invoke(ctx Context, message Message) error > >>>>>>> >> >> > > } > >>>>>>> >> >> > > >>>>>>> >> >> > > >>>>>>> >> >> > ... where Runtime would be everything currently in > >>>>>>> statefun.Context, > >>>>>>> >> >> except > >>>>>>> >> >> > the context.Context part. This would allow context.Context > >>>>>>> to be > >>>>>>> >> >> > manipulated and passed around normally. > >>>>>>> >> >> > > >>>>>>> >> >> > I think this could potentially be done in a > >>>>>>> backward-compatible way, > >>>>>>> >> >> with a > >>>>>>> >> >> > new set of types and methods, e.g. StatefulFunctionV2, > >>>>>>> >> >> > StatefufFunctionSpecV2, StatefulFunctions.WithSpecV2, etc. > >>>>>>> Or it > >>>>>>> >> could > >>>>>>> >> >> be > >>>>>>> >> >> > done in an almost backward-compatible way, by changing the > >>>>>>> existing > >>>>>>> >> >> > StatefulFunction, StatefulFunctionSpec, > >>>>>>> StatefulFunctions.WithSpec > >>>>>>> >> and > >>>>>>> >> >> > providing an adapter for people who want to continue to use > >>>>>>> the > >>>>>>> >> >> > two-parameter version of Invoke. > >>>>>>> >> >> > > >>>>>>> >> >> > If those kinds of changes are a non-starter, then IMO the > >>>>>>> next best > >>>>>>> >> >> option > >>>>>>> >> >> > would be adding something like: > >>>>>>> >> >> > > >>>>>>> >> >> > PrepareContext func(ctx statefun.Context) context.Context > >>>>>>> >> >> > > >>>>>>> >> >> > > >>>>>>> >> >> > ... to StatefulFunctionSpec to allow a one-time > >>>>>>> customization of the > >>>>>>> >> >> > underlying context at the beginning of a stateful function > >>>>>>> >> invocation. > >>>>>>> >> >> That > >>>>>>> >> >> > would cover a lot of use cases. > >>>>>>> >> >> > > >>>>>>> >> >> > > >>>>>>> >> >> > On Mon, Feb 21, 2022 at 3:06 AM Till Rohrmann < > >>>>>>> trohrm...@apache.org> > >>>>>>> >> >> > wrote: > >>>>>>> >> >> > > >>>>>>> >> >> > > Thanks a lot for clarifying the problem. I think I now > >>>>>>> understand > >>>>>>> >> the > >>>>>>> >> >> > > problem. As you've probably figured out, I have no clue > >>>>>>> about Go > >>>>>>> >> and > >>>>>>> >> >> > > its usage of the Context type. > >>>>>>> >> >> > > > >>>>>>> >> >> > > After looking into it a bit I was wondering whether we > >>>>>>> can't > >>>>>>> >> follow a > >>>>>>> >> >> > > similar route as it is done for the Context type. By > adding > >>>>>>> >> something > >>>>>>> >> >> > like > >>>>>>> >> >> > > > >>>>>>> >> >> > > type valueCtx struct { > >>>>>>> >> >> > > Context > >>>>>>> >> >> > > key, val interface{} > >>>>>>> >> >> > > } > >>>>>>> >> >> > > > >>>>>>> >> >> > > func (c *valueCtx) Value(key interface{}) interface{} { > >>>>>>> >> >> > > if c.key == key { > >>>>>>> >> >> > > return c.val > >>>>>>> >> >> > > } > >>>>>>> >> >> > > return c.Context.Value(key) > >>>>>>> >> >> > > } > >>>>>>> >> >> > > > >>>>>>> >> >> > > func WithValue(parent Context, key, val interface{}) > >>>>>>> Context { > >>>>>>> >> >> > > if parent == nil { > >>>>>>> >> >> > > panic("cannot create context from nil parent") > >>>>>>> >> >> > > } > >>>>>>> >> >> > > if key == nil { > >>>>>>> >> >> > > panic("nil key") > >>>>>>> >> >> > > } > >>>>>>> >> >> > > return &valueCtx{parent, key, val} > >>>>>>> >> >> > > } > >>>>>>> >> >> > > > >>>>>>> >> >> > > to the statefun/context.go we would allow to extend a > >>>>>>> Statefun > >>>>>>> >> context > >>>>>>> >> >> > with > >>>>>>> >> >> > > values w/o changing the underlying instance. If > >>>>>>> statefun.Context is > >>>>>>> >> >> not > >>>>>>> >> >> > > needed, then there is already the option to unwrap the > >>>>>>> >> context.Context > >>>>>>> >> >> > and > >>>>>>> >> >> > > to extend it with values and then pass on this instance. > >>>>>>> But maybe > >>>>>>> >> >> this > >>>>>>> >> >> > is > >>>>>>> >> >> > > no idiomatic Go. Let me know what you think. > >>>>>>> >> >> > > > >>>>>>> >> >> > > Cheers, > >>>>>>> >> >> > > Till > >>>>>>> >> >> > > > >>>>>>> >> >> > > On Fri, Feb 18, 2022 at 7:01 PM Galen Warren < > >>>>>>> >> ga...@cvillewarrens.com > >>>>>>> >> >> > > >>>>>>> >> >> > > wrote: > >>>>>>> >> >> > > > >>>>>>> >> >> > > > Hmm ... a downside to my proposal is that Go contexts > are > >>>>>>> >> supposed > >>>>>>> >> >> to > >>>>>>> >> >> > be > >>>>>>> >> >> > > > immutable, i.e. when adding a custom value to a > context, > >>>>>>> a new > >>>>>>> >> >> context > >>>>>>> >> >> > is > >>>>>>> >> >> > > > created with the new value and the old context isn't > >>>>>>> changed. > >>>>>>> >> >> Changing > >>>>>>> >> >> > > the > >>>>>>> >> >> > > > context.Context associated with the statefun.Context > >>>>>>> sort of goes > >>>>>>> >> >> > against > >>>>>>> >> >> > > > the spirit of that, i.e. a consumer of statefun.Context > >>>>>>> could see > >>>>>>> >> >> > custom > >>>>>>> >> >> > > > values change unexpectedly if another consumer of the > >>>>>>> same > >>>>>>> >> >> > > statefun.Context > >>>>>>> >> >> > > > modified the underlying context.Context. > >>>>>>> >> >> > > > > >>>>>>> >> >> > > > To avoid that, I think we'd be back to having some > >>>>>>> mechanism to > >>>>>>> >> >> > customize > >>>>>>> >> >> > > > the underlying context.Context once, when the > >>>>>>> statefun.Context is > >>>>>>> >> >> > created > >>>>>>> >> >> > > > at the beginning of a stateful function invocation. > >>>>>>> Adding a > >>>>>>> >> field > >>>>>>> >> >> > like: > >>>>>>> >> >> > > > > >>>>>>> >> >> > > > PrepareContext func(ctx statefun.Context) > context.Context > >>>>>>> >> >> > > > > >>>>>>> >> >> > > > ... to the StatefulFunctionSpec struct could accomplish > >>>>>>> that, > >>>>>>> >> i.e. > >>>>>>> >> >> if > >>>>>>> >> >> > > > PrepareContext were supplied, the context could be > >>>>>>> customized > >>>>>>> >> once > >>>>>>> >> >> at > >>>>>>> >> >> > the > >>>>>>> >> >> > > > start of a function invocation and then left immutable > >>>>>>> after that > >>>>>>> >> >> > point. > >>>>>>> >> >> > > > > >>>>>>> >> >> > > > (Using statefun.Context as the input is deliberate > here, > >>>>>>> in > >>>>>>> >> order to > >>>>>>> >> >> > > allow > >>>>>>> >> >> > > > the context.Context to be populated using values from > the > >>>>>>> >> >> > > statefun.Context, > >>>>>>> >> >> > > > for example the function id). > >>>>>>> >> >> > > > > >>>>>>> >> >> > > > > >>>>>>> >> >> > > > > >>>>>>> >> >> > > > > >>>>>>> >> >> > > > > >>>>>>> >> >> > > > > >>>>>>> >> >> > > > On Fri, Feb 18, 2022 at 11:34 AM Galen Warren < > >>>>>>> >> >> ga...@cvillewarrens.com > >>>>>>> >> >> > > > >>>>>>> >> >> > > > wrote: > >>>>>>> >> >> > > > > >>>>>>> >> >> > > > > An example of passing it around would be: > >>>>>>> >> >> > > > > > >>>>>>> >> >> > > > > func (f *MyFunc) Invoke(ctx statefun.Context, message > >>>>>>> >> >> > statefun.Message) > >>>>>>> >> >> > > > > error { > >>>>>>> >> >> > > > > > >>>>>>> >> >> > > > > logger := NewLogger() > >>>>>>> >> >> > > > > ctx.SetContext(ctxzap.ToContext(ctx, logger)) > >>>>>>> >> >> > > > > > >>>>>>> >> >> > > > > return SomeOtherFunc(ctx) > >>>>>>> >> >> > > > > } > >>>>>>> >> >> > > > > > >>>>>>> >> >> > > > > func SomeOtherFunc(ctx context.Context) error { > >>>>>>> >> >> > > > > > >>>>>>> >> >> > > > > logger := ctxzap.Extract(ctx) > >>>>>>> >> >> > > > > logger.Info(...) > >>>>>>> >> >> > > > > > >>>>>>> >> >> > > > > return nil > >>>>>>> >> >> > > > > } > >>>>>>> >> >> > > > > > >>>>>>> >> >> > > > > This would also work with further nested calls, so > >>>>>>> long as the > >>>>>>> >> >> > context > >>>>>>> >> >> > > is > >>>>>>> >> >> > > > > passed to them. > >>>>>>> >> >> > > > > > >>>>>>> >> >> > > > > On Fri, Feb 18, 2022 at 11:23 AM Galen Warren < > >>>>>>> >> >> > ga...@cvillewarrens.com > >>>>>>> >> >> > > > > >>>>>>> >> >> > > > > wrote: > >>>>>>> >> >> > > > > > >>>>>>> >> >> > > > >> Ha, our emails keep passing. > >>>>>>> >> >> > > > >> > >>>>>>> >> >> > > > >> I've been playing around with options locally, and > the > >>>>>>> >> SetContext > >>>>>>> >> >> > > option > >>>>>>> >> >> > > > >> seems to be the most flexible (and non-breaking), > imo. > >>>>>>> >> >> > > > >> > >>>>>>> >> >> > > > >> The implementation would be trivial, just add: > >>>>>>> >> >> > > > >> > >>>>>>> >> >> > > > >> SetContext(ctx context.Context) > >>>>>>> >> >> > > > >> > >>>>>>> >> >> > > > >> ... to the statefun.Context interface, which is > >>>>>>> implemented > >>>>>>> >> as: > >>>>>>> >> >> > > > >> > >>>>>>> >> >> > > > >> func (s *statefunContext) SetContext(ctx > >>>>>>> context.Context) { > >>>>>>> >> >> > > > >> s.Context = ctx > >>>>>>> >> >> > > > >> } > >>>>>>> >> >> > > > >> > >>>>>>> >> >> > > > >> > >>>>>>> >> >> > > > >> > >>>>>>> >> >> > > > >> > >>>>>>> >> >> > > > >> On Fri, Feb 18, 2022 at 11:18 AM Austin > >>>>>>> Cawley-Edwards < > >>>>>>> >> >> > > > >> austin.caw...@gmail.com> wrote: > >>>>>>> >> >> > > > >> > >>>>>>> >> >> > > > >>> It would be helpful to have a small example though, > >>>>>>> if you > >>>>>>> >> have > >>>>>>> >> >> on > >>>>>>> >> >> > > > Galen, > >>>>>>> >> >> > > > >>> to see how you're passing it around. > >>>>>>> >> >> > > > >>> > >>>>>>> >> >> > > > >>> On Fri, Feb 18, 2022 at 11:10 AM Austin > >>>>>>> Cawley-Edwards < > >>>>>>> >> >> > > > >>> austin.caw...@gmail.com> wrote: > >>>>>>> >> >> > > > >>> > >>>>>>> >> >> > > > >>> > Looking through the statefun Context interface, > it > >>>>>>> indeed > >>>>>>> >> >> doesn't > >>>>>>> >> >> > > > give > >>>>>>> >> >> > > > >>> > access to the underlying context.Context and the > >>>>>>> only > >>>>>>> >> >> > > implementation > >>>>>>> >> >> > > > is > >>>>>>> >> >> > > > >>> > package-private [1]. I don't think there would be > >>>>>>> a way to > >>>>>>> >> >> update > >>>>>>> >> >> > > the > >>>>>>> >> >> > > > >>> > statfun.Context interface without introducing > >>>>>>> breaking > >>>>>>> >> >> changes, > >>>>>>> >> >> > but > >>>>>>> >> >> > > > if > >>>>>>> >> >> > > > >>> we > >>>>>>> >> >> > > > >>> > were to make that implementation public, that > >>>>>>> might be a > >>>>>>> >> >> stopgap > >>>>>>> >> >> > > > >>> solution. > >>>>>>> >> >> > > > >>> > e.g., > >>>>>>> >> >> > > > >>> > > >>>>>>> >> >> > > > >>> > ``` > >>>>>>> >> >> > > > >>> > type StatefunContext struct { > >>>>>>> >> >> > > > >>> > // expose embedded context > >>>>>>> >> >> > > > >>> > context.Context > >>>>>>> >> >> > > > >>> > > >>>>>>> >> >> > > > >>> > // make the mutext private > >>>>>>> >> >> > > > >>> > mu sync.Mutex > >>>>>>> >> >> > > > >>> > > >>>>>>> >> >> > > > >>> > // keep internals private > >>>>>>> >> >> > > > >>> > self Address > >>>>>>> >> >> > > > >>> > caller *Address > >>>>>>> >> >> > > > >>> > storage *storage > >>>>>>> >> >> > > > >>> > response > *protocol.FromFunction_InvocationResponse > >>>>>>> >> >> > > > >>> > } > >>>>>>> >> >> > > > >>> > ``` > >>>>>>> >> >> > > > >>> > > >>>>>>> >> >> > > > >>> > You could then do a type assertion in the > handlers > >>>>>>> for this > >>>>>>> >> >> type > >>>>>>> >> >> > of > >>>>>>> >> >> > > > >>> > context, and modify the context on it directly. > It > >>>>>>> would > >>>>>>> >> be a > >>>>>>> >> >> bit > >>>>>>> >> >> > > > >>> ugly, but > >>>>>>> >> >> > > > >>> > may work. > >>>>>>> >> >> > > > >>> > > >>>>>>> >> >> > > > >>> > ``` > >>>>>>> >> >> > > > >>> > func (s aFunc) Invoke(ctx Context, message > >>>>>>> Message) error { > >>>>>>> >> >> > > > >>> > if sCtx, ok := ctx.(*statefun.StatefunContext); > >>>>>>> ok { > >>>>>>> >> >> > > > >>> > sCtx.Context = > context.WithValue(sCtx.Context, > >>>>>>> >> "logger", > >>>>>>> >> >> > > aLogger) > >>>>>>> >> >> > > > >>> > } > >>>>>>> >> >> > > > >>> > // ... > >>>>>>> >> >> > > > >>> > } > >>>>>>> >> >> > > > >>> > ``` > >>>>>>> >> >> > > > >>> > > >>>>>>> >> >> > > > >>> > Let me know what you all think, > >>>>>>> >> >> > > > >>> > Austin > >>>>>>> >> >> > > > >>> > > >>>>>>> >> >> > > > >>> > > >>>>>>> >> >> > > > >>> > [1]: > >>>>>>> >> >> > > > >>> > > >>>>>>> >> >> > > > >>> > >>>>>>> >> >> > > > > >>>>>>> >> >> > > > >>>>>>> >> >> > > >>>>>>> >> >> > >>>>>>> >> > >>>>>>> > https://github.com/apache/flink-statefun/blob/1dfe226d85fea05a46c8ffa688175b4c0f2d4900/statefun-sdk-go/v3/pkg/statefun/context.go#L66-L73 > >>>>>>> >> >> > > > >>> > > >>>>>>> >> >> > > > >>> > > >>>>>>> >> >> > > > >>> > On Fri, Feb 18, 2022 at 11:03 AM Galen Warren < > >>>>>>> >> >> > > > ga...@cvillewarrens.com > >>>>>>> >> >> > > > >>> > > >>>>>>> >> >> > > > >>> > wrote: > >>>>>>> >> >> > > > >>> > > >>>>>>> >> >> > > > >>> >> Sorry Austin, I didn't see your response before > I > >>>>>>> replied. > >>>>>>> >> >> Yes, > >>>>>>> >> >> > > > we're > >>>>>>> >> >> > > > >>> >> saying the same thing. > >>>>>>> >> >> > > > >>> >> > >>>>>>> >> >> > > > >>> >> On Fri, Feb 18, 2022 at 10:56 AM Austin > >>>>>>> Cawley-Edwards < > >>>>>>> >> >> > > > >>> >> austin.caw...@gmail.com> wrote: > >>>>>>> >> >> > > > >>> >> > >>>>>>> >> >> > > > >>> >> > Hey all, jumping in. This makes sense to me – > >>>>>>> for > >>>>>>> >> instance > >>>>>>> >> >> to > >>>>>>> >> >> > > > >>> attach a > >>>>>>> >> >> > > > >>> >> > logger with some common metadata, e.g trace ID > >>>>>>> for the > >>>>>>> >> >> > request? > >>>>>>> >> >> > > > >>> This is > >>>>>>> >> >> > > > >>> >> > common in go to add arbitrary items without > >>>>>>> updating the > >>>>>>> >> >> > method > >>>>>>> >> >> > > > >>> >> signatures, > >>>>>>> >> >> > > > >>> >> > similar to thread local storage in Java. > >>>>>>> >> >> > > > >>> >> > > >>>>>>> >> >> > > > >>> >> > On Fri, Feb 18, 2022 at 10:53 AM Till > Rohrmann < > >>>>>>> >> >> > > > >>> trohrm...@apache.org> > >>>>>>> >> >> > > > >>> >> > wrote: > >>>>>>> >> >> > > > >>> >> > > >>>>>>> >> >> > > > >>> >> > > Thanks for the clarification Galen. If you > >>>>>>> call the > >>>>>>> >> >> other Go > >>>>>>> >> >> > > > >>> >> functions, > >>>>>>> >> >> > > > >>> >> > > then you could also pass the other values as > >>>>>>> separate > >>>>>>> >> >> > > arguments > >>>>>>> >> >> > > > to > >>>>>>> >> >> > > > >>> >> these > >>>>>>> >> >> > > > >>> >> > > functions, can't you? > >>>>>>> >> >> > > > >>> >> > > > >>>>>>> >> >> > > > >>> >> > > Cheers, > >>>>>>> >> >> > > > >>> >> > > Till > >>>>>>> >> >> > > > >>> >> > > > >>>>>>> >> >> > > > >>> >> > > On Fri, Feb 18, 2022 at 3:31 PM Galen > Warren < > >>>>>>> >> >> > > > >>> ga...@cvillewarrens.com > >>>>>>> >> >> > > > >>> >> > > >>>>>>> >> >> > > > >>> >> > > wrote: > >>>>>>> >> >> > > > >>> >> > > > >>>>>>> >> >> > > > >>> >> > > > The former. > >>>>>>> >> >> > > > >>> >> > > > > >>>>>>> >> >> > > > >>> >> > > > I think there's potential for confusion > >>>>>>> here because > >>>>>>> >> >> we're > >>>>>>> >> >> > > > >>> using the > >>>>>>> >> >> > > > >>> >> > > > word *function > >>>>>>> >> >> > > > >>> >> > > > *in a couple of senses. One sense is a > >>>>>>> *stateful > >>>>>>> >> >> > function*; > >>>>>>> >> >> > > > >>> another > >>>>>>> >> >> > > > >>> >> > sense > >>>>>>> >> >> > > > >>> >> > > > is a *Go function*. > >>>>>>> >> >> > > > >>> >> > > > > >>>>>>> >> >> > > > >>> >> > > > What I'm looking to do is to put values in > >>>>>>> the > >>>>>>> >> Context > >>>>>>> >> >> so > >>>>>>> >> >> > > that > >>>>>>> >> >> > > > >>> >> > downstream > >>>>>>> >> >> > > > >>> >> > > > Go functions that receive the context can > >>>>>>> access > >>>>>>> >> those > >>>>>>> >> >> > > values. > >>>>>>> >> >> > > > >>> Those > >>>>>>> >> >> > > > >>> >> > > > downstream Go functions would be called > >>>>>>> during one > >>>>>>> >> >> > > invocation > >>>>>>> >> >> > > > >>> of the > >>>>>>> >> >> > > > >>> >> > > > stateful function. > >>>>>>> >> >> > > > >>> >> > > > > >>>>>>> >> >> > > > >>> >> > > > On Fri, Feb 18, 2022 at 6:48 AM Till > >>>>>>> Rohrmann < > >>>>>>> >> >> > > > >>> trohrm...@apache.org > >>>>>>> >> >> > > > >>> >> > > >>>>>>> >> >> > > > >>> >> > > > wrote: > >>>>>>> >> >> > > > >>> >> > > > > >>>>>>> >> >> > > > >>> >> > > > > Hi Galen, > >>>>>>> >> >> > > > >>> >> > > > > > >>>>>>> >> >> > > > >>> >> > > > > Am I understanding it correctly, that > you > >>>>>>> would > >>>>>>> >> like > >>>>>>> >> >> to > >>>>>>> >> >> > > set > >>>>>>> >> >> > > > >>> some > >>>>>>> >> >> > > > >>> >> > values > >>>>>>> >> >> > > > >>> >> > > > in > >>>>>>> >> >> > > > >>> >> > > > > the Context of function A that is then > >>>>>>> accessible > >>>>>>> >> in > >>>>>>> >> >> a > >>>>>>> >> >> > > > >>> downstream > >>>>>>> >> >> > > > >>> >> > call > >>>>>>> >> >> > > > >>> >> > > of > >>>>>>> >> >> > > > >>> >> > > > > function B? Or would you like to set a > >>>>>>> value that > >>>>>>> >> is > >>>>>>> >> >> > > > >>> accessible > >>>>>>> >> >> > > > >>> >> once > >>>>>>> >> >> > > > >>> >> > > > > function A is called again (w/ or w/o > the > >>>>>>> same > >>>>>>> >> id)? > >>>>>>> >> >> > > > >>> >> > > > > > >>>>>>> >> >> > > > >>> >> > > > > Cheers, > >>>>>>> >> >> > > > >>> >> > > > > Till > >>>>>>> >> >> > > > >>> >> > > > > > >>>>>>> >> >> > > > >>> >> > > > > On Thu, Feb 17, 2022 at 10:59 PM Galen > >>>>>>> Warren < > >>>>>>> >> >> > > > >>> >> > ga...@cvillewarrens.com > >>>>>>> >> >> > > > >>> >> > > > > >>>>>>> >> >> > > > >>> >> > > > > wrote: > >>>>>>> >> >> > > > >>> >> > > > > > >>>>>>> >> >> > > > >>> >> > > > > > Also, a potentially simpler way to > >>>>>>> support this > >>>>>>> >> >> would > >>>>>>> >> >> > be > >>>>>>> >> >> > > > to > >>>>>>> >> >> > > > >>> add > >>>>>>> >> >> > > > >>> >> a > >>>>>>> >> >> > > > >>> >> > > > > > SetContext method to the > >>>>>>> statefun.Context > >>>>>>> >> >> interface, > >>>>>>> >> >> > and > >>>>>>> >> >> > > > >>> have it > >>>>>>> >> >> > > > >>> >> > > assign > >>>>>>> >> >> > > > >>> >> > > > > the > >>>>>>> >> >> > > > >>> >> > > > > > wrapped context. This would not > require > >>>>>>> changes > >>>>>>> >> to > >>>>>>> >> >> the > >>>>>>> >> >> > > > >>> function > >>>>>>> >> >> > > > >>> >> > spec, > >>>>>>> >> >> > > > >>> >> > > > or > >>>>>>> >> >> > > > >>> >> > > > > > anything else, and would be more > >>>>>>> flexible. > >>>>>>> >> >> > > > >>> >> > > > > > > >>>>>>> >> >> > > > >>> >> > > > > > On Thu, Feb 17, 2022 at 1:05 PM Galen > >>>>>>> Warren < > >>>>>>> >> >> > > > >>> >> > > ga...@cvillewarrens.com> > >>>>>>> >> >> > > > >>> >> > > > > > wrote: > >>>>>>> >> >> > > > >>> >> > > > > > > >>>>>>> >> >> > > > >>> >> > > > > > > Thanks for the quick reply! > >>>>>>> >> >> > > > >>> >> > > > > > > > >>>>>>> >> >> > > > >>> >> > > > > > > What I'm trying to do is put some > >>>>>>> things into > >>>>>>> >> the > >>>>>>> >> >> > > > context > >>>>>>> >> >> > > > >>> so > >>>>>>> >> >> > > > >>> >> that > >>>>>>> >> >> > > > >>> >> > > > > they're > >>>>>>> >> >> > > > >>> >> > > > > > > available in downstream calls, > >>>>>>> perhaps in > >>>>>>> >> methods > >>>>>>> >> >> > with > >>>>>>> >> >> > > > >>> pointer > >>>>>>> >> >> > > > >>> >> > > > > receivers > >>>>>>> >> >> > > > >>> >> > > > > > to > >>>>>>> >> >> > > > >>> >> > > > > > > the function struct (MyFunc) but > also > >>>>>>> perhaps > >>>>>>> >> in > >>>>>>> >> >> > > methods > >>>>>>> >> >> > > > >>> that > >>>>>>> >> >> > > > >>> >> are > >>>>>>> >> >> > > > >>> >> > > > > further > >>>>>>> >> >> > > > >>> >> > > > > > > downstream that don't have access to > >>>>>>> MyFunc. > >>>>>>> >> If > >>>>>>> >> >> I'm > >>>>>>> >> >> > > > >>> >> understanding > >>>>>>> >> >> > > > >>> >> > > > > > > correctly, your proposal would work > >>>>>>> for the > >>>>>>> >> >> former > >>>>>>> >> >> > but > >>>>>>> >> >> > > > >>> not the > >>>>>>> >> >> > > > >>> >> > > > latter. > >>>>>>> >> >> > > > >>> >> > > > > > > > >>>>>>> >> >> > > > >>> >> > > > > > > An example would be to put a > >>>>>>> configured Logger > >>>>>>> >> >> into > >>>>>>> >> >> > > the > >>>>>>> >> >> > > > >>> >> context > >>>>>>> >> >> > > > >>> >> > > via a > >>>>>>> >> >> > > > >>> >> > > > > > > WithLogger method (logging package - > >>>>>>> >> >> > > > >>> knative.dev/pkg/logging > >>>>>>> >> >> > > > >>> >> - > >>>>>>> >> >> > > > >>> >> > > > > > pkg.go.dev > >>>>>>> >> >> > > > >>> >> > > > > > > < > >>>>>>> >> >> > > https://pkg.go.dev/knative.dev/pkg/logging#WithLogger > >>>>>>> >> >> > > > >) > >>>>>>> >> >> > > > >>> and > >>>>>>> >> >> > > > >>> >> > then > >>>>>>> >> >> > > > >>> >> > > > pull > >>>>>>> >> >> > > > >>> >> > > > > > it > >>>>>>> >> >> > > > >>> >> > > > > > > out downstream via FromContext > >>>>>>> (logging > >>>>>>> >> package - > >>>>>>> >> >> > > > >>> >> > > > > > knative.dev/pkg/logging > >>>>>>> >> >> > > > >>> >> > > > > > > - pkg.go.dev < > >>>>>>> >> >> > > > >>> >> > > > >>>>>>> >> https://pkg.go.dev/knative.dev/pkg/logging#FromContext > >>>>>>> >> >> > > > >>> >> > > > > >). > >>>>>>> >> >> > > > >>> >> > > > > > > > >>>>>>> >> >> > > > >>> >> > > > > > > > >>>>>>> >> >> > > > >>> >> > > > > > > > >>>>>>> >> >> > > > >>> >> > > > > > > > >>>>>>> >> >> > > > >>> >> > > > > > > On Wed, Feb 16, 2022 at 5:50 PM Seth > >>>>>>> Wiesman < > >>>>>>> >> >> > > > >>> >> > sjwies...@gmail.com> > >>>>>>> >> >> > > > >>> >> > > > > > wrote: > >>>>>>> >> >> > > > >>> >> > > > > > > > >>>>>>> >> >> > > > >>> >> > > > > > >> Hi Galen, > >>>>>>> >> >> > > > >>> >> > > > > > >> > >>>>>>> >> >> > > > >>> >> > > > > > >> No, that is not currently > supported, > >>>>>>> the > >>>>>>> >> current > >>>>>>> >> >> > > > >>> idiomatic > >>>>>>> >> >> > > > >>> >> way > >>>>>>> >> >> > > > >>> >> > > would > >>>>>>> >> >> > > > >>> >> > > > > be > >>>>>>> >> >> > > > >>> >> > > > > > to > >>>>>>> >> >> > > > >>> >> > > > > > >> pass those values to the struct > >>>>>>> implementing > >>>>>>> >> the > >>>>>>> >> >> > > > Statefun > >>>>>>> >> >> > > > >>> >> > > interface. > >>>>>>> >> >> > > > >>> >> > > > > > >> > >>>>>>> >> >> > > > >>> >> > > > > > >> > >>>>>>> >> >> > > > >>> >> > > > > > >> type MyFunc struct { > someRuntimeInfo > >>>>>>> string } > >>>>>>> >> >> func > >>>>>>> >> >> > (m > >>>>>>> >> >> > > > >>> >> *MyFunc) > >>>>>>> >> >> > > > >>> >> > > > > > Invoke(ctx > >>>>>>> >> >> > > > >>> >> > > > > > >> statefun.Context, message > >>>>>>> statefun.Message) > >>>>>>> >> >> error > >>>>>>> >> >> > { } > >>>>>>> >> >> > > > >>> func > >>>>>>> >> >> > > > >>> >> > main() > >>>>>>> >> >> > > > >>> >> > > { > >>>>>>> >> >> > > > >>> >> > > > > > >> builder > >>>>>>> >> >> > > > >>> >> > > > > > >> := > >>>>>>> statefun.StatefulFunctionsBuilder() > >>>>>>> >> >> > > > >>> >> > > > > > >> f := MyFunc { someRuntimeInfo: > >>>>>>> >> >> "runtime-provided" } > >>>>>>> >> >> > > > >>> >> > > builder.WithSpec > >>>>>>> >> >> > > > >>> >> > > > > > >> (statefun.StatefulFunctionSpec{ > >>>>>>> FunctionType: > >>>>>>> >> >> > > > >>> >> > > statefun.TypeNameFrom( > >>>>>>> >> >> > > > >>> >> > > > > > >> "example/my-func"), Function: f }) > >>>>>>> >> >> > > > >>> >> > > > > > >> http.Handle("/statefun", > >>>>>>> builder.AsHandler()) > >>>>>>> >> >> > > > >>> >> > > > > > >> _ = http.ListenAndServe(":8000", > >>>>>>> nil) } > >>>>>>> >> >> > > > >>> >> > > > > > >> > >>>>>>> >> >> > > > >>> >> > > > > > >> Would this work for you? Or what is > >>>>>>> the > >>>>>>> >> context > >>>>>>> >> >> > (pun > >>>>>>> >> >> > > > >>> >> intended) > >>>>>>> >> >> > > > >>> >> > you > >>>>>>> >> >> > > > >>> >> > > > are > >>>>>>> >> >> > > > >>> >> > > > > > >> looking for? > >>>>>>> >> >> > > > >>> >> > > > > > >> > >>>>>>> >> >> > > > >>> >> > > > > > >> Seth > >>>>>>> >> >> > > > >>> >> > > > > > >> > >>>>>>> >> >> > > > >>> >> > > > > > >> On Wed, Feb 16, 2022 at 4:35 PM > >>>>>>> Galen Warren > >>>>>>> >> < > >>>>>>> >> >> > > > >>> >> > > > ga...@cvillewarrens.com > >>>>>>> >> >> > > > >>> >> > > > > > > >>>>>>> >> >> > > > >>> >> > > > > > >> wrote: > >>>>>>> >> >> > > > >>> >> > > > > > >> > >>>>>>> >> >> > > > >>> >> > > > > > >> > When stateful functions are > >>>>>>> invoked, they > >>>>>>> >> are > >>>>>>> >> >> > > passed > >>>>>>> >> >> > > > an > >>>>>>> >> >> > > > >>> >> > instance > >>>>>>> >> >> > > > >>> >> > > > of > >>>>>>> >> >> > > > >>> >> > > > > > >> > statefun.Context, which wraps the > >>>>>>> >> >> context.Context > >>>>>>> >> >> > > > >>> received > >>>>>>> >> >> > > > >>> >> by > >>>>>>> >> >> > > > >>> >> > > the > >>>>>>> >> >> > > > >>> >> > > > > HTTP > >>>>>>> >> >> > > > >>> >> > > > > > >> > request. Is there any way to > >>>>>>> customize that > >>>>>>> >> >> > > > >>> context.Context > >>>>>>> >> >> > > > >>> >> > to, > >>>>>>> >> >> > > > >>> >> > > > say, > >>>>>>> >> >> > > > >>> >> > > > > > >> hold > >>>>>>> >> >> > > > >>> >> > > > > > >> > custom values, using > >>>>>>> ctx.WithValue()? I > >>>>>>> >> don't > >>>>>>> >> >> > see a > >>>>>>> >> >> > > > way > >>>>>>> >> >> > > > >>> >> but I > >>>>>>> >> >> > > > >>> >> > > > wanted > >>>>>>> >> >> > > > >>> >> > > > > > to > >>>>>>> >> >> > > > >>> >> > > > > > >> > ask. > >>>>>>> >> >> > > > >>> >> > > > > > >> > > >>>>>>> >> >> > > > >>> >> > > > > > >> > If not, would you be interested > in > >>>>>>> a PR to > >>>>>>> >> add > >>>>>>> >> >> > this > >>>>>>> >> >> > > > >>> >> > > > functionality? A > >>>>>>> >> >> > > > >>> >> > > > > > >> simple > >>>>>>> >> >> > > > >>> >> > > > > > >> > way might be to add a property to > >>>>>>> >> >> > > > StatefulFunctionSpec, > >>>>>>> >> >> > > > >>> >> say: > >>>>>>> >> >> > > > >>> >> > > > > > >> > > >>>>>>> >> >> > > > >>> >> > > > > > >> > TransformContext func(ctx > >>>>>>> context.Context) > >>>>>>> >> >> > > > >>> context.Context > >>>>>>> >> >> > > > >>> >> > > > > > >> > > >>>>>>> >> >> > > > >>> >> > > > > > >> > ... that, if supplied, would be > >>>>>>> called to > >>>>>>> >> >> create > >>>>>>> >> >> > a > >>>>>>> >> >> > > > >>> >> customized > >>>>>>> >> >> > > > >>> >> > > > > context > >>>>>>> >> >> > > > >>> >> > > > > > >> that > >>>>>>> >> >> > > > >>> >> > > > > > >> > would be used downstream? > >>>>>>> >> >> > > > >>> >> > > > > > >> > > >>>>>>> >> >> > > > >>> >> > > > > > >> > Thanks. > >>>>>>> >> >> > > > >>> >> > > > > > >> > > >>>>>>> >> >> > > > >>> >> > > > > > >> > >>>>>>> >> >> > > > >>> >> > > > > > > > >>>>>>> >> >> > > > >>> >> > > > > > > >>>>>>> >> >> > > > >>> >> > > > > > >>>>>>> >> >> > > > >>> >> > > > > >>>>>>> >> >> > > > >>> >> > > > >>>>>>> >> >> > > > >>> >> > > >>>>>>> >> >> > > > >>> >> > >>>>>>> >> >> > > > >>> > > >>>>>>> >> >> > > > >>> > >>>>>>> >> >> > > > >> > >>>>>>> >> >> > > > > >>>>>>> >> >> > > > >>>>>>> >> >> > > >>>>>>> >> >> > >>>>>>> >> > > >>>>>>> >> > >>>>>>> > > >>>>>>> > >>>>>> >