[bug] IntegrityError in notifications celery task when staff user is created
nemesifier opened this issue · 6 comments
I can replicate this in an instance installed via ansible-openwisp2.
I suspect it can be replicated also in the development environment with the celery worker running.
How to replicate:
- create a staff user, of the group administrator, add a relation to an org and flag (is_admin=True)
Expected outcome:
- notification settings are created
Actual outcome:
This error is logged:
IntegrityError: insert or update on table "openwisp_notifications_notificationsetting" violates foreign key constraint "openwisp_notificatio_user_id_3cc7bf95_fk_openwisp_"
DETAIL: Key (user_id)=(13201df6-2bb2-4017-a95b-30a4e88a21a7) is not present in table "openwisp_users_user".
This error is really typical. It happens when the background task is passed to the worker before the object it must operate on is committed to the DB.
The solution is to use transaction.on_commit to call the celery task, like we do in other modules, eg:
- https://github.com/openwisp/openwisp-firmware-upgrader/blob/94288de5c970ab7fcd11a178d54bf7a45fcc98cc/openwisp_firmware_upgrader/base/models.py#L127-L129
- https://github.com/openwisp/openwisp-firmware-upgrader/blob/94288de5c970ab7fcd11a178d54bf7a45fcc98cc/openwisp_firmware_upgrader/base/models.py#L307
@nemesisdesign cannot seem to reproduce this.
@R9295 are you running the celery worker?
PS: the bug shows up in the celery worker, not in the web app.
@nemesisdesign yup.
[2021-01-19 12:06:50,558: DEBUG/MainProcess] TaskPool: Apply <function _fast_trace_task at 0x7fa819f488c8> (args:('openwisp_notifications.tasks.ns_user_created', 'b61bd3fb-8f95-442f-80fc-f5490d7b1889', {'lang': 'py', 'task': 'openwisp_notifications.tasks.ns_user_created', 'id': 'b61bd3fb-8f95-442f-80fc-f5490d7b1889', 'shadow': None, 'eta': None, 'expires': None, 'group': None, 'group_index': None, 'retries': 0, 'timelimit': [None, None], 'root_id': 'b61bd3fb-8f95-442f-80fc-f5490d7b1889', 'parent_id': None, 'argsrepr': "(UUID('488170ca-9039-4cbc-b066-9822dc840ba0'), True, True)", 'kwargsrepr': '{}', 'origin': 'gen28160@r9pc', 'reply_to': '86efbac7-e386-3084-b6d7-d8bc4396b9df', 'correlation_id': 'b61bd3fb-8f95-442f-80fc-f5490d7b1889', 'hostname': 'celery@r9pc', 'delivery_info': {'exchange': '', 'routing_key': 'celery', 'priority': 0, 'redelivered': None}, 'args': ['488170ca-9039-4cbc-b066-9822dc840ba0', True, True], 'kwargs': {}}, b'[["488170ca-9039-4cbc-b066-9822dc840ba0", true, true], {}, {"callbacks": null, "errbacks": null, "chain": null, "chord": null}]', 'application/json', 'utf-8') kwargs:{})
[2021-01-19 12:06:50,559: DEBUG/MainProcess] Task accepted: openwisp_notifications.tasks.ns_user_created[b61bd3fb-8f95-442f-80fc-f5490d7b1889] pid:28117
ns_user_created
waits for the post_save
message too
@receiver(post_save, sender=User, dispatch_uid='user_notification_setting')
def notification_setting_user_created(instance, created, **kwargs):
tasks.ns_user_created.delay(instance.pk, instance.is_superuser, created)
@nemesisdesign yup.
[2021-01-19 12:06:50,558: DEBUG/MainProcess] TaskPool: Apply <function _fast_trace_task at 0x7fa819f488c8> (args:('openwisp_notifications.tasks.ns_user_created', 'b61bd3fb-8f95-442f-80fc-f5490d7b1889', {'lang': 'py', 'task': 'openwisp_notifications.tasks.ns_user_created', 'id': 'b61bd3fb-8f95-442f-80fc-f5490d7b1889', 'shadow': None, 'eta': None, 'expires': None, 'group': None, 'group_index': None, 'retries': 0, 'timelimit': [None, None], 'root_id': 'b61bd3fb-8f95-442f-80fc-f5490d7b1889', 'parent_id': None, 'argsrepr': "(UUID('488170ca-9039-4cbc-b066-9822dc840ba0'), True, True)", 'kwargsrepr': '{}', 'origin': 'gen28160@r9pc', 'reply_to': '86efbac7-e386-3084-b6d7-d8bc4396b9df', 'correlation_id': 'b61bd3fb-8f95-442f-80fc-f5490d7b1889', 'hostname': 'celery@r9pc', 'delivery_info': {'exchange': '', 'routing_key': 'celery', 'priority': 0, 'redelivered': None}, 'args': ['488170ca-9039-4cbc-b066-9822dc840ba0', True, True], 'kwargs': {}}, b'[["488170ca-9039-4cbc-b066-9822dc840ba0", true, true], {}, {"callbacks": null, "errbacks": null, "chain": null, "chord": null}]', 'application/json', 'utf-8') kwargs:{}) [2021-01-19 12:06:50,559: DEBUG/MainProcess] Task accepted: openwisp_notifications.tasks.ns_user_created[b61bd3fb-8f95-442f-80fc-f5490d7b1889] pid:28117
ns_user_created
waits for thepost_save
message too@receiver(post_save, sender=User, dispatch_uid='user_notification_setting') def notification_setting_user_created(instance, created, **kwargs): tasks.ns_user_created.delay(instance.pk, instance.is_superuser, created)
I confirm that in the test environment the bug doesn't show up, but does show up in every system deployed with ansible-openwisp2 and postgresql. It probably shows up with a DB different than SQlite, although I'm not exactly sure.
I would try the suggested fix, I can test it for you.
Related PR: #167
Not ready yet.
While replicating I am getting this error:
[2021-10-05 16:40:16,621: ERROR/ForkPoolWorker-6] Task openwisp_notifications.tasks.update_superuser_notification_settings[bf8ed8e2-2f55-4ba1-a5fd-cc16737d7b1f] raised unexpected: DoesNotExist('User matching query does not exist.')
Traceback (most recent call last):
File "/home/sankalp/Desktop/openwisp/not-env/lib/python3.7/site-packages/celery/app/trace.py", line 412, in trace_task
R = retval = fun(*args, **kwargs)
File "/home/sankalp/Desktop/openwisp/not-env/lib/python3.7/site-packages/celery/app/trace.py", line 704, in __protected_call__
return self.run(*args, **kwargs)
File "/home/sankalp/Desktop/openwisp/openwisp-notifications/openwisp_notifications/tasks.py", line 90, in update_superuser_notification_settings
user = User.objects.get(pk=instance_id)
File "/home/sankalp/Desktop/openwisp/not-env/lib/python3.7/site-packages/django/db/models/manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/home/sankalp/Desktop/openwisp/not-env/lib/python3.7/site-packages/django/db/models/query.py", line 431, in get
self.model._meta.object_name
openwisp_users.models.User.DoesNotExist: User matching query does not exist.