diff --git a/swh/objstorage/backends/azure.py b/swh/objstorage/backends/azure.py --- a/swh/objstorage/backends/azure.py +++ b/swh/objstorage/backends/azure.py @@ -14,7 +14,7 @@ ContainerSasPermissions, generate_container_sas, ) -from azure.core.exceptions import ResourceNotFoundError +from azure.core.exceptions import ResourceExistsError, ResourceNotFoundError from swh.objstorage.objstorage import ( ObjStorage, @@ -210,7 +210,14 @@ data += compressor.flush() client = self.get_blob_client(hex_obj_id) - client.upload_blob(data=data, length=len(data)) + try: + client.upload_blob(data=data, length=len(data)) + except ResourceExistsError: + # There's a race condition between check_presence and upload_blob, + # that we can't get rid of as the azure api doesn't allow atomic + # replaces or renaming a blob. As the restore operation explicitly + # removes the blob, it should be safe to just ignore the error. + pass return obj_id diff --git a/swh/objstorage/tests/objstorage_testing.py b/swh/objstorage/tests/objstorage_testing.py --- a/swh/objstorage/tests/objstorage_testing.py +++ b/swh/objstorage/tests/objstorage_testing.py @@ -36,6 +36,15 @@ self.assertEqual(obj_id, r) self.assertContentMatch(obj_id, content) + def test_add_twice(self): + content, obj_id = self.hash_content(b"add_twice") + r = self.storage.add(content, obj_id=obj_id) + self.assertEqual(obj_id, r) + self.assertContentMatch(obj_id, content) + r = self.storage.add(content, obj_id=obj_id, check_presence=False) + self.assertEqual(obj_id, r) + self.assertContentMatch(obj_id, content) + def test_add_big(self): content, obj_id = self.hash_content(b"add_big" * 1024 * 1024) r = self.storage.add(content, obj_id=obj_id) diff --git a/swh/objstorage/tests/test_objstorage_azure.py b/swh/objstorage/tests/test_objstorage_azure.py --- a/swh/objstorage/tests/test_objstorage_azure.py +++ b/swh/objstorage/tests/test_objstorage_azure.py @@ -9,7 +9,7 @@ from unittest.mock import patch from urllib.parse import urlparse, parse_qs -from azure.core.exceptions import ResourceNotFoundError +from azure.core.exceptions import ResourceExistsError, ResourceNotFoundError import pytest from swh.model.hashutil import hash_to_hex @@ -48,7 +48,7 @@ def upload_blob(self, data, length=None): if self.blob in self.container.blobs: - raise ValueError("Blob already exists") + raise ResourceExistsError("Blob already exists") if length is not None and length != len(data): raise ValueError("Wrong length for blob data!")