In this case it was caused by a collision in the DB between different workers, so I don't have any control over the order of the operations. Reading that doc you sent tho, I was struck by "Normally, you must write your applications so that they are always prepared to re-issue a transaction if it gets rolled back because of a deadlock." which is obvious, but I didn't think about. I think I'm reaching for a more complicated solution where a simpler one is better. I'm just going to wrap my bulk_create() in a try/except and monitor to see how often it comes up.
def bulk_set_file_permissions(paths: list, uwnetid: str = None):
"""Set the permission for a UWNetID for a list of paths"""
if not uwnetid:
request = get_request_object()
if hasattr(request, "uw_net_id"):
uwnetid = get_request_object().uw_net_id
elif hasattr(request, "net_id"):
uwnetid = get_request_object().net_id
create_list = [UWNetIDAuth(uwnetid=uwnetid, path=path) for path in paths]
try:
UWNetIDAuth.objects.bulk_create(
create_list,
ignore_conflicts=True,
)
except OperationalError as e:
log.warning(f"Error creating file permissions: {e}")
bulk_set_file_permissions(paths=paths, uwnetid=uwnetid)