from functools import wraps
from django.core.cache import cache
def execute_once_in(seconds):
"""
This decorator wraps a normal function
so that it can be executed only once in the next few seconds.
Useful to make Celery email sending tasks idempotent and safeguard
against SQS messages delivered twice (in rare cases).
Usage:
@task
@execute_once_in(3600)
def myfunction():
pass
If called multiple times, the above method will be executed only one
time during the next hour following its first execution.
"""
def decorator(func):
def inner_decorator(*args, **kwargs):
key = "%s.%s" % (func.__module__, func.__name__)
key = key.replace(' ','_') # memcache doesn't like spaces
# NB: there's no way to tell if
# func() didn't execute or returned nothing
if cache.get(key):
return
cache.set(key, True, seconds)
return func(*args, **kwargs)
return wraps(func)(inner_decorator)
return decorator