import logging from contextlib import contextmanager from celery import shared_task from django.core.cache import cache from .event_listener import EventListener LOCK_KEY = '_django_ethereum_events_cache_lock' LOCK_VALUE = 'LOCK' logger = logging.getLogger(__name__) @contextmanager def cache_lock(lock_id, lock_value): """Cache based locking mechanism. Cache backends `memcached` and `redis` are recommended. """ # cache.add fails if the key already exists status = cache.add(lock_id, lock_value) try: yield status finally: cache.delete(lock_id) @shared_task def event_listener(): """ Celery task that transverses the blockchain looking for event logs. This task should be run periodically via celerybeat to monitor for new blocks in the blockchain. Examples: CELERYBEAT_SCHEDULE = { 'ethereum_events': { 'task': 'django_ethereum_events.tasks.event_listener', 'schedule': crontab(minute='*/2') # run every 2 minutes } } """ with cache_lock(LOCK_KEY, LOCK_VALUE) as acquired: if acquired: listener = EventListener() try: listener.execute() except Exception: logger.exception('Exception while running event listener task', exc_info=True) daemon = listener.daemon last_processed_block = daemon.block_number daemon.last_error_block_number = last_processed_block + 1 daemon.save() else: logger.info('Event listener is already running. Skipping execution.')