Task run Once¶
Scenario¶
On an e-commerce website, a task runs periodically to synchronize its database with suppliers inventory.
To ensure the inventory is up to date, the task runs every 5 minutes.
Several issues may arise that could lead to execution overlap and concurrent updates:
Suppliers platform slowdowns or technical issues.
Inventory size growth: A significant number of products may have been added over time.
Resource limitations: Increased traffic can lead to performance degradation and execution slowdown.
Once execution overlap occurs, other issues may emerge:
Queue size growth (if a single worker executes these tasks).
Performance degradation: Inventory updates can be resource-intensive. Multiple instances running simultaneously may degrade performance.
Data integrity: Multiple workers updating the same inventory with potentially outdated data.
Database locking: The codebase may contain locking mechanisms that could be triggered by the updating inventory task, resulting in a blocked database.
Solution¶
To avoid these issues, we could introduce a simple “locking mechanism” for this task to ensure that only a single instance runs at any given time.
from functools import wraps
from django.core.cache import cache
def run_once(task_name, timeout):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            # Check if the task is already in the cache
            if cache.get(task_name):
                print(f"Task '{task_name}' is already running. Exiting.")
                return
            # Add the task to the cache with a timeout
            cache.set(task_name, 'locked', timeout)
            try:
                # Execute the function
                result = func(*args, **kwargs)
            finally:
                # Remove the task from the cache
                cache.delete(task_name)
            return result
        return wrapper
    return decorator
@run_once(task_name='example_task', timeout=60*5)
def example_function():
    print("Long execution .... ")
This simple decorator ensures that only a single task is executed at a time. I would suggest to implement a logging or monitoring mechanism to analyze the execution time of such tasks to prevent overlap and performance issues.