Dado el apio corriendo con estas opciones:
celery -A openwisp2 worker -l info --pool=gevent --concurrency=15 -OfairDada esta tarea de apio de openwisp-monitoring :
@shared_task def perform_check(uuid): """ Retrieves check according to the passed UUID and calls ``check.perform_check()`` """ try: check = get_check_model().objects.get(pk=uuid) except ObjectDoesNotExist: logger.warning(f'The check with uuid {uuid} has been deleted') return result = check.perform_check() if settings.DEBUG: # pragma: nocover print(json.dumps(result, indent=4, sort_keys=True))La mayoría de las veces la tarea funciona, pero algunas veces (generalmente con ráfagas), se genera la siguiente excepción:
SynchronousOnlyOperation: You cannot call this from an async context - use a thread or sync_to_async.Rastreo de pila completa:
SynchronousOnlyOperation: You cannot call this from an async context - use a thread or sync_to_async. File "celery/app/trace.py", line 412, in trace_task R = retval = fun(*args, **kwargs) File "celery/app/trace.py", line 704, in __protected_call__ return self.run(*args, **kwargs) File "openwisp_monitoring/check/tasks.py", line 44, in perform_check check = get_check_model().objects.get(pk=uuid) File "django/db/models/manager.py", line 85, in manager_method return getattr(self.get_queryset(), name)(*args, **kwargs) File "django/db/models/query.py", line 425, in get num = len(clone) File "django/db/models/query.py", line 269, in __len__ self._fetch_all() File "django/db/models/query.py", line 1308, in _fetch_all self._result_cache = list(self._iterable_class(self)) File "django/db/models/query.py", line 53, in __iter__ results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size) File "django/db/models/sql/compiler.py", line 1154, in execute_sql cursor = self.connection.cursor() File "django/utils/asyncio.py", line 24, in inner raise SynchronousOnlyOperation(message)No entiendo completamente por qué sucede esto.
Recapitulemos, corríjanme si me equivoco:
async_unsafe con el que se decoran las operaciones de la base de datos, este decorador comprueba si el bucle de eventos se está ejecutando y, en ese caso, genera una SynchronousOnlyOperationPero, ¿por qué esta excepción no se plantea en el 100% de los casos sino solo en una minoría de casos?
Esas tareas sí están funcionando, puedo verlo porque producen la recopilación de datos del gráfico que se muestra normalmente, o producen cambios de estado en el modelo del dispositivo (por ejemplo: ok a crítico).
¿Es un error en OpenWISP Monitoring, una mala configuración o un error en Django?
Parece que el bucle de eventos no se usa en Django, pero Django genera esta excepción a pesar de que no le concierne. Esto podría ser un error, pero me gustaría escuchar la opinión de expertos en el tema antes de presentar un informe de error.
Pensé que una posible solución rápida podría ser establecer la variable env DJANGO_ALLOW_ASYNC_UNSAFE pero solo en el proceso de apio.
Gracias por adelantado.
Resultó que mis suposiciones eran incorrectas y estaba usando un código que no era seguro con gevent.
Dado que reescribir el código para que sea seguro para greenlet por ahora no era una opción, cambié de nuevo a prefork.