Prevent error when duplicate mapping is created

The mapping has unique constraint and creating a mapping with the same
local entity will fail. Creation of mapping with the same local entity
might happen because of race condition, that would happen if 2 processes
start mapping of the same local entity at the same time.

Return the existing public_id if creation failed due to duplicate.

Change-Id: Ic6e6ec9614e16a49c791dc6c06a4464d18be09be
Related-Bug: 1582585
This commit is contained in:
Boris Bobrov 2016-07-15 21:29:47 +03:00
parent 5f7377f5ab
commit 29624d47e3
2 changed files with 29 additions and 6 deletions

View File

@ -65,12 +65,17 @@ class Mapping(base.MappingDriverV8):
def create_id_mapping(self, local_entity, public_id=None):
entity = local_entity.copy()
with sql.session_for_write() as session:
if public_id is None:
public_id = self.id_generator_api.generate_public_ID(entity)
entity['public_id'] = public_id
mapping_ref = IDMapping.from_dict(entity)
session.add(mapping_ref)
try:
with sql.session_for_write() as session:
if public_id is None:
public_id = self.id_generator_api.generate_public_ID(
entity)
entity['public_id'] = public_id
mapping_ref = IDMapping.from_dict(entity)
session.add(mapping_ref)
except sql.DBDuplicateEntry:
# something else created the mapping already. We can use it.
public_id = self.get_public_id(local_entity)
return public_id
def delete_id_mapping(self, public_id):

View File

@ -196,3 +196,21 @@ class SqlIDMapping(test_backend_sql.SqlTests):
self.id_mapping_api.purge_mappings({})
self.assertThat(mapping_sql.list_id_mappings(),
matchers.HasLength(initial_mappings))
def test_create_duplicate_mapping(self):
local_entity = {
'domain_id': self.domainA['id'],
'local_id': uuid.uuid4().hex,
'entity_type': mapping.EntityType.USER}
public_id1 = self.id_mapping_api.create_id_mapping(local_entity)
# second call should be successful and return the same
# public_id as above
public_id2 = self.id_mapping_api.create_id_mapping(local_entity)
self.assertEqual(public_id1, public_id2)
# even if public_id was specified, it should not be used,
# and still the same public_id should be returned
public_id3 = self.id_mapping_api.create_id_mapping(
local_entity, public_id=uuid.uuid4().hex)
self.assertEqual(public_id1, public_id3)