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. >>>>>> >> >> > > > >>> >> > > > > > >> > >>>>>> >> >> > > > >>> >> > > > > > >> >>>>>> >> >> > > > >>> >> > > > > > > >>>>>> >> >> > > > >>> >> > > > > > >>>>>> >> >> > > > >>> >> > > > > >>>>>> >> >> > > > >>> >> > > > >>>>>> >> >> > > > >>> >> > > >>>>>> >> >> > > > >>> >> > >>>>>> >> >> > > > >>> >> >>>>>> >> >> > > > >>> > >>>>>> >> >> > > > >>> >>>>>> >> >> > > > >> >>>>>> >> >> > > > >>>>>> >> >> > > >>>>>> >> >> > >>>>>> >> >> >>>>>> >> > >>>>>> >> >>>>>> > >>>>>> >>>>>