25-Comp-B10 Distributed Systems · December 2016
Nivaar worked solution (AI-drafted; not reviewed by a licensed engineer)
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) Local call vs. remote call. A local procedure call executes within a single address space: arguments are passed by direct reference to shared memory, the call always either completes or crashes together with its caller (no partial failure — caller and callee share the same fate), and latency is nanoseconds (a stack push/pop and a jump). A remote call crosses a process and usually a machine boundary over a network: arguments must be marshalled into a message (no shared memory, so pointers/references are meaningless to the other side) and unmarshalled at the far end; the call can fail independently of the caller — the network can drop the message, or the remote process/machine can crash, while the caller keeps running (partial failure, which a local call can never exhibit); latency is orders of magnitude higher and variable rather than constant; and the caller must handle new categories of error (timeout, lost message, remote crash) that simply do not exist for a local call.
(b) Call by reference vs. call by value. Call by value passes a copy of the argument's value to the callee; the callee may modify its own local copy freely, but this has no effect on the caller's original variable. Call by reference passes the address of the caller's variable, so the callee operates directly on the caller's storage and any modification is visible to the caller once the call returns. In a distributed setting, true call-by-reference for an ordinary parameter is impossible once the call crosses address spaces — the callee has no access to the caller's memory at all — so RMI/RPC systems marshal ordinary (serializable) parameters as copies and pass them by value (sometimes called "copy-in/copy-out" semantics, since any returned/output value is copied back at completion, not written live into the caller's memory). The one exception is a parameter that is itself a remote object reference (e.g. in Java RMI): rather than copying the whole remote object, the system passes a proxy/stub that all callers use to reach the same single remote object, which behaves like reference semantics for that object even though the reference itself was, mechanically, copied across the network.
(c) Three ways to invoke a method on a remote object. (1) Remote Method Invocation (RMI) — e.g. Java RMI: a local proxy object exposes the same interface as the remote object; calling a method on the proxy marshals the call and forwards it to a server-side skeleton/dispatcher that invokes the real method, giving the caller ordinary object-oriented call syntax. (2) Remote Procedure Call (RPC) via a generated stub — e.g. CORBA or gRPC: an IDL-defined interface is compiled into a client-side stub and server-side skeleton, and the "method" is invoked as if it were a locally-linked procedure, with the stub handling marshalling transparently. (3) Message-based / dynamic invocation — e.g. CORBA's Dynamic Invocation Interface, or a hand-built request-reply message naming the target object and method explicitly: the client constructs a message identifying the object reference, the method name and its arguments at run time (no compiled stub required) and a generic server-side dispatcher decodes it and performs the call, which trades the convenience of a generated stub for the flexibility of invoking methods not known when the client was compiled.
(d) Purpose of an Interface Definition Language. An Interface Definition Language (IDL) specifies a remote object's or service's interface — its method names, parameter types and return types — in a notation that is independent of any particular programming language or machine architecture. From one IDL specification, a compiler generates the matching client-side stub (proxy) and server-side skeleton for each language a project uses, and both sides are then guaranteed to agree on the call signature and the wire format used to marshal/unmarshal every argument. This lets a client written in one language interoperate correctly with a server written in a completely different language and running on different hardware, and it removes the error-prone, repetitive burden of hand-writing marshalling code for every remote interface in every language used.
(e) Decreasing server-held reply data — the RRA protocol. With the plain request-reply (RR) protocol, a server cannot tell whether a client actually received its reply: if the client's own retransmission timer fires because a message was lost, the server sees a duplicate request. To answer that duplicate correctly without re-executing a non-idempotent operation, the server must keep a copy of every reply filed by request identifier for as long as a duplicate might plausibly still arrive — and since the client's retransmission timeout is its own local value the server cannot observe, this retention period is effectively unbounded under RR alone. The request-reply-acknowledge (RRA) protocol fixes this by having the client send an explicit acknowledgement as soon as it receives the reply. This directly answers the question: the length of time a server must retain an unacknowledged reply is bounded by, and ends exactly at, the arrival of that acknowledgement — the server discards the cached reply the moment the ack arrives, rather than guessing at a client-side timeout; if the ack itself is lost, the server instead learns the reply needs resending when it sees the original request retransmitted (a duplicate request is proof the client never got the reply). Servers do not have to continually resend the reply hoping for an acknowledgement: the exchange is reactive, not proactive — the server sends the reply once and waits, resending only if a duplicate of the original request arrives, never running its own speculative retry timer. This keeps server-side state small (one cached reply per outstanding, unacknowledged call) and pushes the cost of detecting loss onto the client, the party that already runs a retransmission timer.