Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 1cec277

Browse files
authored
Merge pull request #162 from ambitioninc/develop
5.0.2
2 parents 8f8c5d2 + e304277 commit 1cec277

File tree

4 files changed

+67
-42
lines changed

4 files changed

+67
-42
lines changed

Diff for: entity/sync.py

+48-36
Original file line numberDiff line numberDiff line change
@@ -118,20 +118,51 @@ def _get_super_entities_by_ctype(model_objs_by_ctype, model_ids_to_sync, sync_al
118118
return super_entities_by_ctype
119119

120120

121-
def _get_model_objs_to_sync(model_ids_to_sync, model_objs_map, sync_all):
121+
def _fetch_entity_models(model_ids_to_sync, model_objs_map, model_objs_by_ctype, sync_all):
122+
"""
123+
Fetch the entity models per content type. This will also handle the
124+
case where accounts are created before _get_super_entities_by_ctype and
125+
the model_ids_to_sync do not match the model_objs_map
126+
"""
127+
for ctype, model_ids in model_ids_to_sync.items():
128+
129+
if sync_all:
130+
131+
# Build a set of ids of already fetched models
132+
fetched_model_ids = {
133+
model.id
134+
for model in model_objs_by_ctype[ctype]
135+
}
136+
137+
# Compute the set diff to see if any records are missing
138+
unfetched_model_ids = model_ids - fetched_model_ids
139+
else:
140+
unfetched_model_ids = model_ids
141+
142+
# Check if new records
143+
if unfetched_model_ids:
144+
145+
# Fetch the records and add them to the model_objs_map
146+
model_qset = entity_registry.entity_registry.get(ctype.model_class()).queryset
147+
model_objs_to_sync = model_qset.filter(id__in=unfetched_model_ids)
148+
for model_obj in model_objs_to_sync:
149+
model_objs_by_ctype[ctype].append(model_obj)
150+
model_objs_map[(ctype, model_obj.id)] = model_obj
151+
152+
153+
def _get_model_objs_to_sync(model_ids_to_sync, model_objs_map, model_objs_by_ctype, sync_all):
122154
"""
123155
Given the model IDs to sync, fetch all model objects to sync
124156
"""
125157
model_objs_to_sync = {}
126-
for ctype, model_ids_to_sync_for_ctype in model_ids_to_sync.items():
127-
model_qset = entity_registry.entity_registry.get(ctype.model_class()).queryset
128158

129-
if not sync_all:
130-
model_objs_to_sync[ctype] = model_qset.filter(id__in=model_ids_to_sync_for_ctype)
131-
else:
132-
model_objs_to_sync[ctype] = [
133-
model_objs_map[ctype, model_id] for model_id in model_ids_to_sync_for_ctype
134-
]
159+
_fetch_entity_models(model_ids_to_sync, model_objs_map, model_objs_by_ctype, sync_all)
160+
161+
for ctype, model_ids_to_sync_for_ctype in model_ids_to_sync.items():
162+
model_objs_to_sync[ctype] = [
163+
model_objs_map[ctype, model_id]
164+
for model_id in model_ids_to_sync_for_ctype
165+
]
135166

136167
return model_objs_to_sync
137168

@@ -231,32 +262,10 @@ def sync(self):
231262
# IDs of sub/super entity relationships
232263
super_entities_by_ctype = _get_super_entities_by_ctype(model_objs_by_ctype, model_ids_to_sync, sync_all)
233264

234-
# Handle the case where accounts are created before _get_super_entities_by_ctype and
235-
# the model_ids_to_sync do not match the model_objs_map
236-
for ctype, model_ids in model_ids_to_sync.items():
237-
238-
# Build a set of ids of already fetched models
239-
ids_of_fetched_models = {
240-
model.id
241-
for model in model_objs_by_ctype[ctype]
242-
}
243-
244-
# Compute the set diff to see if any new records were created
245-
created_model_ids = model_ids - ids_of_fetched_models
246-
247-
# Check if new records
248-
if created_model_ids:
249-
250-
# Fetch the records and add them to the model_objs_map
251-
new_records = ctype.model_class().objects.filter(id__in=created_model_ids)
252-
for new_record in new_records:
253-
model_objs_by_ctype[ctype].append(new_record)
254-
model_objs_map[(ctype, new_record.id)] = new_record
255-
256265
# Now that we have all models we need to sync, fetch them so that we can extract
257266
# metadata and entity kinds. If we are syncing all entities, we've already fetched
258267
# everything and can fill in this data struct without doing another DB hit
259-
model_objs_to_sync = _get_model_objs_to_sync(model_ids_to_sync, model_objs_map, sync_all)
268+
model_objs_to_sync = _get_model_objs_to_sync(model_ids_to_sync, model_objs_map, model_objs_by_ctype, sync_all)
260269

261270
# Obtain all entity kind tuples associated with the models
262271
entity_kind_tuples_to_sync = set()
@@ -384,7 +393,7 @@ def upsert_entity_kinds(self, entity_kinds):
384393
# Select all our existing entity kinds for update so we can do proper locking
385394
# We have to select all here for some odd reason, if we only select the ones
386395
# we are syncing we still run into deadlock issues
387-
list(EntityKind.all_objects.all().select_for_update().values_list('id', flat=True))
396+
list(EntityKind.all_objects.all().order_by('id').select_for_update().values_list('id', flat=True))
388397

389398
# Upsert the entity kinds
390399
upserted_enitity_kinds = manager_utils.bulk_upsert(
@@ -412,7 +421,7 @@ def upsert_entities(self, entities, sync=False):
412421
if entities:
413422
# Default select for update query when syncing all
414423
select_for_update_query = (
415-
'SELECT FROM {table_name} FOR NO KEY UPDATE'
424+
'SELECT FROM {table_name} ORDER BY id ASC FOR NO KEY UPDATE'
416425
).format(
417426
table_name=Entity._meta.db_table
418427
)
@@ -421,7 +430,10 @@ def upsert_entities(self, entities, sync=False):
421430
# If we are not syncing all, only select those we are updating
422431
if not sync:
423432
select_for_update_query = (
424-
'SELECT FROM {table_name} WHERE (entity_type_id, entity_id) IN %s FOR NO KEY UPDATE'
433+
'SELECT FROM {table_name} '
434+
'WHERE (entity_type_id, entity_id) IN %s '
435+
'ORDER BY id ASC '
436+
'FOR NO KEY UPDATE'
425437
).format(
426438
table_name=Entity._meta.db_table
427439
)
@@ -506,7 +518,7 @@ def upsert_entity_relationships(self, queryset, entity_relationships):
506518

507519
# Select the relationships for update
508520
if entity_relationships:
509-
list(queryset.select_for_update().values_list(
521+
list(queryset.order_by('id').select_for_update().values_list(
510522
'id',
511523
flat=True
512524
))

Diff for: entity/tests/sync_tests.py

+16-5
Original file line numberDiff line numberDiff line change
@@ -309,23 +309,34 @@ def test_sync_all_accounts_teams_deleted_account_during_sync(self):
309309
accounts[3].save()
310310

311311
def wrapped_super_entities(*args, **kwargs):
312-
if Account.objects.filter(email='[email protected]').exists():
313-
Account.objects.filter(email='[email protected]').delete()
312+
account = Account.objects.filter(email='[email protected]', is_active=True).first()
313+
if account:
314+
account.is_active = False
315+
account.save()
314316

315317
return _get_super_entities_by_ctype(*args, **kwargs)
316318

317-
# Sync all the entities. There should be 6 (4 accounts 2 teams)
319+
# Sync the accounts
318320
with patch('entity.sync._get_super_entities_by_ctype', wraps=wrapped_super_entities):
319321
sync_entities(*accounts)
320-
# Sync again to hit other wrapped function branch and make sure it doesn't error
322+
account = Account.objects.get(email='[email protected]')
323+
entity = Entity.all_objects.get_for_obj(account)
324+
self.assertEqual(entity.is_active, False)
325+
326+
# Fetch accounts and sync again - hits other block in wrapped function
327+
with patch('entity.sync._get_super_entities_by_ctype', wraps=wrapped_super_entities):
328+
accounts = Account.objects.all()
321329
sync_entities(*accounts)
330+
account = Account.objects.get(email='[email protected]')
331+
entity = Entity.all_objects.get_for_obj(account)
332+
self.assertEqual(entity.is_active, False)
322333

323334
self.assertEquals(Entity.objects.filter(entity_type=ContentType.objects.get_for_model(Account)).count(), 4)
324335
self.assertEquals(Entity.objects.filter(entity_type=ContentType.objects.get_for_model(Team)).count(), 2)
325336
self.assertEquals(Entity.objects.all().count(), 6)
326337

327338
# There should be six entity relationships
328-
self.assertEquals(EntityRelationship.objects.all().count(), 3)
339+
self.assertEquals(EntityRelationship.objects.all().count(), 4)
329340

330341
def test_sync_all_accounts_teams_inactive_entity_kind(self):
331342
"""

Diff for: entity/version.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '5.0.1'
1+
__version__ = '5.0.2'

Diff for: release_notes.md

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
## Release Notes
22

3+
- 5.0.2:
4+
- Only fetch new entities on a sync_all
35
- 5.0.1:
46
- Fix bug where an entity created or deleted during a sync would cause errors
57
- 5.0.0:

0 commit comments

Comments
 (0)