Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Status code replaced with Code::Internal on client when returning Err(Status) with Code::Ok on server #2074

Open
DOCtorActoAntohich opened this issue Nov 28, 2024 · 0 comments

Comments

@DOCtorActoAntohich
Copy link

A big question

most likely it's not a bug with tonic or grpc, but i'd like to clarify if things work as expected before making conclusions.

Versions

first noticed it with these older versions cos we didn't update for a while

[dependencies]
tonic = "0.11"
prost = "0.12"
prost-types = "0.12"

[build-dependencies]
tonic-build = "0.11"
prost-build = "0.12"

but it behaves exactly the same in the latest version

[dependencies]
tonic = "0.12.3"
prost = "0.13.3"
prost-types = "0.13.3"

[build-dependencies]
tonic-build = "0.12.3"
prost-build = "0.13.3"

Platform

seems irrelevant, but still

  • 23.6.0 Darwin Kernel Version 23.6.0 arm64

also occurs when communicating between my macos client and other linux server (unfortunately, i don't know distro is there)

Description

How we came to this question

imagine a legacy system that monitors some state in real time.
it also has this grpc api described in demo.proto - see a minimal example below.
unfortunately, we can't modify the .proto in the nearest future because it will break compatibility with other systems.

syntax = "proto3";

package demo;

service DemoService {
    rpc GetStatus(Empty) returns (SystemStatus);
}

message Empty {}

message SystemStatus {
    string some_crazy_state = 1;
}

fields in SystemStatus are irrelevant for this example.

all you need to know is that, after many big updates, the server that implements this protocol can no longer represent its state in a meaningful way via the old SystemStatus. (on the server, it was just a struct, but now it's a more complex enum).

as a silly workaround, just to let the client know that the system is in a special state,
we made one of the branches that returns the status look kinda like this (stripped down to the basics again)

mode demo {
    tonic::include_proto!("demo");
}

struct MyService;

#[tonic::async_trait]
impl demo::demo_service_server::DemoService for MyService {
    async fn get_status(
        &self,
        _request: tonic::Request<demo::Empty>,
    ) -> tonic::Result<tonic::Response<demo::SystemStatus>> {
        Err(tonic::Status::new(
            tonic::Code::Ok,
            "Cannot return meaningful response, but the service works fine",
        ))
    }
}

so as the code suggests, the tonis::Status with Code::Ok and some text message is intended to tell the client that the server is not dead and is working properly, but just cannot give a proper expected response at this moment (will be able to in a few seconds).

Actual behavior on the client

the following call on the client

let result = client.get_status(demo::Empty{}).await;
dbg!(result);

prints the following

result = Err(
    Status {
        code: Internal,
        message: "Missing response message.",
        source: None,
    },
)

which is different from what was coded on the server.

Expected behavior?

here is what makes me confused.

first, tonic does not prevent me from doing things this way, therefore, on the first glance, it looks like a valid solution, simply because nobody told me "you can't do that". since rpc "looks like a regular function call" then to me it seems like that the client should get exactly what was sent from the server. and if we draw parallels with http, nobody prevents you from returning 404 not found instead of 204 no content - it's not recommended, but it's possible :trollface:

second, postman adds fuel to the fire, because it simply shows what i'd expect. that's how we tested the server at first, and that's why we didn't realize at first that our tonic client implementation works differently

image

and finally, we went through some md files in grpc repo, but couldn't find a concrete answer to how exactly it should behave.

The real question

to avoid confusion now and in the future, i would like to ask the developers to clarify - what's the intended behavior when we return tonic::Status with Code::Ok and arbitrary text message in the Result::Err branch from the server?

(although i admit this edge case is quite dumb, and there are many other, probably more fitting status codes to use)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant