eclipse-uprotocol / up-rust

uProtocol Language Specific Library for Rust
Apache License 2.0
11 stars 9 forks source link

How to get RPC reply if we use up-l1 API `send` #50

Closed evshary closed 7 months ago

evshary commented 7 months ago

invoke_method can get the reply UMessage from RpcClientResult (pub type RpcClientResult = Result<UMessage, RpcMapperError>;)

async fn invoke_method(
        &self,
        method: UUri,
        request: UPayload,
        options: CallOptions,
    ) -> RpcClientResult;

I'm wondering how we get the reply when we use up-l1 API send to comprise the invoke_method.

After taking a look, I guess receive should be called after we use send to send out the request. async fn receive(&self, topic: UUri) -> Result<UMessage, UStatus> However, is it possible to send multiple requests to the same topic at the same time? If yes, we'll have trouble to distinguish the mapping of request and reply.

PLeVasseur commented 7 months ago

I'm glad you're asking about this :slightly_smiling_face:

I was also unsure how this would work.

Essentially there must be a way within UTransport::register_listener() such that we're able to receive back any replies relevant to us.

Is that possible? Are there technical challenges?

What I had imagined is that the Zenoh implementation would have to keep track of the Queryable it sent out and listen for replies over in register_listener(). But I probably don't understand Zenoh well enough to tell if this works.

evshary commented 7 months ago

Hi @PLeVasseur Thanks for your input! I think the key is how to use reqid in uAttributes to map to the corresponding reply in Zenoh. While UTransport::receive doesn't have this argument, we can only use UTransport::register_listener() and put the certain reqid into callback as a filter here, indeed. Let me chew on it and come up with a proper way.

By the way, do you know why we added the new API UTransport::receive and which kind of case we should use it?

PLeVasseur commented 7 months ago

I believe there's an option at uP-L1 to implement it as a push or pull mechanism.

I think the addition of receive() is to keep up-rust compliant with the spec, offering it as an option when implementing a up-client-foo-rust.

From my point of view it's at a minimum an either or thing, both need not be implemented, not relevant to this implementation I think.

evshary commented 7 months ago

Thank you for the clear explanation. Then I'll ignore receive() and focus on register_listener() for the time being. Let me keep the issue open and I can update the status if I encounter any other technical issues.

sophokles73 commented 7 months ago

@evshary when you send a request message using UTransport::send then the request must contain

  1. a reply-to-address in the UAttributes::source field, and
  2. a correlation ID in the UAttributes::reqid field.

So, before you send the request message, you need to make sure to register a listener for the reply-to-address. In the callback you can then compare the correlation ID.

evshary commented 7 months ago

Hi @sophokles73 Thank you for the valuable input!

What bothered me was that Zenoh has three different mechanisms for publish / request / response. That means I need to distinguish which type we're using while calling send() or register_listener() This is not the problem for send() since we can get type from uAttributes. However, it's quite difficult for register_listener since there is only UUri.

   async fn register_listener(
        &self,
        topic: UUri,
        listener: Box<dyn Fn(Result<UMessage, UStatus>) + Send + Sync + 'static>,
    ) -> Result<String, UStatus>;

But I just found out we can distinguish RPC and RPC response with the UUri validator. https://github.com/eclipse-uprotocol/up-spec/blob/main/basics/uri.adoc#3-validators Maybe this can do the magic. I'll check it in the weekend.

evshary commented 7 months ago

I think this can be closed now.