cloudstate icon indicating copy to clipboard operation
cloudstate copied to clipboard

Create Java StatelessFunction example

Open sleipnir opened this issue 4 years ago • 17 comments

It would be interesting to have an example of a stateless function available

sleipnir avatar Apr 24 '20 01:04 sleipnir

@sleipnir good point but first of all the stateless function support should be implemented. I came accross this part of the code today. I would be cool to write an issue for stateless function support. I can to that if you don't mind.

ralphlaude avatar May 07 '20 21:05 ralphlaude

Thanks @ralphlaude Yes, I know that the support does not exist so I opened this issue so that it is already created with a usable example :D I'm talking to @viktorklang about the best semantics we should expose in the API (which annotation names for example would be better, since @CommandHandler seems to apply better in the case of EventSourced. I thought of something like @ActionHandler that seems to be more aligned other FaaS technologies). Feel free to implement, there is already a draft in the java-support (StatelessFunctionImpl.scala) but I think it would be good to discuss these semantics because I am implementing it in Dart and I think the semantics should be similar (at least the names of things)

sleipnir avatar May 07 '20 22:05 sleipnir

@sleipnir Having thought about it for a little while, how about starting with CommandHandler, and then we can rename it when/if we find that something else is more appropriate?

viktorklang avatar May 07 '20 22:05 viktorklang

@viktorklang you live playing tricks on me :P Ok. No problema. Let's do it @ralphlaude then @CommandHandler

sleipnir avatar May 07 '20 23:05 sleipnir

Actually, I figured it could make the transition from a stateless to a stateful function simpler 😊

Cheers, √

viktorklang avatar May 07 '20 23:05 viktorklang

@sleipnir let stay with @CommandHanler and let discuss about the semantic of stateless function :) based on the Dart implementation. I will ping you later for this.

ralphlaude avatar May 08 '20 11:05 ralphlaude

@viktorklang, in the StatelessFunction there are some functions taking akka.stream.scaladsl.Source as input like the one described below:

def handleStreamedIn(in: Source[FunctionCommand, akka.NotUsed]): Future[FunctionReply]

One way to map it in the handler (StatelessHandler) of the java support is to have something like this:

public Optional<Any> handleStreamInCommand(commands List<Any>)

If the source of command is small enough all is fine. But the source could effectively be infinite or very big then the proprosal does not work anymore. What could be a better option?

ralphlaude avatar May 19 '20 21:05 ralphlaude

@ralphlaude for this type of operation the stream will never be infinite. the user will be sending a finite stream, otherwise he would use Streamed (stream in and stream out) instead of this operation. What you need to do is to document the maximum allowed input stream size which in the case of the implementation in question will be given by the maximum number of Sink.seq. That is Int.MAX_VALUE

sleipnir avatar May 19 '20 21:05 sleipnir

a classic case for using this operation would be a file upload where the maximum file size and the required number of chunks needed for upload is previously known

sleipnir avatar May 19 '20 21:05 sleipnir

I was thinking about a possible API for a Stateless User Function for the java support. More generally one of the question here is to know which type we want to support. For this i have two proposals (see below). The first option supports Java Collection for streamedIn and Akka Source for streamedOut. The second option supports Akka Source for streamedIn and streamedOut. I would like to know what other type do we want to support.

@jroper on the last contributor call you mentioned something in this direction, meaning what types we should support for stateless function. Could you please elaborate on this?

The other question is for the streamedIn with Akka Source. The tradeoff is to know whether the proxy or the User Function should execute the incoming Source. If the User Function should execute the incoming it has to create its own materializer or the proxy should provide one. This puts the burden on the user and I think the proxy should execute the the incoming Source as pass the result to the User Function as Java Collection.

First option:

@Stateless
public class SomeStatelessService {

    @CommandHandler
    public SomeOutput someUnary(SomeInput input, CommandContext ctx) {
    }

    @CommandHandler
    public SomeOutput someStreamIn(List<SomeInput> inputs, CommandContext ctx) {
        // the source is executed by the proxy and the result is passed here as inputs
    }

    @CommandHandler
    public Source<SomeOutput, NotUsed> someStreamOut(SomeInput input, CommandContext ctx) {
    }

