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

Reply via email to