Implementing RPC client and server in RabbitMQ - Time & Space Complexity
When using RabbitMQ for RPC, we want to know how the time to process requests grows as more requests come in.
We ask: How does the system handle more calls and how does that affect response time?
Analyze the time complexity of the following RabbitMQ RPC server code snippet.
channel.queue_declare(queue='rpc_queue')
def on_request(ch, method, props, body):
n = int(body)
response = fib(n) # Calculate Fibonacci number
ch.basic_publish(exchange='', routing_key=props.reply_to,
properties=pika.BasicProperties(correlation_id=props.correlation_id),
body=str(response))
ch.basic_ack(delivery_tag=method.delivery_tag)
channel.basic_consume(queue='rpc_queue', on_message_callback=on_request, auto_ack=False)
channel.start_consuming()
This code listens for RPC requests, computes a Fibonacci number, and sends back the result.
Look at what repeats when a request arrives.
- Primary operation: The Fibonacci calculation inside
on_request. - How many times: Once per RPC request received.
The time to respond depends on the Fibonacci number requested.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 steps |
| 20 | About 20 steps |
| 30 | About 30 steps |
Pattern observation: The time grows roughly in a straight line as n increases, because each request does a calculation proportional to n.
Time Complexity: O(n)
This means the time to handle each request grows linearly with the size of the input number n.
[X] Wrong: "The RPC server handles all requests instantly no matter the input size."
[OK] Correct: Each request runs a calculation that takes longer for bigger inputs, so response time grows with input size.
Understanding how request processing time grows helps you design better services and explain system behavior clearly in interviews.
What if we changed the Fibonacci calculation to a constant-time lookup? How would the time complexity change?
Practice
Solution
Step 1: Understand RPC server role
The RPC server listens for requests on a queue and processes them.Step 2: Identify server response behavior
After processing, the server sends the result back to the client via a reply queue.Final Answer:
To receive requests, process them, and send back responses -> Option CQuick Check:
RPC server handles requests and replies [OK]
- Confusing client and server roles
- Thinking server only stores messages
- Assuming server sends requests
Solution
Step 1: Identify callback queue declaration
For RPC replies, a temporary exclusive queue is declared with empty name and exclusive=True.Step 2: Match syntax with pika usage
Using queue_declare with queue='' and exclusive=True creates a unique callback queue.Final Answer:
result = channel.queue_declare(queue='', exclusive=True) -> Option BQuick Check:
Exclusive empty queue for callback = result = channel.queue_declare(queue='', exclusive=True) [OK]
- Declaring fixed queue name for callback
- Using basic_publish instead of queue_declare
- Not setting exclusive=True for callback queue
corr_id = str(uuid.uuid4())
channel.basic_publish(
exchange='',
routing_key='rpc_queue',
properties=pika.BasicProperties(
reply_to=callback_queue,
correlation_id=corr_id
),
body='5'
)
print('Sent request with correlation_id:', corr_id)Solution
Step 1: Understand correlation_id assignment
corr_id is assigned a new UUID string using uuid.uuid4(), which is random.Step 2: Analyze print statement output
The print outputs the fixed string plus the generated corr_id value.Final Answer:
Sent request with correlation_id: <random UUID> -> Option AQuick Check:
corr_id is UUID string printed [OK]
- Confusing correlation_id with routing_key
- Printing message body instead of corr_id
- Assuming correlation_id is undefined
def on_request(ch, method, props, body):
n = int(body)
response = fib(n)
ch.basic_publish(
exchange='',
routing_key=props.reply_to,
properties=pika.BasicProperties(correlation_id=props.correlation_id),
body=str(response)
)
ch.basic_ack(delivery_tag=method.delivery_tag)
channel.basic_consume(queue='rpc_queue', on_message_callback=on_request)Solution
Step 1: Check message consumption setup
basic_consume is set but start_consuming() is not called to begin processing.Step 2: Verify other parts
Correlation_id usage and ack are correct; queue name matches.Final Answer:
Missing call to start consuming messages -> Option AQuick Check:
start_consuming() needed to process messages [OK]
- Forgetting start_consuming() call
- Misusing correlation_id property
- Not acknowledging messages
Solution
Step 1: Understand concurrency in RPC client
Multiple requests need unique IDs to identify replies correctly.Step 2: Evaluate options for reply matching
Using unique correlation_id per request allows client to match replies properly.Final Answer:
Use a unique correlation_id for each request and check it on reply -> Option DQuick Check:
Unique correlation_id matches replies to requests [OK]
- Using same correlation_id for all requests
- Not using correlation_id at all
- Creating many reply queues unnecessarily
Ready to prove it?
Pass the test and this pattern moves unknown