    @CommandHandler
    public SomeOutput someStreamed(SomeInput input, CommandContext ctx) {
    }
}

Second option:

@Stateless
public class SomeStatelessService {

    @CommandHandler
    public SomeOutput someUnary(SomeInput input, CommandContext ctx) {
    }

    @CommandHandler
    public SomeOutput someStreamIn(Source<SomeInput, NotUsed> inputs, CommandContext ctx) {
        // materializer needed here for running the inputs source
    }

    @CommandHandler
    public Source<SomeOutput, NotUsed> someStreamOut(SomeInput input, CommandContext ctx) {
    }

    @CommandHandler
    public SomeOutput someStreamed(SomeInput input, CommandContext ctx) {
    }
}

@viktorklang @pvlugter @sleipnir @marcellanz what do you think?

ralphlaude avatar Jun 26 '20 09:06 ralphlaude

@sleipnir Second option, definitely for me. Also, we can either expose a materializer via the CommandContext, or recommend to use the SystemMaterializer. Thoughts?

viktorklang avatar Jun 26 '20 11:06 viktorklang

@ralphlaude, @viktorklang This would be a user facing API and so far no akka.* types where used to the user. With both options, this would change. What would be against or in favour for to have a matching type of the JDK being included? I understand that the java-support is using akka.* types internally and I have no objections to use them as it might be natural. But, so far if I'm not mistaken, the JVM based API does not expose such types until now.

marcellanz avatar Jun 26 '20 11:06 marcellanz

@marcellanz @ralphlaude I think we could support both the java.util.concurrent.Flow-types AND Akka Streams—since there is no API in java to transform streams. OTOH, it is of questionable value to have a strict return type for methods which take a Source as an input argument. @jroper & @pvlugter, wdyt?

viktorklang avatar Jun 26 '20 11:06 viktorklang

@viktorklang if we use the second option (using AKKA Source) i would go for using the SystemMaterializer in the User Function. I am pretty careful to expose the proxy materializer to the User Function because perhaps of resource congestion that could happen if it is shared. is my my assumption correct? what do you think?

@marcellanz you are totally right to support JDK native types instead of only akka.* for the Java Support and i agree. The middle way would be to support both java.util.concurrent.Flow-types and akka.* like @viktorklang proposed. i think i would be good for the user facing API to have this flexibility.

@viktorklang really good point if it makes sense to strict return type for methods which take a akka.*.Source as an input argument. at first glance it could make easier, the User Function does not care about how to run AKKA streams.

ralphlaude avatar Jun 26 '20 13:06 ralphlaude

Hello gentlemen.

I have been discussing this topic with @ralphlaude at length and my conclusions are as follows. First, I have no problem exposing akka.* Types for the end user's api since java-support incorporates the Akka api. According to StreamIn it will always be a finite flow, it does not make sense an infinite flow of input that would return a unary type in its output. Unless this output was void, which I believe is not allowed in gRPC, then again I see no problem in exposing a primitive api java as Collection to streamIn, this would be even desirable for many developers who are not used to it with reactive apis. The user can also properly convert any type of entry to his preferred reactive library if he so wishes.

That said, @viktorklang proposal to support the materializer in the context is good and can be extended to expose the ActorSystem itself (since the user could reuse the akka log for example).

Having said all that, the summary about @ralphlaude work in supporting Stateless is that I think he is ready for a PR since he already manages to achieve the main objective of exposing an api for all supported gRPC types. I don't see why we hold on to this anymore, sometimes we need to move fast, even if it means offering the state of the art right away. I think that supporting java.util.concurrent.Flow is desirable since it allows users to use any reactive framework for the input and output types naturally, but we must remember that although java is not even its version 14 large Most of the systems base still works using version 8 and that java.util.concurrent.Flow was born in version 9, that is, I do not see support for this api as a priority for this PR. I think we can deliver something now and continue to evolve it gradually.

sleipnir avatar Jun 26 '20 13:06 sleipnir

@ralphlaude Maybe it’s interesting to send a PR, it can be a draft, with everything you’ve achieved in your implementation then we can all give you better insights

sleipnir avatar Jun 26 '20 16:06 sleipnir

@sleipnir good point regarding the api to be exposed. i am preparing the draft :)

ralphlaude avatar Jun 28 '20 00:06 ralphlaude