NivaarExam PrepOfficial exam papers ↗

25-Comp-B10 Distributed Systems · May 2017

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

98-Comp-B10 Distributed Systems — National Examinations, May 2017. 3 hours, closed book, non-programmable calculator only. Candidates were instructed to answer any five of the six questions (only the first five as they appear in the answer book are marked), all carrying equal weight and mostly requiring essay-format answers; 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, peer-to-peer systems, middleware and client-server architecture (ch. 1–2), interprocess communication and the request-reply protocol (ch. 4–5), remote invocation (ch. 5), operating system support for distributed systems (ch. 7), security (ch. 11), distributed file systems (ch. 12), and time, coordination, replication and fault tolerance (ch. 14–15, 18).

Check — source parsing artifact. Every question header on this paper is printed as “Question # N.” (a literal hash between the word and the number).

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)(i) The RPC concept and why it is a breakthrough. A remote procedure call lets a program invoke a procedure that executes on a different process — typically on a different machine — using the same call syntax as an ordinary local procedure call: the caller supplies arguments and receives a return value exactly as if the procedure ran in its own address space, while underneath, a client stub marshals the arguments into a message, sends it across the network, and a server stub unmarshals them, invokes the real procedure, and returns the result the same way in reverse. This is a major intellectual breakthrough because it lets a programmer reason about a distributed computation using the same procedural abstraction already used for local computation, hiding the network communication, marshalling and remote-invocation mechanics behind a familiar interface — before RPC, building a distributed application meant hand-coding explicit message send/receive calls and manually encoding/decoding every argument, which was tedious and error-prone and made distributed and local code look and behave completely differently.

(a)(ii) RPC design issues. 1. Programming with interfaces. Because the calling and called procedures may be written in different languages or compiled separately, an RPC system needs a language-neutral interface definition language (IDL) to specify a remote procedure's name, parameters and return type; the IDL specification is then compiled into client and server stub code in each side's actual programming language, so the interface — not a shared source file — is the contract between the two independently-developed sides. 2. Call semantics. Because a network message (the call, or its reply) can be lost, and because the caller generally cannot tell whether a failure occurred before or after the remote procedure actually executed, RPC systems must define what guarantee a call gives on failure: maybe semantics (no retry at all — the call may or may not have executed), at-least-once semantics (retransmit the request until a reply arrives, risking the procedure executing more than once if a reply was merely lost, not the original request), and at-most-once semantics (retransmit but use duplicate-request filtering so the procedure executes at most a single time even under retransmission) — the choice matters because a non-idempotent operation (e.g. "transfer funds") behaves very differently under at-least-once than under at-most-once. 3. Transparency. The goal of RPC is to make a remote call look as much like a local call as possible (access transparency), but full transparency is not fully achievable: a remote call can fail in ways a local call cannot (partial failure, network partition, arbitrary added latency), so RPC systems in practice aim for the syntax of a local call while accepting that its failure modes and latency remain genuinely different — hiding this difference completely from the programmer can itself be dangerous, since code that assumes a call "never fails" the way a local call effectively never does will not handle a genuine remote failure correctly.

(b)(i) The request-reply primitives. The request-reply protocol is built from three complementary communication primitives that together implement a client-server exchange directly over an unreliable, connectionless transport (typically UDP), without needing a full connection-oriented protocol. doOperation is invoked by the client: it marshals the request message (containing the identifier of the remote operation and its arguments), sends it to the server, and then blocks waiting for the reply message to arrive (or for a timeout, at which point it may retransmit), finally returning the unmarshalled result to the calling code. getRequest is invoked by the server: it blocks waiting for a request message to arrive at the server's designated port, then unmarshals and returns the operation identifier and arguments to the server's dispatcher, which selects and invokes the actual procedure implementing that operation. sendReply is invoked by the server once the requested operation has completed: it marshals the result (or an exception) into a reply message and sends it back to the client that is waiting inside its call to doOperation, completing the round trip.

