Skip to the content.
Building Resilient RPC Servers for Remote Workflow Automation | AI Systems Design From Scratch

Connect with Amin Boulouma Official

๐Ÿ  Documentation Hub ๐Ÿ“ Engineering Blog ๐Ÿ’ป GitHub Repository

Building Resilient RPC Servers for Remote Workflow Automation

Amin Boulouma, Software Engineer

In distributed systems, Remote Procedure Call (RPC) servers act as the bridge for executing tasks on remote nodes. When dealing with sensitive workflows like Git operations, your server must be capable of handling concurrent connections, preventing zombie sessions, and validating incoming payloads defensively.

Core Principles for RPC Design

Implementation: The ThreadedGitRPCServer

The following code provides a template for an RPC server that routes Git-related tasks while maintaining thread-safety and session isolation.

import json
import logging
import threading
from socket import socket as Socket
from typing import Any, Dict, Tuple

from ai_systems_design.utils import SocketUtility

logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
logger = logging.getLogger(__name__)

class ThreadedGitRPCServer:
    """A multi-threaded RPC server for orchestrating remote Git operations."""
    
    def __init__(self, host: str, port: int) -> None:
        self.host = host
        self.port = port

    def start_server(self):
        """Delegates incoming connections to worker threads."""
        server_socket = SocketUtility.create_socket_server(self.host, self.port, "Git Server")
        try:
            while True: 
                client_sock, client_addr = server_socket.accept()
                # Spawn a worker to handle session lifecycle independently
                worker = threading.Thread(
                    target=self._connection_worker_lifecycle,
                    args=(client_sock, client_addr),
                    daemon=True
                )
                worker.start()
        finally:
            server_socket.close()

    def _connection_worker_lifecycle(self, client_sock: Socket, client_addr: Tuple[str, int]) -> None:
        """Manages transport boundaries and ensures socket closure."""
        client_sock.settimeout(15.0) 
        try:
            while True:
                raw_bytes = client_sock.recv(4096)
                if not raw_bytes: break
                
                response = self._route_rpc_request(raw_bytes.decode('utf-8'))
                client_sock.sendall(f"{json.dumps(response)}\n".encode('utf-8'))
        finally:
            client_sock.close()

    def _route_rpc_request(self, raw_payload: str) -> Dict[str, Any]:
        """Validates JSON structure and routes commands to business logic."""
        try:
            request_data = json.loads(raw_payload)
            # Validate required schema
            if request_data.get("type") != "git":
                return {"status": "error", "message": "Invalid type."}
            
            # Extract and execute action
            command = request_data["command"].split()
            if command[0] == "git" and command[1] == "clone":
                return {"status": "success", "message": f"Cloned {command[2]}"}
            
            return {"status": "error", "message": "Unrecognized command."}
        except Exception:
            return {"status": "error", "message": "Malformed request."}

Best Practices for RPC Resilience

  1. Framing: RPC over TCP requires a way to distinguish between different messages. Here, we send a newline (\n) as a delimiter. Ensure your clients follow the same framing logic.
  2. Schema Validation: Before executing any command, verify the request_data has the necessary keys. This prevents KeyError crashes in your logic layer.
  3. Logging: Always log the client_addr when connections are established or dismantled. This is invaluable when auditing system failures in a distributed architecture.

Connect with Amin Boulouma Official