TASK
Implementation
In a distributed system, nodes often need to call remote procedures on other nodes and wait for the result. This is called synchronous RPC (Remote Procedure Call).
Your task is to extend your Maelstrom node with a sync_rpc method that:
- Sends a message to another node
- Blocks until a response is received (matched by
in_reply_to) - Returns the response body
- Times out after a configurable duration (default: 1 second)
The node should still handle init and echo messages. Additionally, implement a proxy message type: when your node receives a proxy request, it forwards the inner message to the target node using sync_rpc, waits for the reply, and returns it to the original caller.
Request: {"type": "proxy", "msg_id": 1, "target": "n2", "inner": {"type": "echo", "echo": "hello"}}
Response: {"type": "proxy_ok", "in_reply_to": 1, "result": {"type": "echo_ok", "echo": "hello", ...}}For this task, simulate the remote node's response inline (since Maelstrom test harness sends single-node input). Your sync_rpc should store callbacks and resolve them when matching replies arrive.
Sample Test Cases
Init and echo still workTimeout: 5000ms
Input
{"src":"c0","dest":"n1","body":{"type":"init","msg_id":1,"node_id":"n1","node_ids":["n1","n2"]}}
{"src":"c1","dest":"n1","body":{"type":"echo","msg_id":2,"echo":"hello"}}
Expected Output
{"src": "n1", "dest": "c0", "body": {"type": "init_ok", "in_reply_to": 1, "msg_id": 0}}
{"src": "n1", "dest": "c1", "body": {"type": "echo_ok", "echo": "hello", "in_reply_to": 2, "msg_id": 1}}
Proxy sends RPC to targetTimeout: 5000ms
Input
{"src":"c0","dest":"n1","body":{"type":"init","msg_id":1,"node_id":"n1","node_ids":["n1","n2"]}}
{"src":"c1","dest":"n1","body":{"type":"proxy","msg_id":2,"target":"n2","inner":{"type":"echo","echo":"test"}}}
Expected Output
{"src": "n1", "dest": "c0", "body": {"type": "init_ok", "in_reply_to": 1, "msg_id": 0}}
{"src": "n1", "dest": "n2", "body": {"type": "echo", "echo": "test", "msg_id": 1}}
Hints
Hint 1▾
Use a dictionary to store pending requests keyed by msg_id
Hint 2▾
Block the caller using threading.Event or a simple polling loop
Hint 3▾
Set a timeout so the caller does not block forever
Hint 4▾
When a response arrives, match it via in_reply_to and unblock the caller
Hint 5▾
Return None or raise an exception if the timeout expires
OVERVIEW
Theoretical Hub
Concept overview coming soon
Key Concepts
RPCsynchronous communicationtimeoutblocking calls
main.py
python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#!/usr/bin/env python3
import sys
import json
import threading
class Node:
def __init__(self):
self.node_id = None
self.node_ids = []
self.next_msg_id = 0
self.lock = threading.Lock()
self.pending = {} # msg_id -> threading.Event
def send(self, dest, body):
with self.lock:
body["msg_id"] = self.next_msg_id
msg_id = self.next_msg_id
self.next_msg_id += 1
message = {"src": self.node_id, "dest": dest, "body": body}
print(json.dumps(message), flush=True)
return msg_id
def reply(self, request, body):
body["in_reply_to"] = request["body"]["msg_id"]
self.send(request["src"], body)
def sync_rpc(self, dest, body, timeout=1.0):
# TODO: Send a message and block until a reply arrives
# 1. Send the message and record its msg_id
# 2. Create a threading.Event and store it in self.pending
# 3. Wait for the event with the given timeout
# 4. Return the reply body or None on timeout
pass
def handle_reply(self, body):
# TODO: Check if this is a reply to a pending RPC
# Match using in_reply_to field
pass
def main():