2

In an RPC (Remote Procedure Call) design, an interaction across the network is hidden behind a synchronous API that makes the network interaction look (to the rest of the local program) like "just another local function call". The function-call sends its arguments (in some form) to the remote machine, waits for the corresponding reply to come back, and then parses that reply-data to compute a value to return.

That's convenient for programmers who are used to making synchronous/local function calls, as it functions logically the way they are used to, but it seems like there is one big potential fly in the ointment: unlike most local function calls, it's difficult to make any predictions about how quickly an RPC function call will return. That is, even the most trivial "O(1)" RPC-call might get stalled by a temporary network outage, such that the reply-message from the remote machine doesn't come back for several seconds (or even minutes), leaving the local RPC-calling thread "hung" for an unpredictable amount of time.

Once way to ameliorate the problem would be to specify a timeout (e.g. "if the server doesn't reply within 10 seconds, the RPC call should return with an error), but that's not entirely satisfactory either, since if you set the timeout-threshold too high, you still have unacceptably long "hangs", and if you set it too low, you start getting false-positive errors (i.e. where the server did respond, but not quite fast enough to satisfy the specified timeout; and now you'll have to repeat the entire process again unnecessarily, adding yet more load to the server).

Is there some other approach that RPC-based designs use to address this problem, or is it the case that RPC is only appropriate in situations where function-call durations are relatively unimportant, or the network is guaranteed to be "robust enough that this problem won't happen"?

2
  • You also have integrity checks and acknowledgements to let the other party know you received a coherent chunk well and we can now move on to the next big thing. But there is no way to make up for physical imperfections other than deal with them. If this is disappointing, perhaps mathematics is your thing. Jun 22, 2021 at 6:41
  • @MartinMaat it's not disappointing, I've been doing (non-RPC) network programming for a while and I'm used to it. I am curious how the RPC-style APIs handle it, though. Jun 22, 2021 at 19:03

3 Answers 3

2

In an RPC (Remote Procedure Call) design, an interaction across the network is hidden behind a synchronous API that makes the network interaction look (to the rest of the local program) like "just another local function call".

Any network request in a program can be hidden behind a synchronous local API. There's nothing special about RPC in that regard. However, I think one of the failures of RPC as a style is the idea that we can ignore that network interaction in a application's design. The fallacies of distributed computing are as follows:

  • The network is reliable;
  • Latency is zero;
  • Bandwidth is infinite;
  • The network is secure;
  • Topology doesn't change;
  • There is one administrator;
  • Transport cost is zero;
  • The network is homogeneous.

In reality, any web service call is 'remotely executing a procedure' of some sort. We are really just talking about an architectural style. A style that's not terribly sophisticated. If you look through any of the innumerable threads about exception handling, you'll find that one of the main kinds of exceptions people attempt to handle is IO, especially networking. Not only can it fail, it should be expected to fail relatively frequently.

4
  • 1
    For contrast, the style I usually design for is the asynchronous-message-passing style, where each program has an event loop and can send and receive Messages across the network, and never blocks except in a select()-style wait-for-the-next-event-from-any-source call. That approach requires a bit more explicit state-management, as the program has to keep track of what requests it has sent and is expecting replies for, but the advantage is that it will never get "stuck in a call" due to network or server problems; the thread always remains responsive to input. Jun 22, 2021 at 19:01
  • @JeremyFriesner Yes, that's what I often do now (when I need it) as well. Language support and nonblocking IO has made that a lot easier as well in recent years (outside of JS.) But at the end of the day, you still need to account for the possibility some or all of those calls will not return usable result. What that means for your application varies wildly.
    – JimmyJames
    Jun 22, 2021 at 19:13
  • That's a great list! But even the original RPC took into account a lot of those items. At least, it made provisions for the application to take those into account. And applications that used RPC like NFS certainly did and do take most of those into account. I think your list should be hung on the wall of anyone doing distributed computing.
    – user10489
    Jun 22, 2021 at 22:54
  • 1
    @user10489 I'm glad you like it but I can't claim it. IIRC L. Peter Deutsch came up with 7 of those and someone else added an 8th item. I did hang it up on the wall at one of my jobs, though. To your other point, anyone working with networked applications needs to deal with this (RPC or not.) My point is more that the idea that you can sweep this under the rug is wrong so that's not really an advantage of RPC and I think it's better understood that certain styles of interaction are preferred.
    – JimmyJames
    Jun 23, 2021 at 13:53
0

I think all of the approaches you mentioned are used, depending on the situation. Really RPC is not different than any other network situation, and it can be implemented via UDP or TCP. Do you want your data reliably transmitted and in order? Or is it better to get it faster, but possibly out of order or not at all? Some protocols have switched back and forth between UDP and TCP. (NFS started as RPC over UDP, but now is exclusively TCP. HTTP started as exclusively TCP, but current versions of http are supporting UDP instead.)

Network performance is measured in bandwidth and latency, and doesn't use order notation. Also, some network transactions, including RPC, can be asynchronous, where the "caller" doesn't wait for an answer, but expects to get it later, and may move on to other things before the answer is returned. Network responses are rarely measured in seconds -- response times are more typically in the 20ms to 400ms range on a WAN and much much shorter on a LAN.

If reliability is needed, TCP is frequently used. The TCP protocol handles getting the data over the network reliably and does bandwidth management and congestion control. To the application, a TCP connection looks like a data stream rather than packets. If network traffic is interrupted for more than 30 seconds, TCP may drop the connection completely.

UDP is frequently faster, but then the application has to deal with reliability issues itself. It may sometimes not get an answer or get them out of order, and it may have to deal with flow control as well. Missing a response from the network is not necessarily an error -- the application can retry the transmission. TCP uses lost packets as a signal that data is being sent too quickly and will throttle and retry. UDP's advantage here is that the application can implement its own newer algorithms and timeouts and possibly get better results than the canned TCP tuning, some of which is now 50 years old.

2
  • 1
    "HTTP started as exclusively TCP, but current versions of http are moving to UDP" – That's misleading. HTTP/3 is using QUIC, which is a reliable protocol like TCP, but applying 50 years of "lessons learned". Conceptually, QUIC is an L4 protocol like UDP, RDP, and TCP. The fact that QUIC is implemented on top of UDP is a concession to backwards-compatibility (so-called "middle boxes", i.e. all the devices data flows through that sit "in between" the browser and the server know how to deal with TCP, UDP, and nothing else, and upgrading them would take decades). Jun 22, 2021 at 5:59
  • I was trying to imply the 50 years of lessons learned...I'll try to phrase that better. But it does use UDP -- if UDP wasn't better for it, why would it not fall back to TCP instead? That was the point. Going into the details of QUIC and RDP would have made this even longer and not answered the question better.
    – user10489
    Jun 22, 2021 at 11:23
0

If we're talking about a regular 3-tier intranet application (Windows client -> Backend webservice -> Database) where the Windows client communicates via RPC style SOAP or REST webservice, I would say you basically assume that there may be a network connectivity error, but only in very rare circumstances.
Because TCP handles resending of lost network packets and guarantees in order delivery, a simple 20 second timeout on your RPC calls is usually enough to get your responses and provide a happy user experience.

Only problems like someone pulling a network plug or taking down a proxy server cannot be handled by this, and if one of these occurs, they shouldn't leave the application in an inconsistent state where data might be corrupted. And unless it's a very simple CRUD application, because a typical operation might involve several RPC calls and each one could fail, it's not easy to keep the client in a stable state after such a failure, and it might be considered acceptable to have the client just shut down gracefully after this.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.