fermyon / spin

Spin is the open source developer tool for building and running serverless applications powered by WebAssembly.
https://developer.fermyon.com/spin
Apache License 2.0
5.16k stars 247 forks source link

How to Invoke a function which is on an another application? #1241

Open Patrick0308 opened 1 year ago

Patrick0308 commented 1 year ago

In spin, how can I invoke a function of another application? Is it necessary to do it through the network? Or can't? If it is possible, it's awsome.

radu-matei commented 1 year ago

Currently, you have to go through the trigger — if it's an HTTP component, you can make an outbound HTTP call to the endpoint for that function (and if it's another trigger, generate an event that would trigger that function).

For example, if you have a front-end and a back-end, you could make calls from the front-end to the back-end: https://github.com/radu-matei/spin-react-fullstack/blob/0252f6eb64be0276b9f2de30193ab89b4807ed09/frontend/src/App.js#L8-L16

(notice how the back-end is on the /api/... route: https://github.com/radu-matei/spin-react-fullstack/blob/0252f6eb64be0276b9f2de30193ab89b4807ed09/spin.toml#L22-L23)

This is a Rust example that makes a call to another component of the same application (or to another application, depending on where the URL points): https://github.com/fermyon/spin-kitchensink/blob/c0d0f2b0487df0cb9f151b7dd8f7cd13f9ab1087/rust-outbound-http/src/lib.rs#L19-L24

We are trying to simplify the scenario where two components of the same application are trying to make calls to each other — see #957.

In the future, we want to enable invoking other components based on the component model — but that is not yet possible.

Let me know if this answers your question.

Patrick0308 commented 1 year ago

@radu-matei Yeah, Thanks for your reply.

In the future, we want to enable invoking other components based on the component model — but that is not yet possible.

Looking forward to this feature. It's seems that better than traditional microservice rpc.

Patrick0308 commented 1 year ago

In the future, we want to enable invoking other components based on the component model — but that is not yet possible.

@radu-matei Are there any plans for this amazing feature?

Patrick0308 commented 1 year ago

@radu-matei I'm so interest in invoking other components feature. May be we can split it into multi small tasks? Can assign some task to me?

jpshackelford commented 10 months ago

What?

Nice to see that we now have the conveniences for HTTP communication between components here: https://github.com/fermyon/spin/issues/957.

I would love to see some means of more direct component to component interaction on the basis of direct function calls. For example we have could a spring component written in Rust that would expose a function for another component written in JavaScript to call. My grasp of WASM is tenuous at best but it appears that at least when running in a browser context a WASM module can declare exports accessible via javascript c.f. https://developer.mozilla.org/en-US/docs/WebAssembly/Exported_functions. I am imaging that spin.toml would declare an import (perhaps reiterating an export as well) and that those are then available for a call.

Why?

As new Spin SDKs come online would allow earlier use from other language environments. For example today we have the ability to connected to external DBs from Rust but not JavaScript. With this in place, a Rust component could provide access to external DBs by virtue of function call rather than having to make it available to a JavaScript component with another network call. There may be faster better solutions to this particular problem but we could imagine others like it. One of the beauties of a component / plugin model is that the spin ecosystem can grow via contributions from users of many different language / library ecosystems.

CORRECTION: Today we can use external DBs from JavaScript but the doc is not up-to-date. See https://github.com/fermyon/developer/issues/932.

How?

What pieces are needed for something like this to work?

Javy from which spin-js-sdk borrows, does have support for exporting functions with a wit file that can be executed via wasmtime run --invoke. Javy also provides support for dynamically linked modules which are accessible to the main wasm/wasi app via wasmtime run --preload. I have not read the spin code yet to understand what runtime it uses--is it wasmtime or something else?--or how it is invoked. I'll add any further learnings in a follow-up comment. Pointers from the core team appreciated if they read this before I get further.

jpshackelford commented 10 months ago

Now reading https://github.com/fermyon/spin/blob/main/crates/core/src/lib.rs and https://github.com/fermyon/spin/blob/main/crates/core/src/store.rs ... : )

Also helpful:

Re-reading about the component model is helping.

jpshackelford commented 10 months ago

See also https://github.com/fermyon/spin/discussions/1686#discussioncomment-6732756 and https://www.fermyon.com/blog/composing-components-with-spin-2

I am also trying to wrap my head around what is happening in trigger-http (below). With wasm-tools compose two components are statically linked and combined into a new binary but this appears to load and invoke another component dynamically -- is that right? Or is this Rust feature not really a wasm feature?

        let (instance, mut store) = engine.prepare_instance(component_id).await?;
        let EitherInstance::Component(instance) = instance else {
            unreachable!()
        };

        set_http_origin_from_request(&mut store, engine, &req);

        let resp = match HandlerType::from_exports(instance.exports(&mut store)) {
            Some(HandlerType::Wasi) => Self::execute_wasi(store, instance, base, raw_route, req, client_addr).await?,
            Some(HandlerType::Spin) => {
                Self::execute_spin(store, instance, base, raw_route, req, client_addr)
                    .await
                    .map_err(contextualise_err)?
            }
            None => bail!("Expected component to either export `{}` or `fermyon:spin/inbound-http` but it exported neither", WASI_HTTP_EXPORT)
itowlson commented 10 months ago

@jpshackelford What you are seeing there is some host magic to adapt core modules so that Spin can treat everything, classic core module or new-fangled component, as a component model component.

For composing CM components, my colleague @kate-goldenring has just published a blog https://www.fermyon.com/blog/composing-components-with-spin-2 which addresses how to do this with actual Wasm components - that might be more relevant to what you're asking about I think? It's not directly what you're asking about here but possibly in the right direction?

itowlson commented 10 months ago

@jpshackelford wow... I am a complete muppet. You mentioned Kate's blog in the first line of your comment. Sorry for missing it and apologies for my redundant comment!

jpshackelford commented 10 months ago

@itowlson Thanks very much for the quick reply. I am asking some very newb questions here so I appreciate the patience of you and others. I had the pleasure of being in the room where @kate-goldenring presented this morning and enjoyed her talk.

I think I am attracted to this host magic approach because it is very simple and dynamic whereas creating components has the overhead building the wit files and running wasm-tools compose so the end-user loses some of the feel of staying in their own language ecosystem and "borrowing" pre-built functions from another ecosystem.

I am still learning about wit-bindgen and witgen. Perhaps finding ways to have spin do more work with these and wasm-tools compose behind the scenes would help but I have more learning to do. I'll keep posting questions learnings on this issue as trail for others with similar interests but also happy to move to posts in Discord or https://github.com/fermyon/spin/discussions if that is better.

itowlson commented 10 months ago

Host magic is very much where we want to get to. @fibonacci1729 has a SIP in progress, https://github.com/fermyon/spin/pull/1804, which we were hoping to get done for Spin 2.0 but ran out of time: this proposes a way for users to say, in the spin.toml file, "hey Spin, compose my handler with this auth component (or that credit card processing component, or...) and run that composed component."

But yes, for now, manual composition using wasm-tools is very much where it's at. And even Brian's proposal doesn't solve the problem of "huh, I need to run a special cargo component build command... and copy all these WITs... and..." Adoption for that stuff into core toolchains will take a lot of time.

kate-goldenring commented 10 months ago

@jpshackelford this is helpful to hear where the journey of using components is becoming convoluted. As @itowlson mentions, SIP https://github.com/fermyon/spin/pull/1804 will help remove the wasm-tools compose but it does not remove the need for someone to use their language's component tooling to generate bindings and use them within their component, which does require an understanding of WIT. There have been discussions within the Bytecode Alliance about creating a tool that can generate docs for generated bindings to make it easier to understand how to use them.

I could also see us get to a future where ubiquitous components or libraries become well known enough again that they each have their own upstream SDKs for using them. You could imagine auth_middleware::authenticate() being the call instead of crate::wasi::http::incoming_handler::handle(request, output.into_inner());