SIENTIAPDE-1314

Enhance thread safety in MLFlowRepository model caching

- Introduced a reentrant lock to synchronize access to the model cache, ensuring thread safety during cache checks and updates.
- Updated the cache management logic to acquire the lock when checking for existing models and when updating the cache after downloading a new model.
- Reduced the maximum cached workflows in the worker configuration for improved resource management.
This commit is contained in:
vitor-aignosi
2025-10-27 13:29:01 -03:00
parent 1cbf356d0c
commit 67af03ee94
2 changed files with 21 additions and 17 deletions

View File

@@ -16,6 +16,7 @@ Capabilities:
import ctypes import ctypes
import gc import gc
import threading
import traceback import traceback
from datetime import datetime, timedelta from datetime import datetime, timedelta
from os import environ, makedirs, path from os import environ, makedirs, path
@@ -74,6 +75,7 @@ class MLFlowRepository:
self.client = mlflow.tracking.MlflowClient() self.client = mlflow.tracking.MlflowClient()
self.model_cache: dict[str, Any] = {} self.model_cache: dict[str, Any] = {}
self._cache_lock = threading.RLock()
self.logger = logger self.logger = logger
""" """
@@ -466,28 +468,31 @@ class MLFlowRepository:
model_key = f'{model_name}_{model_type}' model_key = f'{model_name}_{model_type}'
if model_key in self.model_cache: # Acquire lock to check cache
cache = self.model_cache[model_key] with self._cache_lock:
if model_key in self.model_cache:
cache = self.model_cache[model_key]
# Check if config has changed or is outdated # Check if config has changed or is outdated
if self.check_cache_retention(cache, retention): if self.check_cache_retention(cache, retention):
return self.handle_valid_model(model_name=model_name, cache=cache) return self.handle_valid_model(model_name=model_name, cache=cache)
else:
# Model is outdated, delete old model files
self.handle_outdated_model(model_name=model_name, model_key=model_key)
else: else:
# Model is outdated, delete old model files self.logger.debug(
self.handle_outdated_model(model_name=model_name, model_key=model_key) f'Model {model_name} is not in {model_type} cache, downloading a new one'
else: )
self.logger.debug(
f'Model {model_name} is not in {model_type} cache, downloading a new one'
)
# Donwload new model # Donwload new model (without lock to avoid blocking other threads)
model, _artifact_path = self.download_model( model, _artifact_path = self.download_model(
model_name=model_name, model_type=model_type, flavor=flavor, load_wrapper=False model_name=model_name, model_type=model_type, flavor=flavor, load_wrapper=False
) )
cache = {'target': model, 'timestamp': datetime.now()} # Update cache with lock
with self._cache_lock:
self.model_cache[model_key] = cache cache = {'target': model, 'timestamp': datetime.now()}
self.model_cache[model_key] = cache
return model return model

View File

@@ -152,7 +152,7 @@ async def main():
max_concurrent_workflow_tasks=50, max_concurrent_workflow_tasks=50,
max_concurrent_activities=50, max_concurrent_activities=50,
max_concurrent_local_activities=50, max_concurrent_local_activities=50,
max_cached_workflows=200, max_cached_workflows=2,
workflow_task_poller_behavior=PollerBehaviorAutoscaling(), workflow_task_poller_behavior=PollerBehaviorAutoscaling(),
activity_task_poller_behavior=PollerBehaviorAutoscaling(), activity_task_poller_behavior=PollerBehaviorAutoscaling(),
), ),
@@ -164,7 +164,6 @@ async def main():
# MLFlow # MLFlow
activities.request_predict, activities.request_predict,
activities.request_transform, activities.request_transform,
activities.query_to_minio,
# Gates # Gates
activities.input_gate, activities.input_gate,
activities.mlflow_response_gate, activities.mlflow_response_gate,