(b)(ii) Masking heterogeneity. The request-reply protocol masks heterogeneity of operating systems and networks by defining its three primitives, and the wire format of the messages they exchange, entirely in terms of an external, agreed representation rather than either side's native in-memory representation. Before sending, doOperation/sendReply marshal arguments and results into this common external format (converting local data types, byte ordering/endianness, and structure layout into a standard wire encoding, e.g. via an IDL-generated marshalling routine), and the receiving getRequest/doOperation unmarshal that same wire format back into whatever native representation the local machine and language use. Because every participant only ever needs to marshal into, and unmarshal out of, this one agreed external format, a client running on one OS/CPU architecture (say, little-endian x86 under Linux) can transparently interoperate with a server on a completely different one (say, big-endian under a different OS), and the network in between is used only as a generic byte-stream/datagram transport — the primitives themselves make no assumption about what network technology carries the bytes, so the same request-reply exchange works unchanged over Ethernet, Wi-Fi, or any other underlying network.

(c) RMI timing: single-threaded vs. two-threaded client.

Given. Client compute time per request 4 ms; server processing time per request 10 ms; local OS send/receive processing 0.6 ms per operation; network transmission 3 ms per message (request or reply); two RMI calls to time, single-threaded vs. two-threaded on one client processor.

Given data — Q3(c)
QuantityValue
Client compute time (per request)4 ms
Server processing time (per request)10 ms
OS send/receive (per operation)0.6 ms
Network transmission (per message)3 ms

Find. Total elapsed time for the client to issue and complete two RMI calls, single-threaded and two-threaded.

Approach. Split each RMI round trip into the segments that occupy the client's own CPU (compute, OS send, OS receive) versus the segments that happen off the client CPU (network out, server processing, network back); a single-threaded client must serialize both calls entirely, while a second thread can use the client CPU during the first call's off-CPU wait (no separate marshalling cost is given here, so it is folded into the stated compute/OS figures).

  1. One RMI round trip. Client CPU segments: compute (4) + OS send (0.6) + OS receive (0.6) $=5.2$ ms. Off-CPU segments: network out (3) + server processing (10) + network back (3) $=16.0$ ms. $$\boxed{\text{RTT}_1=5.2+16.0=21.2\ \text{ms}}$$
  2. (i) Single-threaded, two requests. A single thread's RMI call blocks until its reply returns, so the second call cannot begin until the first fully completes: $$\boxed{T_{single}=2\times21.2=42.4\ \text{ms}}$$
  3. (ii) Two threads, one client processor. Splitting each call's client-CPU work into a 4.6 ms pre-send slice (compute + OS send) and a 0.6 ms post-receive slice (OS receive): Thread 1 occupies the CPU from $t=0$ to $t=4.6$ and sends its request, then waits off-CPU for 16 ms while the CPU sits free. Since the off-CPU wait (16 ms) exceeds a whole pre-send slice (4.6 ms), Thread 2 runs its own pre-send slice on the now-free CPU from $t=4.6$ to $t=9.2$ and sends its request at $t=9.2$, with no CPU contention between the threads. Thread 1's reply lands at $t=4.6+16=20.6$; the CPU is free (Thread 2 is off-CPU waiting), so Thread 1 finishes its 0.6 ms post-receive slice at $t=21.2$. Thread 2's reply lands at $t=9.2+16=25.2$, and it finishes its own post-receive slice at $t=25.8$ (CPU free by then). $$\boxed{T_{2\text{-thread}}=\max(21.2,\,25.8)=25.8\ \text{ms}}$$ Assumption: the server has enough capacity to process both requests without one queuing behind the other (e.g. a multi-threaded or multi-processor server); the client's own single CPU is the only serialization constraint being modelled.
Final Results — Q3(c)
ScenarioTotal time for 2 RMI calls
Single-threaded42.40 ms
Two threads, single client CPU25.80 ms