NivaarExam PrepOfficial exam papers ↗

25-Comp-B10 Distributed Systems · Undated paper

Question 3 of 6: Client-Server Systems and Inter-Process Communication

Nivaar worked solution (AI-drafted; not reviewed by a licensed engineer)

Notes on this paper

17-Comp-B10 Distributed Systems — National Exams, May 2019. 3 hours, closed book, Casio/Sharp approved calculator only. Candidates were instructed to answer any five of the six questions, only the first five as they appear in the answer book marked, with most questions requiring an essay-format answer; all six are answered below as a complete study resource.

Reference texts: Coulouris, Dollimore, Kindberg & Blair, Distributed Systems: Concepts and Design (5th ed.) — system models, characterization of distributed systems and openness (ch. 1–2), networking and internetworking, TCP/IP (ch. 3), interprocess communication and remote invocation, RPC (ch. 4–5), operating system support (ch. 6–7), security (ch. 11), distributed file systems (ch. 12).

Question 3: Client-Server Systems and Inter-Process Communication (20 marks)

Question text not reproduced: the examination questions are © Engineers and Geoscientists BC. Open the official past paper (linked at the top of this page) to read the question, then follow the worked solution below.

(a) Two chained asynchronous RPCs vs. a normal RPC. No — it is not the same, although the client's blocking behaviour looks alike (it sends a request and does nothing until the result arrives). An asynchronous RPC returns control to the caller as soon as the server has acknowledged receipt of the request, without waiting for a result. The differences are: (1) Messages: the client's asynchronous call costs a request plus an acknowledgement, and the server's result-carrying asynchronous call costs a second request plus acknowledgement — four messages, where a normal RPC needs two (request, reply), because in a normal RPC the reply carries the result and implicitly acknowledges the request. (2) Roles: the client must itself export an interface and act as a server to receive the callback, and must match that incoming call to its outstanding request. (3) Failure semantics: a normal RPC's runtime times out and retransmits until it gets a reply, giving at-least-once/at-most-once call semantics; with two independent one-way calls, if the result-carrying call is lost the client simply waits forever unless the application adds its own timeouts and retries. The two constructions are therefore equivalent only if communication is reliable, which in general it is not. Replacing the asynchronous RPCs with synchronous RPCs still does not give a normal RPC. The client's first synchronous call blocks until the server replies; if the server simply returned the result in that reply, the second call would be redundant (that is a normal RPC). As described, however, the server replies without the result and later calls the client back synchronously, so the exchange takes two full request-reply pairs (four messages), the server is also blocked until the client answers the callback, and if the server makes the callback before replying to the first call, a single-threaded client that is still blocked inside that call cannot service it — the two sides deadlock. The synchronous version is thus at best twice the cost of a normal RPC, and at worst a deadlock.

ClientApplicationClient Stub(proxy)RPC Runtime(client)RPC Runtime(server)Server Stub(skeleton)ServerApplication1. local call2. marshal3. request over network4. unmarshal5. local call7. marshal8. reply over network9. unmarshal
RPC architecture: client application → client stub (marshals the call into a request message) → client-side RPC runtime → network → server-side RPC runtime → server stub (unmarshals and dispatches the local call) → server application, then symmetrically in reverse for the result (steps 5–9, mirroring 1–4).

(b) Time for the client to generate and return from two requests.

Given. Client compute time per request = 5 ms; server processing time per request = 10 ms; OS processing time per send or receive operation = 0.5 ms; network latency per one-way message = 3 ms; marshalling or unmarshalling = 0.5 ms per message; single-threaded client; ignore context-switching time.

Find. The total elapsed time for the client to generate and return from two remote procedure calls.

Approach. Decompose one RPC round trip into its component operations, sum them, then multiply by two since a single-threaded client cannot overlap the second request's compute phase with the first request's still-outstanding reply.

  1. Decompose one RPC round trip. Each round trip consists of: client compute ($5$ ms) + marshal request ($0.5$ ms) + OS send at client ($0.5$ ms) + network transit of the request ($3$ ms) + OS receive at server ($0.5$ ms) + unmarshal request ($0.5$ ms) + server processing ($10$ ms) + marshal reply ($0.5$ ms) + OS send at server ($0.5$ ms) + network transit of the reply ($3$ ms) + OS receive at client ($0.5$ ms) + unmarshal reply ($0.5$ ms) — four OS send/receive operations, four marshal/unmarshal operations, and two network legs in total.
  2. Sum one round trip. $$t_{\text{RPC}} = 5 + 4(0.5) + 4(0.5) + 2(3) + 10 = 5 + 2 + 2 + 6 + 10 = \boxed{25\ \text{ms}}$$
  3. Scale to two requests. The client is single-threaded, so it cannot begin computing the second request's arguments until the first RPC has fully returned — there is no pipelining. $$t_{\text{2 requests}} = 2 \times t_{\text{RPC}} = 2 \times 25 = \boxed{50\ \text{ms}}$$
Final Results — Q3(b)
QuantityValue
Time per single RPC round trip25 ms
Time for client to generate and return from two requests50 ms

(c) RPC architecture. The figure above shows the standard RPC architecture. The client application makes what looks like an ordinary local procedure call to the client stub (a generated proxy that presents the exact same interface as the real remote procedure). The client stub marshals the call's arguments into a flat, transmittable message and hands it to the client-side RPC runtime, which is responsible for the request-reply protocol itself — addressing, sending the message over the network, and retransmitting/timing out if no reply arrives. The message crosses the network to the server-side RPC runtime, which passes it to the server stub (skeleton); the server stub unmarshals the arguments and makes the corresponding ordinary local call into the server application. The result flows back through exactly the same chain in reverse: server application → server stub (marshal result) → server RPC runtime → network → client RPC runtime → client stub (unmarshal result) → client application, which receives the result exactly as if the original call had returned locally. The key architectural property is that the stubs make remote invocation transparent to both application programs — neither is written any differently than it would be for a purely local call.

(d) Marshalling and unmarshalling, with examples. Marshalling is the process of converting a structured, in-memory representation of data (procedure arguments, a return value, an object) into a flat sequence of bytes suitable for transmission over a network or storage to a file, in a form that a receiver on a possibly different architecture/language/platform can correctly reconstruct. Unmarshalling is the reverse process at the receiving end: parsing that flat byte sequence back into the structured, in-memory representation the receiving program's language expects. Examples across different technologies: in classic Sun/ONC RPC, marshalling is done via XDR (External Data Representation), which defines a fixed, platform-independent binary encoding for each data type (e.g. always big-endian 4-byte integers) so a little-endian client and a big-endian server still agree on the bytes. In Java RMI, marshalling is Java object serialization, which flattens an entire object graph (including type information) into a byte stream. In a modern Web-services/REST setting, marshalling is commonly JSON serialization — converting an in-memory object/struct into a JSON text document — with unmarshalling being the corresponding JSON parse back into a language-native object on the other end; Protocol Buffers/gRPC instead marshal into a compact binary encoding described by a shared schema (a `.proto` file) for higher performance than text-based JSON.