SIENTIAPDE-1255: Refactor worker to support only the train_model-queue and remove prediction workflows.

This commit is contained in:
Bruno Domingues
2025-10-16 18:15:52 -03:00
parent 8f1cc21bb1
commit 7abd951806
2 changed files with 26 additions and 59 deletions

View File

@@ -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,

View File

@@ -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