[autotest] Delete shard user if it differs from master.
For most models the shard starts off with an empty database
and copies over records from the master. There is one exception,
afe_users, this is because users are proactively created when
someone navigates to the frontend. So even though we start
off with a clean afe_users table the `debug_user` will be
inserted the first time anyone visits the shard frontend. The id
of this record can be different from the id on the master.
To avoid this race, before saving any user record for the first
time check if a matching user already exists (matching id or login)
and simply delete that user before saving the new one.
TEST=Ran suites on shard.
BUG=chromium:423225
DEPLOY=apache
Change-Id: Ibe1d452bbba037c88e6525c749f27a2f1f5ca2c3
Reviewed-on: https://chromium-review.googlesource.com/235533
Tested-by: Prashanth B <beeps@chromium.org>
Reviewed-by: Dan Shi <dshi@chromium.org>
Commit-Queue: Prashanth B <beeps@chromium.org>
Trybot-Ready: Prashanth B <beeps@chromium.org>
diff --git a/frontend/afe/model_logic.py b/frontend/afe/model_logic.py
index d89de86..5781646 100644
--- a/frontend/afe/model_logic.py
+++ b/frontend/afe/model_logic.py
@@ -1014,12 +1014,29 @@
if pair[0] in cls.SERIALIZATION_LOCAL_LINKS_TO_UPDATE]
+ @classmethod
+ def delete_matching_record(cls, **filter_args):
+ """Delete records matching the filter.
+
+ @param filter_args: Arguments for the django filter
+ used to locate the record to delete.
+ """
+ try:
+ existing_record = cls.objects.get(**filter_args)
+ except cls.DoesNotExist:
+ return
+ existing_record.delete()
+
+
def _deserialize_local(self, data):
"""Set local attributes from a list of tuples.
@param data: List of tuples like returned by
_split_local_from_foreign_values.
"""
+ if not data:
+ return
+
for link, value in data:
setattr(self, link, value)
# Overwridden save() methods are prone to errors, so don't execute them.
@@ -1047,6 +1064,21 @@
@classmethod
+ def get_record(cls, data):
+ """Retrieve a record with the data in the given input arg.
+
+ @param data: A dictionary containing the information to use in a query
+ for data. If child models have different constraints of
+ uniqueness they should override this model.
+
+ @return: An object with matching data.
+
+ @raises DoesNotExist: If a record with the given data doesn't exist.
+ """
+ return cls.objects.get(id=data['id'])
+
+
+ @classmethod
def deserialize(cls, data):
"""Recursively deserializes and saves an object with it's dependencies.
@@ -1072,9 +1104,8 @@
return None
local, related = cls._split_local_from_foreign_values(data)
-
try:
- instance = cls.objects.get(id=data['id'])
+ instance = cls.get_record(data)
local = cls._filter_update_allowed_fields(local)
except cls.DoesNotExist:
instance = cls()