Worker Architecture and Dependencies
This document explains the relationships and dependencies between the Rhesis worker system, the backend API, and the SDK components.
Component Relationships
The Rhesis platform consists of several interrelated components:
Backend-Worker Interdependencies
How Tasks Flow Through the System
- Backend API: Endpoints receive client requests and enqueue asynchronous tasks
- Broker: Redis-based queue stores pending tasks (with TLS support)
- Worker: Processes tasks from the queue and executes business logic
- Database: Shared between backend and worker for storing and retrieving application data
- SDK: Provides shared utilities and models used by both components
Code Dependencies
The worker depends on the backend code in several ways:
- Shared Models: The worker needs access to the same data models defined in the backend
- Database Access: Worker tasks use the same database connection/ORM layer as the backend
- Business Logic: Tasks often execute backend business logic in an asynchronous context
- Context Management: The worker needs to maintain the same multi-tenant context system
Example import hierarchy:
SDK Dependencies
Both the worker and backend depend on the Rhesis SDK for:
- Client Libraries: API clients for external services (e.g., LLM providers)
- Shared Utilities: Common functions used by both backend and worker
- Type Definitions: Shared type definitions and interfaces
- Configuration Management: Loading and accessing configuration
Deployment Considerations
Package Structure
When deploying the worker, it must include:
- The entire
rhesis.backendpackage - The
rhesis.sdkpackage - Worker-specific code (
rhesis.backend.tasksandrhesis.backend.worker)
Environment Configuration
The worker requires the same environment variables as the backend, plus additional worker-specific settings:
Development Workflow
When developing tasks, you need to:
- Write code in the backend repository
- Ensure both backend and worker containers have access to the latest code
- Test tasks using both API-triggered execution and direct worker execution
Managing Circular Dependencies
One challenge in the worker-backend relationship is avoiding circular dependencies. The system follows these patterns:
- Worker tasks can import backend modules
- Backend modules should not directly import worker tasks (use dynamic imports if needed)
- Shared dependencies go in the SDK package
- Base task classes, task organization, and worker configuration belong in
rhesis.backend.tasks
Task Context and State
Because the worker executes backend code asynchronously:
- The tenant context (organization/user IDs) must be explicitly passed to tasks
- Database sessions must be properly managed (opened and closed)
- Any state or context that would normally be available in an API request must be reconstructed
This is handled through: