SIENTIAPDE-1255: Refactor worker to support only the train_model-queue and remove prediction workflows.
This commit is contained in:
@@ -1,20 +1,17 @@
|
|||||||
"""
|
"""Model Manager Worker Module
|
||||||
Model Manager Worker Module
|
|
||||||
|
|
||||||
This module provides the main worker implementation for the Sientia DataOps Model Manager system.
|
This module provides the main worker implementation for the Sientia DataOps Model Manager system.
|
||||||
It orchestrates Temporal workers, manages task queues, and handles the lifecycle of
|
It orchestrates Temporal workers, manages task queues, and handles the lifecycle of
|
||||||
prediction and retraining workflows.
|
model training workflows.
|
||||||
|
|
||||||
The worker supports two main task queues:
|
The worker supports the train_model-queue task queue for ML model training workflows.
|
||||||
- predictions_batch-queue: Handles batch prediction workflows
|
|
||||||
- minimal_retrain-queue: Handles model retraining workflows
|
|
||||||
|
|
||||||
Key Features:
|
Key Features:
|
||||||
- Automatic scaling with PollerBehaviorAutoscaling
|
- Automatic scaling with PollerBehaviorAutoscaling
|
||||||
- Prometheus metrics integration
|
- Prometheus metrics integration
|
||||||
- Comprehensive error handling and logging
|
- Comprehensive error handling and logging
|
||||||
- Graceful shutdown with cleanup
|
- Graceful shutdown with cleanup
|
||||||
- Multiple worker instances for different workflow types
|
- ML model training pipeline orchestration
|
||||||
|
|
||||||
Environment Variables:
|
Environment Variables:
|
||||||
- TEMPORAL_HOST: Temporal server address (default: localhost:7233)
|
- TEMPORAL_HOST: Temporal server address (default: localhost:7233)
|
||||||
@@ -46,12 +43,7 @@ with workflow.unsafe.imports_passed_through():
|
|||||||
build_mongodb_config,
|
build_mongodb_config,
|
||||||
build_postgres_config,
|
build_postgres_config,
|
||||||
)
|
)
|
||||||
from model_manager.workflows.minimal_retrain import MinimalRetrain
|
from model_manager.workflows.train_model import TrainModel
|
||||||
from model_manager.workflows.predictions_batch import PredictionsBatch
|
|
||||||
from model_manager.workflows.sub_workflows.format_and_export_prediction import (
|
|
||||||
FormatAndExportPrediction,
|
|
||||||
)
|
|
||||||
from model_manager.workflows.sub_workflows.prediction_process import PredictionProcess
|
|
||||||
|
|
||||||
POD_ID = os.getenv('POD_ID')
|
POD_ID = os.getenv('POD_ID')
|
||||||
SDK_METRICS_PORT = int(os.getenv('HTTP_SDK_METRICS_PORT', '9091'))
|
SDK_METRICS_PORT = int(os.getenv('HTTP_SDK_METRICS_PORT', '9091'))
|
||||||
@@ -133,41 +125,21 @@ async def main():
|
|||||||
workers = [
|
workers = [
|
||||||
Worker(
|
Worker(
|
||||||
temporal_client,
|
temporal_client,
|
||||||
task_queue='minimal_retrain-queue',
|
task_queue='train_model-queue',
|
||||||
workflows=[MinimalRetrain],
|
workflows=[TrainModel],
|
||||||
activities=[
|
|
||||||
activities.load_custom_query,
|
|
||||||
activities.retrain_model,
|
|
||||||
activities.update_production_model,
|
|
||||||
activities.export_data_to_postgres,
|
|
||||||
],
|
|
||||||
max_concurrent_workflow_tasks=50,
|
|
||||||
max_concurrent_activities=50,
|
|
||||||
max_concurrent_local_activities=50,
|
|
||||||
max_cached_workflows=200,
|
|
||||||
workflow_task_poller_behavior=PollerBehaviorAutoscaling(),
|
|
||||||
activity_task_poller_behavior=PollerBehaviorAutoscaling(),
|
|
||||||
),
|
|
||||||
Worker(
|
|
||||||
temporal_client,
|
|
||||||
task_queue='predictions_batch-queue',
|
|
||||||
workflows=[PredictionsBatch, PredictionProcess, FormatAndExportPrediction],
|
|
||||||
activities=[
|
activities=[
|
||||||
|
# Training & Validation
|
||||||
|
activities.validate_train_params,
|
||||||
|
activities.train_model,
|
||||||
# MLFlow
|
# MLFlow
|
||||||
activities.request_predict,
|
activities.save_model,
|
||||||
activities.request_transform,
|
# MinIO
|
||||||
# Gates
|
activities.fetch_file_from_minio,
|
||||||
activities.input_gate,
|
activities.delete_file_from_minio,
|
||||||
activities.mlflow_response_gate,
|
# Filesystem
|
||||||
activities.mlflow_content_gate,
|
activities.cleanup_run_directory,
|
||||||
activities.format_prediction,
|
# Database
|
||||||
activities.format_default_prediction,
|
activities.update_experiment_run,
|
||||||
activities.get_last_timestamp,
|
|
||||||
# Postgres
|
|
||||||
activities.load_custom_query,
|
|
||||||
activities.repeat_last_prediction,
|
|
||||||
activities.export_data_to_postgres,
|
|
||||||
activities.write_metrics,
|
|
||||||
],
|
],
|
||||||
max_concurrent_workflow_tasks=50,
|
max_concurrent_workflow_tasks=50,
|
||||||
max_concurrent_activities=50,
|
max_concurrent_activities=50,
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ async def test_main_success(
|
|||||||
namespace='test-namespace',
|
namespace='test-namespace',
|
||||||
runtime=ANY,
|
runtime=ANY,
|
||||||
)
|
)
|
||||||
assert mock_worker.call_count == 2 # Two workers created
|
assert mock_worker.call_count == 1 # Only one worker created
|
||||||
mock_gather.assert_called_once()
|
mock_gather.assert_called_once()
|
||||||
mock_sys_exit.assert_called_once_with(1)
|
mock_sys_exit.assert_called_once_with(1)
|
||||||
|
|
||||||
@@ -201,7 +201,7 @@ async def test_main_exception_handling(
|
|||||||
@patch('model_manager.worker.worker.NotificationHandler')
|
@patch('model_manager.worker.worker.NotificationHandler')
|
||||||
@patch('model_manager.worker.worker.start_prometheus_server')
|
@patch('model_manager.worker.worker.start_prometheus_server')
|
||||||
@patch('model_manager.worker.worker.get_logger')
|
@patch('model_manager.worker.worker.get_logger')
|
||||||
async def test_main_creates_two_workers(
|
async def test_main_creates_only_one_worker(
|
||||||
mock_get_logger,
|
mock_get_logger,
|
||||||
mock_start_prometheus,
|
mock_start_prometheus,
|
||||||
mock_notification_handler,
|
mock_notification_handler,
|
||||||
@@ -213,7 +213,7 @@ async def test_main_creates_two_workers(
|
|||||||
mock_sys_exit,
|
mock_sys_exit,
|
||||||
mock_env_vars,
|
mock_env_vars,
|
||||||
):
|
):
|
||||||
"""Test that main creates two workers with correct configurations."""
|
"""Test that main creates only one worker with correct configurations."""
|
||||||
# Arrange
|
# Arrange
|
||||||
mock_logger = MagicMock()
|
mock_logger = MagicMock()
|
||||||
mock_get_logger.return_value = mock_logger
|
mock_get_logger.return_value = mock_logger
|
||||||
@@ -240,18 +240,13 @@ async def test_main_creates_two_workers(
|
|||||||
# Act
|
# Act
|
||||||
await main()
|
await main()
|
||||||
|
|
||||||
# Assert - Verify two workers were created
|
# Assert - Verify only one worker was created
|
||||||
assert mock_worker.call_count == 2
|
assert mock_worker.call_count == 1
|
||||||
|
|
||||||
# Verify first worker (minimal_retrain-queue)
|
# Verify worker (train_model-queue)
|
||||||
first_call = mock_worker.call_args_list[0]
|
first_call = mock_worker.call_args_list[0]
|
||||||
assert first_call[1]['task_queue'] == 'minimal_retrain-queue'
|
assert first_call[1]['task_queue'] == 'train_model-queue'
|
||||||
assert 'MinimalRetrain' in str(first_call[1]['workflows'])
|
assert 'TrainModel' in str(first_call[1]['workflows'])
|
||||||
|
|
||||||
# Verify second worker (predictions_batch-queue)
|
|
||||||
second_call = mock_worker.call_args_list[1]
|
|
||||||
assert second_call[1]['task_queue'] == 'predictions_batch-queue'
|
|
||||||
assert 'PredictionsBatch' in str(second_call[1]['workflows'])
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
|
|||||||
Reference in New Issue
Block a user